Attendance register
A mark for each student at each session - present, late, excused, absent or none - and attendance rates under four ways of counting, which flag different students: an absence, a missing mark and no sessions yet are three different nothings.
Every screen below was recorded under CPython. When this page was built, the EML interpreter replayed each session from the same input and printed the same bytes.
About
A mark for each student at each session: present, late, excused or absent - or no mark at all, when the register was not taken or the student had not joined yet. Four ways of counting give four attendance rates, and they flag different students.
main.eml- the menu, the register, the rates and who is below 80%, taking a register, correcting a mark, and a new studentroll.eml- the register, the four rates, percentages to a tenth, the flags, the sessions not taken, and the changes
How each part works:
- A rate is present or late over one of four counts of sessions: 1. every session held; 2. the sessions since the student joined; 3. the sessions with a mark; 4. those, leaving out the excused ones. Percentages are rounded half up to a tenth, in whole numbers only.
- As in the corpus case
absent-vs-empty-vs-zero, an absence, a missing mark and a student with no sessions yet are three different things: - counting (1) or (2), a session nobody marked lowers everyone, and (1) counts Dana's sessions before joining as absences: 50.0% against 100.0%; - a new student with no marked session has no rate at all; (1) would give 0.0% and flag them. - The counts fall from (1) to (4) and the rates never drop, so each list of flagged students contains the next; the program names those flagged only when a session with no mark counts as an absence.
- As in the corpus case
attendance-register, the counts are a dict updated one mark at a time.
What is checked: menu choices 1 to 6; one mark per student, each P, L, E or A in either case, or skip; a student on the register, a session from the one they joined to the last, and a mark or none; a new name of up to 10 letters, not already there, and at most 8 students. An empty answer cancels.
Sessions: sessions/basic.in gives: - the register (session 3 not taken, Dana joining at session 4) and the rates: Ana and Dana flagged only when no mark counts as an absence; - Finn added, with no rate yet, and session 7 taken for all six; - Ana's session 3 corrected to P, and the rates; Cara's session 6 to no mark, and the register and the rates.
sessions/bad-input.in gives: - menu choices 0 and x; - registers: a cancel, two marks, an X, and PP; - corrections: a cancel, Zed, then Ana with a cancel, 0, 9 and x for the session, Dana's session 2 (before joining), and Ana's session 1 with a cancel, Q and PP; - new students: a cancel, Ana, Jo-Jo and Bartholomews, then Gia, Hal and Ivy, and Jon, one too many; - session 7 not taken, the register and the rates.
Built on the verified corpus cases attendance-register (a dict of counts kept across many small updates) and absent-vs-empty-vs-zero (three kinds of nothing, and a rule that treats them alike).
Recorded sessions
What the screen shows while someone uses the program. Each typed line appears after its prompt, the way a terminal shows it.
bad-input
interpreter: byte-equal== Attendance register ==
A mark for each student at each session: present, late, excused or absent - or no mark, when the register was not taken or the student had not joined.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 0
Pick a number from 1 to 6.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> x
Pick a number from 1 to 6.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 3
Session 7: a mark for each of Ana, Ben, Cara, Dana and Eli in turn (P, L, E or A), or skip if the register was not taken.
marks>
Cancelled.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 3
Session 7: a mark for each of Ana, Ben, Cara, Dana and Eli in turn (P, L, E or A), or skip if the register was not taken.
marks> P P
Type 5 marks, one for each student.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 3
Session 7: a mark for each of Ana, Ben, Cara, Dana and Eli in turn (P, L, E or A), or skip if the register was not taken.
marks> P X P P P
Each mark is P, L, E or A, separated by spaces.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 3
Session 7: a mark for each of Ana, Ben, Cara, Dana and Eli in turn (P, L, E or A), or skip if the register was not taken.
marks> PP P P P P
Each mark is P, L, E or A, separated by spaces.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 4
student>
Cancelled.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 4
student> Zed
No such student.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 4
student> Ana
session (1 to 6)>
Cancelled.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 4
student> Ana
session (1 to 6)> 0
Type a session from 1 to 6.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 4
student> Ana
session (1 to 6)> 9
Type a session from 1 to 6.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 4
student> Ana
session (1 to 6)> x
Type a session from 1 to 6.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 4
student> Dana
session (1 to 6)> 2
Dana joined at session 4.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 4
student> Ana
session (1 to 6)> 1
mark (P, L, E, A or none)>
Cancelled.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 4
student> Ana
session (1 to 6)> 1
mark (P, L, E, A or none)> Q
Type P, L, E, A or none.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 4
student> Ana
session (1 to 6)> 1
mark (P, L, E, A or none)> PP
Type P, L, E, A or none.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 5
name (up to 10 letters)>
Cancelled.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 5
name (up to 10 letters)> Ana
Ana is already on the register.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 5
name (up to 10 letters)> Jo-Jo
Type a name of up to 10 letters.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 5
name (up to 10 letters)> Bartholomews
Type a name of up to 10 letters.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 5
name (up to 10 letters)> Gia
Gia joins from session 7.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 5
name (up to 10 letters)> Hal
Hal joins from session 7.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 5
name (up to 10 letters)> Ivy
Ivy joins from session 7.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 5
name (up to 10 letters)> Jon
8 students is the most.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 3
Session 7: a mark for each of Ana, Ben, Cara, Dana, Eli, Gia, Hal and Ivy in turn (P, L, E or A), or skip if the register was not taken.
marks> skip
Session 7 recorded.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 1
Register (P present, L late, E excused, A absent, - no mark):
session 1 2 3 4 5 6 7
Ana P P - L P A -
Ben P A - P E P -
Cara P P - P P L -
Dana - - - P P P - joined at session 4
Eli A L - E E A -
Gia - - - - - - - joined at session 7
Hal - - - - - - - joined at session 7
Ivy - - - - - - - joined at session 7
Sessions 3 and 7: the register was not taken.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 2
Present or late, out of: (1) every session held, (2) the sessions since joining, (3) the sessions with a mark, (4) those without excused ones.
student (1) (2) (3) (4)
Ana 57.1% 57.1% 80.0% 80.0%
Ben 42.9% 42.9% 60.0% 75.0%
Cara 71.4% 71.4% 100.0% 100.0%
Dana 42.9% 75.0% 100.0% 100.0%
Eli 14.3% 14.3% 20.0% 33.3%
Gia 0.0% 0.0% - -
Hal 0.0% 0.0% - -
Ivy 0.0% 0.0% - -
Below 80%:
(1) Ana, Ben, Cara, Dana, Eli, Gia, Hal and Ivy
(2) Ana, Ben, Cara, Dana, Eli, Gia, Hal and Ivy
(3) Ben and Eli
(4) Ben and Eli
Flagged only when a session with no mark counts as an absence: Ana, Cara, Dana, Gia, Hal and Ivy.
No marked session yet, so no rate: Gia, Hal and Ivy. Counting every session held gives 0.0% instead.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 6
Bye.
What was typed (62 lines)
0
x
3
3
P P
3
P X P P P
3
PP P P P P
4
4
Zed
4
Ana
4
Ana
0
4
Ana
9
4
Ana
x
4
Dana
2
4
Ana
1
4
Ana
1
Q
4
Ana
1
PP
5
5
Ana
5
Jo-Jo
5
Bartholomews
5
Gia
5
Hal
5
Ivy
5
Jon
3
skip
1
2
6
basic
interpreter: byte-equal== Attendance register ==
A mark for each student at each session: present, late, excused or absent - or no mark, when the register was not taken or the student had not joined.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 1
Register (P present, L late, E excused, A absent, - no mark):
session 1 2 3 4 5 6
Ana P P - L P A
Ben P A - P E P
Cara P P - P P L
Dana - - - P P P joined at session 4
Eli A L - E E A
Session 3: the register was not taken.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 2
Present or late, out of: (1) every session held, (2) the sessions since joining, (3) the sessions with a mark, (4) those without excused ones.
student (1) (2) (3) (4)
Ana 66.7% 66.7% 80.0% 80.0%
Ben 50.0% 50.0% 60.0% 75.0%
Cara 83.3% 83.3% 100.0% 100.0%
Dana 50.0% 100.0% 100.0% 100.0%
Eli 16.7% 16.7% 20.0% 33.3%
Below 80%:
(1) Ana, Ben, Dana and Eli
(2) Ana, Ben and Eli
(3) Ben and Eli
(4) Ben and Eli
Flagged only when a session with no mark counts as an absence: Ana and Dana.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 5
name (up to 10 letters)> Finn
Finn joins from session 7.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 2
Present or late, out of: (1) every session held, (2) the sessions since joining, (3) the sessions with a mark, (4) those without excused ones.
student (1) (2) (3) (4)
Ana 66.7% 66.7% 80.0% 80.0%
Ben 50.0% 50.0% 60.0% 75.0%
Cara 83.3% 83.3% 100.0% 100.0%
Dana 50.0% 100.0% 100.0% 100.0%
Eli 16.7% 16.7% 20.0% 33.3%
Finn 0.0% - - -
Below 80%:
(1) Ana, Ben, Dana, Eli and Finn
(2) Ana, Ben and Eli
(3) Ben and Eli
(4) Ben and Eli
Flagged only when a session with no mark counts as an absence: Ana, Dana and Finn.
No marked session yet, so no rate: Finn. Counting every session held gives 0.0% instead.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 3
Session 7: a mark for each of Ana, Ben, Cara, Dana, Eli and Finn in turn (P, L, E or A), or skip if the register was not taken.
marks> P P A E L P
Session 7 recorded.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 1
Register (P present, L late, E excused, A absent, - no mark):
session 1 2 3 4 5 6 7
Ana P P - L P A P
Ben P A - P E P P
Cara P P - P P L A
Dana - - - P P P E joined at session 4
Eli A L - E E A L
Finn - - - - - - P joined at session 7
Session 3: the register was not taken.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 2
Present or late, out of: (1) every session held, (2) the sessions since joining, (3) the sessions with a mark, (4) those without excused ones.
student (1) (2) (3) (4)
Ana 71.4% 71.4% 83.3% 83.3%
Ben 57.1% 57.1% 66.7% 80.0%
Cara 71.4% 71.4% 83.3% 83.3%
Dana 42.9% 75.0% 75.0% 100.0%
Eli 28.6% 28.6% 33.3% 50.0%
Finn 14.3% 100.0% 100.0% 100.0%
Below 80%:
(1) Ana, Ben, Cara, Dana, Eli and Finn
(2) Ana, Ben, Cara, Dana and Eli
(3) Ben, Dana and Eli
(4) Eli
Flagged only when a session with no mark counts as an absence: Ana, Cara and Finn.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 4
student> Ana
session (1 to 7)> 3
mark (P, L, E, A or none)> P
Ana, session 3: - -> P.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 2
Present or late, out of: (1) every session held, (2) the sessions since joining, (3) the sessions with a mark, (4) those without excused ones.
student (1) (2) (3) (4)
Ana 85.7% 85.7% 85.7% 85.7%
Ben 57.1% 57.1% 66.7% 80.0%
Cara 71.4% 71.4% 83.3% 83.3%
Dana 42.9% 75.0% 75.0% 100.0%
Eli 28.6% 28.6% 33.3% 50.0%
Finn 14.3% 100.0% 100.0% 100.0%
Below 80%:
(1) Ben, Cara, Dana, Eli and Finn
(2) Ben, Cara, Dana and Eli
(3) Ben, Dana and Eli
(4) Eli
Flagged only when a session with no mark counts as an absence: Cara and Finn.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 4
student> Cara
session (1 to 7)> 6
mark (P, L, E, A or none)> none
Cara, session 6: L -> -.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 1
Register (P present, L late, E excused, A absent, - no mark):
session 1 2 3 4 5 6 7
Ana P P P L P A P
Ben P A - P E P P
Cara P P - P P - A
Dana - - - P P P E joined at session 4
Eli A L - E E A L
Finn - - - - - - P joined at session 7
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 2
Present or late, out of: (1) every session held, (2) the sessions since joining, (3) the sessions with a mark, (4) those without excused ones.
student (1) (2) (3) (4)
Ana 85.7% 85.7% 85.7% 85.7%
Ben 57.1% 57.1% 66.7% 80.0%
Cara 57.1% 57.1% 80.0% 80.0%
Dana 42.9% 75.0% 75.0% 100.0%
Eli 28.6% 28.6% 33.3% 50.0%
Finn 14.3% 100.0% 100.0% 100.0%
Below 80%:
(1) Ben, Cara, Dana, Eli and Finn
(2) Ben, Cara, Dana and Eli
(3) Ben, Dana and Eli
(4) Eli
Flagged only when a session with no mark counts as an absence: Cara and Finn.
1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit
choice> 6
Bye.
What was typed (21 lines)
1
2
5
Finn
2
3
P P A E L P
1
2
4
Ana
3
P
2
4
Cara
6
none
1
2
6
Modules
The program as written, entry module first. Each module transpiles to its own Python file, which is what eml project run executes.
main.eml(entry)
eml# P121 attendance register: the register, attendance rates under four
# ways of counting, taking a register, correcting a mark, a new student.
import roll
def plain(s):
0 => i
len(s) => j
while i < j and s[i] == " ":
i + 1 => i
while j > i and s[j - 1] == " ":
j - 1 => j
return s[i:j]
def pad(s, n):
s => out
while len(out) < n:
out + " " => out
return out
def lpad(s, n):
s => out
while len(out) < n:
" " + out => out
return out
def names_text(ns):
"" => out
for i in [0:len(ns) - 1]:
if i > 0 and i == len(ns) - 1:
out + " and " => out
elif i > 0:
out + ", " => out
out + ns[i] => out
return out
def numbers_text(ns):
[] => out
for x in ns:
out + [str(x)] => out
return names_text(out)
def mark_of(c):
# a typed mark as P, L, E or A, or "" if it is none of them
if c == "P" or c == "p":
return "P"
if c == "L" or c == "l":
return "L"
if c == "E" or c == "e":
return "E"
if c == "A" or c == "a":
return "A"
return ""
def find(reg, name):
for i in [0:len(reg) - 1]:
if reg[i][0] == name:
return i
return 0 - 1
def show(reg):
roll.held(reg) => n
"Register (P present, L late, E excused, A absent, - no mark):" ^0
" session " => head
for k in [1:n]:
head + lpad(str(k), 3) => head
head ^0
for st in reg:
" " + pad(st[0], 10) => line
for m in st[2]:
line + " " + m => line
if st[1] > 1:
line + " joined at session " + str(st[1]) => line
line ^0
roll.not_taken(reg) => gone
if len(gone) == 1:
("Session " + str(gone[0]) + ": the register was not taken.") ^0
elif len(gone) > 1:
("Sessions " + numbers_text(gone) + ": the register was not taken.") ^0
def rates(reg, line):
roll.held(reg) => n
("Present or late, out of: (1) every session held, (2) the sessions since joining, (3) the sessions with a mark, (4) those without excused ones.") ^0
" " + pad("student", 10) => head
for k in [1:4]:
head + lpad("(" + str(k) + ")", 10) => head
head ^0
for st in reg:
" " + pad(st[0], 10) => out
for p in roll.policies():
roll.rate(st, p, n) => r
out + lpad(roll.percent_text(r[0], r[1]), 10) => out
out ^0
("Below " + str(line) + "%:") ^0
[] => lists
for k in [0:3]:
roll.flagged(reg, roll.policies()[k], line) => f
lists + [f] => lists
if len(f) == 0:
(" (" + str(k + 1) + ") nobody") ^0
else:
(" (" + str(k + 1) + ") " + names_text(f)) ^0
[] => only
for name in lists[0]:
False => in3
for x in lists[2]:
if x == name:
True => in3
if not in3:
only + [name] => only
if len(only) > 0:
("Flagged only when a session with no mark counts as an absence: " + names_text(only) + ".") ^0
[] => none
for st in reg:
if roll.rate(st, "marked", n)[1] == 0:
none + [st[0]] => none
if len(none) > 0:
("No marked session yet, so no rate: " + names_text(none) + ". Counting every session held gives 0.0% instead.") ^0
def take_register(reg):
[] => names
for st in reg:
names + [st[0]] => names
("Session " + str(roll.held(reg) + 1) + ": a mark for each of " + names_text(names) + " in turn (P, L, E or A), or skip if the register was not taken.") ^0
plain(input("marks> ")) => t
if t == "":
"Cancelled." ^0
return reg
[] => marks
if t == "skip":
for st in reg:
marks + ["-"] => marks
else:
"" => w
for c in t + " ":
if c == " ":
if w != "":
if len(w) != 1 or mark_of(w) == "":
"Each mark is P, L, E or A, separated by spaces." ^0
return reg
marks + [mark_of(w)] => marks
"" => w
else:
w + c => w
if len(marks) != len(reg):
("Type " + str(len(reg)) + " marks, one for each student.") ^0
return reg
roll.take(reg, marks) => out
("Session " + str(roll.held(out)) + " recorded.") ^0
return out
def correct_mark(reg):
plain(input("student> ")) => name
if name == "":
"Cancelled." ^0
return reg
find(reg, name) => i
if i < 0:
"No such student." ^0
return reg
plain(input("session (1 to " + str(roll.held(reg)) + ")> ")) => t
if t == "":
"Cancelled." ^0
return reg
0 => k
if len(t) > 0 and len(t) <= 2:
True => digits
for c in t:
if c < "0" or c > "9":
False => digits
if digits:
int(t) => k
if k < 1 or k > roll.held(reg):
("Type a session from 1 to " + str(roll.held(reg)) + ".") ^0
return reg
if k < reg[i][1]:
(name + " joined at session " + str(reg[i][1]) + ".") ^0
return reg
plain(input("mark (P, L, E, A or none)> ")) => m
if m == "":
"Cancelled." ^0
return reg
"-" => mark
if m != "none":
if len(m) != 1 or mark_of(m) == "":
"Type P, L, E, A or none." ^0
return reg
mark_of(m) => mark
reg[i][2][k - 1] => before
roll.correct(reg, i, k, mark) => out
(name + ", session " + str(k) + ": " + before + " -> " + mark + ".") ^0
return out
def add(reg):
plain(input("name (up to 10 letters)> ")) => name
if name == "":
"Cancelled." ^0
return reg
if len(name) > 10:
"Type a name of up to 10 letters." ^0
return reg
for c in name:
if not ((c >= "a" and c <= "z") or (c >= "A" and c <= "Z")):
"Type a name of up to 10 letters." ^0
return reg
if find(reg, name) >= 0:
(name + " is already on the register.") ^0
return reg
if len(reg) >= 8:
"8 students is the most." ^0
return reg
roll.add_student(reg, name) => out
(name + " joins from session " + str(roll.held(out) + 1) + ".") ^0
return out
roll.default_register() => reg
80 => line
"== Attendance register ==" ^0
"A mark for each student at each session: present, late, excused or absent - or no mark, when the register was not taken or the student had not joined." ^0
True => running
while running:
"" ^0
"1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit" ^0
plain(input("choice> ")) => choice
if choice == "1":
show(reg)
elif choice == "2":
rates(reg, line)
elif choice == "3":
take_register(reg) => reg
elif choice == "4":
correct_mark(reg) => reg
elif choice == "5":
add(reg) => reg
elif choice == "6":
False => running
else:
"Pick a number from 1 to 6." ^0
"Bye." ^0
Python projection (main.py)
import roll
def plain(s):
i = 0
j = len(s)
while i < j and s[i] == " ":
i = i + 1
while j > i and s[j - 1] == " ":
j = j - 1
return s[i:j]
def pad(s, n):
out = s
while len(out) < n:
out = out + " "
return out
def lpad(s, n):
out = s
while len(out) < n:
out = " " + out
return out
def names_text(ns):
out = ""
for i in range(0, len(ns)):
if i > 0 and i == len(ns) - 1:
out = out + " and "
elif i > 0:
out = out + ", "
out = out + ns[i]
return out
def numbers_text(ns):
out = []
for x in ns:
out = out + [str(x)]
return names_text(out)
def mark_of(c):
if c == "P" or c == "p":
return "P"
if c == "L" or c == "l":
return "L"
if c == "E" or c == "e":
return "E"
if c == "A" or c == "a":
return "A"
return ""
def find(reg, name):
for i in range(0, len(reg)):
if reg[i][0] == name:
return i
return 0 - 1
def show(reg):
n = roll.held(reg)
print("Register (P present, L late, E excused, A absent, - no mark):")
head = " session "
for k in range(1, n+1):
head = head + lpad(str(k), 3)
print(head)
for st in reg:
line = " " + pad(st[0], 10)
for m in st[2]:
line = line + " " + m
if st[1] > 1:
line = line + " joined at session " + str(st[1])
print(line)
gone = roll.not_taken(reg)
if len(gone) == 1:
print("Session " + str(gone[0]) + ": the register was not taken.")
elif len(gone) > 1:
print("Sessions " + numbers_text(gone) + ": the register was not taken.")
def rates(reg, line):
n = roll.held(reg)
print("Present or late, out of: (1) every session held, (2) the sessions since joining, (3) the sessions with a mark, (4) those without excused ones.")
head = " " + pad("student", 10)
for k in range(1, 5):
head = head + lpad("(" + str(k) + ")", 10)
print(head)
for st in reg:
out = " " + pad(st[0], 10)
for p in roll.policies():
r = roll.rate(st, p, n)
out = out + lpad(roll.percent_text(r[0], r[1]), 10)
print(out)
print("Below " + str(line) + "%:")
lists = []
for k in range(0, 4):
f = roll.flagged(reg, roll.policies()[k], line)
lists = lists + [f]
if len(f) == 0:
print(" (" + str(k + 1) + ") nobody")
else:
print(" (" + str(k + 1) + ") " + names_text(f))
only = []
for name in lists[0]:
in3 = False
for x in lists[2]:
if x == name:
in3 = True
if not in3:
only = only + [name]
if len(only) > 0:
print("Flagged only when a session with no mark counts as an absence: " + names_text(only) + ".")
none = []
for st in reg:
if roll.rate(st, "marked", n)[1] == 0:
none = none + [st[0]]
if len(none) > 0:
print("No marked session yet, so no rate: " + names_text(none) + ". Counting every session held gives 0.0% instead.")
def take_register(reg):
names = []
for st in reg:
names = names + [st[0]]
print("Session " + str(roll.held(reg) + 1) + ": a mark for each of " + names_text(names) + " in turn (P, L, E or A), or skip if the register was not taken.")
t = plain(input("marks> "))
if t == "":
print("Cancelled.")
return reg
marks = []
if t == "skip":
for st in reg:
marks = marks + ["-"]
else:
w = ""
for c in t + " ":
if c == " ":
if w != "":
if len(w) != 1 or mark_of(w) == "":
print("Each mark is P, L, E or A, separated by spaces.")
return reg
marks = marks + [mark_of(w)]
w = ""
else:
w = w + c
if len(marks) != len(reg):
print("Type " + str(len(reg)) + " marks, one for each student.")
return reg
out = roll.take(reg, marks)
print("Session " + str(roll.held(out)) + " recorded.")
return out
def correct_mark(reg):
name = plain(input("student> "))
if name == "":
print("Cancelled.")
return reg
i = find(reg, name)
if i < 0:
print("No such student.")
return reg
t = plain(input("session (1 to " + str(roll.held(reg)) + ")> "))
if t == "":
print("Cancelled.")
return reg
k = 0
if len(t) > 0 and len(t) <= 2:
digits = True
for c in t:
if c < "0" or c > "9":
digits = False
if digits:
k = int(t)
if k < 1 or k > roll.held(reg):
print("Type a session from 1 to " + str(roll.held(reg)) + ".")
return reg
if k < reg[i][1]:
print(name + " joined at session " + str(reg[i][1]) + ".")
return reg
m = plain(input("mark (P, L, E, A or none)> "))
if m == "":
print("Cancelled.")
return reg
mark = "-"
if m != "none":
if len(m) != 1 or mark_of(m) == "":
print("Type P, L, E, A or none.")
return reg
mark = mark_of(m)
before = reg[i][2][k - 1]
out = roll.correct(reg, i, k, mark)
print(name + ", session " + str(k) + ": " + before + " -> " + mark + ".")
return out
def add(reg):
name = plain(input("name (up to 10 letters)> "))
if name == "":
print("Cancelled.")
return reg
if len(name) > 10:
print("Type a name of up to 10 letters.")
return reg
for c in name:
if not (c >= "a" and c <= "z" or c >= "A" and c <= "Z"):
print("Type a name of up to 10 letters.")
return reg
if find(reg, name) >= 0:
print(name + " is already on the register.")
return reg
if len(reg) >= 8:
print("8 students is the most.")
return reg
out = roll.add_student(reg, name)
print(name + " joins from session " + str(roll.held(out) + 1) + ".")
return out
reg = roll.default_register()
line = 80
print("== Attendance register ==")
print("A mark for each student at each session: present, late, excused or absent - or no mark, when the register was not taken or the student had not joined.")
running = True
while running:
print("")
print("1) register 2) rates 3) take the register 4) correct a mark 5) add a student 6) quit")
choice = plain(input("choice> "))
if choice == "1":
show(reg)
elif choice == "2":
rates(reg, line)
elif choice == "3":
reg = take_register(reg)
elif choice == "4":
reg = correct_mark(reg)
elif choice == "5":
reg = add(reg)
elif choice == "6":
running = False
else:
print("Pick a number from 1 to 6.")
print("Bye.")
roll.eml
eml# P121 attendance register - students, sessions and a mark for each: P
# present, L late, E excused, A absent, or no mark at all (the register was
# not taken, or the student had not joined). As in the corpus case
# absent-vs-empty-vs-zero, an absence, a missing mark and a student with no
# sessions yet are three different nothings, and a rate that treats them
# alike flags the wrong students. As in attendance-register, the counts are
# a dict updated one mark at a time.
#
# A student is [name, session joined, marks]; marks holds one letter per
# session held, "-" for no mark.
def default_register():
return [["Ana", 1, ["P", "P", "-", "L", "P", "A"]], ["Ben", 1, ["P", "A", "-", "P", "E", "P"]], ["Cara", 1, ["P", "P", "-", "P", "P", "L"]], ["Dana", 4, ["-", "-", "-", "P", "P", "P"]], ["Eli", 1, ["A", "L", "-", "E", "E", "A"]]]
def held(reg):
if len(reg) == 0:
return 0
return len(reg[0][2])
def div(p, q):
# the whole part of p / q, for p >= 0 and q > 0
int(p / q) => f
while f * q > p:
f - 1 => f
while (f + 1) * q <= p:
f + 1 => f
return f
def counts(st):
# {letter: count} over a student's marks, "-" included
{"P": 0, "L": 0, "E": 0, "A": 0, "-": 0} => c
for m in st[2]:
c[m] + 1 => c[m]
return c
def policies():
return ["all held", "since joining", "marked", "marked, not excused"]
def rate(st, policy, n):
# [present or late, the sessions it is out of] under a policy
counts(st) => c
c["P"] + c["L"] => present
if policy == "all held":
return [present, n]
if policy == "since joining":
return [present, n - st[1] + 1]
if policy == "marked":
return [present, c["P"] + c["L"] + c["E"] + c["A"]]
return [present, c["P"] + c["L"] + c["A"]]
def tenths(p, q):
# p / q as a percentage in tenths, rounded half up
return div(2000 * p + q, 2 * q)
def percent_text(p, q):
if q <= 0:
return "-"
tenths(p, q) => t
return str(div(t, 10)) + "." + str(t - 10 * div(t, 10)) + "%"
def below(p, q, line):
# under the line (a whole percent): only when there are sessions to count
return q > 0 and 100 * p < line * q
def flagged(reg, policy, line):
held(reg) => n
[] => out
for st in reg:
rate(st, policy, n) => r
if below(r[0], r[1], line):
out + [st[0]] => out
return out
def not_taken(reg):
# the sessions where nobody has a mark
[] => out
for k in [0:held(reg) - 1]:
True => none
for st in reg:
if st[2][k] != "-":
False => none
if none:
out + [k + 1] => out
return out
def take(reg, marks):
# a new session: one mark per student, or "-" for each when not taken
[] => out
for i in [0:len(reg) - 1]:
reg[i] => st
out + [[st[0], st[1], st[2] + [marks[i]]]] => out
return out
def add_student(reg, name):
held(reg) + 1 => j
[] => marks
for k in [1:held(reg)]:
marks + ["-"] => marks
return reg + [[name, j, marks]]
def correct(reg, i, k, mark):
# the student i's mark for session k (1-based)
[] => out
for x in [0:len(reg) - 1]:
reg[x] => st
if x == i:
[] => marks
for y in [0:len(st[2]) - 1]:
if y == k - 1:
marks + [mark] => marks
else:
marks + [st[2][y]] => marks
out + [[st[0], st[1], marks]] => out
else:
out + [st] => out
return out
Python projection (roll.py)
def default_register():
return [["Ana", 1, ["P", "P", "-", "L", "P", "A"]], ["Ben", 1, ["P", "A", "-", "P", "E", "P"]], ["Cara", 1, ["P", "P", "-", "P", "P", "L"]], ["Dana", 4, ["-", "-", "-", "P", "P", "P"]], ["Eli", 1, ["A", "L", "-", "E", "E", "A"]]]
def held(reg):
if len(reg) == 0:
return 0
return len(reg[0][2])
def div(p, q):
f = int(p / q)
while f * q > p:
f = f - 1
while (f + 1) * q <= p:
f = f + 1
return f
def counts(st):
c = {"P": 0, "L": 0, "E": 0, "A": 0, "-": 0}
for m in st[2]:
c[m] = c[m] + 1
return c
def policies():
return ["all held", "since joining", "marked", "marked, not excused"]
def rate(st, policy, n):
c = counts(st)
present = c["P"] + c["L"]
if policy == "all held":
return [present, n]
if policy == "since joining":
return [present, n - st[1] + 1]
if policy == "marked":
return [present, c["P"] + c["L"] + c["E"] + c["A"]]
return [present, c["P"] + c["L"] + c["A"]]
def tenths(p, q):
return div(2000 * p + q, 2 * q)
def percent_text(p, q):
if q <= 0:
return "-"
t = tenths(p, q)
return str(div(t, 10)) + "." + str(t - 10 * div(t, 10)) + "%"
def below(p, q, line):
return q > 0 and 100 * p < line * q
def flagged(reg, policy, line):
n = held(reg)
out = []
for st in reg:
r = rate(st, policy, n)
if below(r[0], r[1], line):
out = out + [st[0]]
return out
def not_taken(reg):
out = []
for k in range(0, held(reg)):
none = True
for st in reg:
if st[2][k] != "-":
none = False
if none:
out = out + [k + 1]
return out
def take(reg, marks):
out = []
for i in range(0, len(reg)):
st = reg[i]
out = out + [[st[0], st[1], st[2] + [marks[i]]]]
return out
def add_student(reg, name):
j = held(reg) + 1
marks = []
for k in range(1, held(reg)+1):
marks = marks + ["-"]
return reg + [[name, j, marks]]
def correct(reg, i, k, mark):
out = []
for x in range(0, len(reg)):
st = reg[x]
if x == i:
marks = []
for y in range(0, len(st[2])):
if y == k - 1:
marks = marks + [mark]
else:
marks = marks + [st[2][y]]
out = out + [[st[0], st[1], marks]]
else:
out = out + [st]
return out