Timesheet
A week of shifts and hours over 8 in a day paid at time and a half, with the day read as the shift, the shop's calendar day and the payroll server's: every midnight that falls inside a long shift pays less.
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 shop's week of shifts and the rule "hours over 8 in a day are paid at time and a half", with the day read three ways.
main.eml- the menu, the shifts, overtime by each reading, one worker's days, and a shift of your ownshifts.eml- the workers and their shifts, the hours on each day of the shop or of the payroll server, and the hours over 8
How each part works:
- Five workers, in the shop's time (UTC+9): day shifts, night shifts, evening shifts, a double shift, and 12-hour nights. 20.00 an hour, 30.00 for each hour over 8.
- As in the corpus case
the-counter-reset-at-midnight-and-the-shift-crossed-it, a count that returns to zero at midnight starts again in the middle of every shift that crosses it. Eve's three 12-hour nights are 12 hours over 8 by the shift and none by the shop's day: midnight splits each into 4 and 8. By the shop's day Ben, Cleo and Eve lose 15 hours of overtime. - As in
the-day-boundary-was-the-servers-and-the-user-was-elsewhere, the payroll server's day is UTC, which ends at 09:00 in the shop: it keeps the night shifts whole and cuts Dan's 07:00 starts instead, 2 hours. - Pay for the week: 5,020.00 by the shift, 4,870.00 by the shop's day, 5,000.00 by the server's day. A shift of 8 hours or less pays the same under every reading; a longer one pays less under any reading whose midnight falls inside it.
What is checked: menu choices 1 to 5; a worker from 1 to 5; a start hour from 0 to 23 and 1 to 16 hours. An empty answer cancels.
Sessions: sessions/basic.in gives: - the shifts and the overtime; - Eve's days and Dan's days; - your own shifts from 22:00 for 10 hours, from 23:00 for 11 and from 10:00 for 10.
sessions/bad-input.in gives: - menu choices 0 and x; - workers: a cancel, 0, 6 and x; - start hours: a cancel, 24 and x; then hours with a cancel, 0, 17 and x; - shifts from 02:00 for 12 hours, which only the server's midnight cuts, and from 09:00 for 6.
Built on the verified corpus cases the-counter-reset-at-midnight-and-the-shift-crossed-it (a daily count reset at midnight, two hours into the night shift) and the-day-boundary-was-the-servers-and-the-user-was-elsewhere (a daily count by the server's midnight, with the users nine hours ahead).
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== Timesheet ==
A week of shifts, and hours over 8 in a day paid at time and a half - by which day?
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 0
Pick a number from 1 to 5.
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> x
Pick a number from 1 to 5.
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 3
worker (1 Ana, 2 Ben, 3 Cleo, 4 Dan, 5 Eve)>
Cancelled.
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 3
worker (1 Ana, 2 Ben, 3 Cleo, 4 Dan, 5 Eve)> 0
Type a number from 1 to 5.
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 3
worker (1 Ana, 2 Ben, 3 Cleo, 4 Dan, 5 Eve)> 6
Type a number from 1 to 5.
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 3
worker (1 Ana, 2 Ben, 3 Cleo, 4 Dan, 5 Eve)> x
Type a number from 1 to 5.
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 4
start hour (0 to 23)>
Cancelled.
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 4
start hour (0 to 23)> 24
Type a whole number from 0 to 23.
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 4
start hour (0 to 23)> x
Type a whole number from 0 to 23.
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 4
start hour (0 to 23)> 9
hours (1 to 16)>
Cancelled.
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 4
start hour (0 to 23)> 9
hours (1 to 16)> 0
Type a whole number from 1 to 16.
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 4
start hour (0 to 23)> 9
hours (1 to 16)> 17
Type a whole number from 1 to 16.
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 4
start hour (0 to 23)> 9
hours (1 to 16)> x
Type a whole number from 1 to 16.
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 4
start hour (0 to 23)> 2
hours (1 to 16)> 12
A Wednesday shift 02:00-14:00, 12 hours:
by the shift: 4 over 8
by the shop's day: Wed 12 - 4 over 8
by the server's day: Tue 7, Wed 5 - 0 over 8
The server's midnight, 09:00 here, cuts it in two.
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 4
start hour (0 to 23)> 9
hours (1 to 16)> 6
A Wednesday shift 09:00-15:00, 6 hours:
by the shift: 0 over 8
by the shop's day: Wed 6 - 0 over 8
by the server's day: Wed 6 - 0 over 8
No more than 8 hours: no reading pays any at time and a half.
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 5
Bye.
What was typed (35 lines)
0
x
3
3
0
3
6
3
x
4
4
24
4
x
4
9
4
9
0
4
9
17
4
9
x
4
2
12
4
9
6
5
basic
interpreter: byte-equal== Timesheet ==
A week of shifts, and hours over 8 in a day paid at time and a half - by which day?
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 1
The week in the shop's time, UTC+9. The payroll server keeps UTC, so its midnight is 09:00 here.
Hours over 8 in a day are paid at time and a half: 20.00 an hour, 30.00 for each hour over 8.
Ana Mon Tue Wed Thu Fri 09:00-19:00, 10 hours
Ben Mon Tue Wed Thu Fri 22:00-08:00, 10 hours
Cleo Mon Tue Wed Thu Fri 15:00-01:00, 10 hours
Dan Mon 07:00-23:00, 16 hours; Wed Thu Fri 07:00-15:00, 8 hours
Eve Mon Wed Fri 20:00-08:00, 12 hours
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 2
Hours over 8 in a day, with the day read three ways:
worker hours by shift shop day server day
Ana 50 10 10 10
Ben 50 10 8 10
Cleo 50 10 9 10
Dan 40 8 8 6
Eve 36 12 0 12
all 226 50 35 48
Pay for the week: 5,020.00 by the shift, 4,870.00 by the shop's day, 5,000.00 by the server's day.
Hours over 8 paid as plain hours by the shop's day: Ben 2, Cleo 1 and Eve 12. A shift that crosses midnight starts its count again at 0.
Hours over 8 paid as plain hours by the server's day: Dan 2. The server's midnight is 09:00 here, and it cuts the shifts that cross it.
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 3
worker (1 Ana, 2 Ben, 3 Cleo, 4 Dan, 5 Eve)> 5
Eve by the shift: Mon 12, Wed 12, Fri 12 - 12 hours over 8
Eve by the shop's day: Mon 4, Tue 8, Wed 4, Thu 8, Fri 4, Sat 8 - 0 hours over 8
Eve by the server's day: Mon 12, Wed 12, Fri 12 - 12 hours over 8
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 3
worker (1 Ana, 2 Ben, 3 Cleo, 4 Dan, 5 Eve)> 4
Dan by the shift: Mon 16, Wed 8, Thu 8, Fri 8 - 8 hours over 8
Dan by the shop's day: Mon 16, Wed 8, Thu 8, Fri 8 - 8 hours over 8
Dan by the server's day: Sun 2, Mon 14, Tue 2, Wed 8, Thu 8, Fri 6 - 6 hours over 8
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 4
start hour (0 to 23)> 22
hours (1 to 16)> 10
A Wednesday shift 22:00-08:00, 10 hours:
by the shift: 2 over 8
by the shop's day: Wed 2, Thu 8 - 0 over 8
by the server's day: Wed 10 - 2 over 8
Midnight in the shop cuts it in two, and each part counts from 0.
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 4
start hour (0 to 23)> 23
hours (1 to 16)> 11
A Wednesday shift 23:00-10:00, 11 hours:
by the shift: 3 over 8
by the shop's day: Wed 1, Thu 10 - 2 over 8
by the server's day: Wed 10, Thu 1 - 2 over 8
Midnight in the shop cuts it in two, and each part counts from 0.
The server's midnight, 09:00 here, cuts it in two.
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 4
start hour (0 to 23)> 10
hours (1 to 16)> 10
A Wednesday shift 10:00-20:00, 10 hours:
by the shift: 2 over 8
by the shop's day: Wed 10 - 2 over 8
by the server's day: Wed 10 - 2 over 8
No midnight falls inside it: every reading pays the same.
1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit
choice> 5
Bye.
What was typed (16 lines)
1
2
3
5
3
4
4
22
10
4
23
11
4
10
10
5
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# P162 timesheet: a week of shifts and "hours over 8 in a day are paid at
# time and a half" - the day read as the shift, the shop's calendar day and
# the payroll server's day; one worker's days, and a shift of your own.
import shifts
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 digits(t):
for c in t:
if c < "0" or c > "9":
return False
return len(t) > 0
def money(cents):
cents % 100 => c
int((cents - c) / 100) => w
str(w) => s
if w >= 1000:
str(int((w - w % 1000) / 1000)) + "," + str(w % 1000 + 1000)[1:] => s
if c < 10:
return s + ".0" + str(c)
return s + "." + str(c)
def clock(h):
h % 24 => h
if h < 10:
return "0" + str(h) + ":00"
return str(h) + ":00"
def span(s):
return clock(s[1]) + "-" + clock(s[1] + s[2]) + ", " + str(s[2]) + " hours"
def show_shifts():
"The week in the shop's time, UTC+9. The payroll server keeps UTC, so its midnight is 09:00 here." ^0
"Hours over 8 in a day are paid at time and a half: 20.00 an hour, 30.00 for each hour over 8." ^0
for w in shifts.workers():
# shifts with the same hours, grouped: [start, hours, days]
[] => groups
for s in w[1]:
False => found
for g in groups:
if g[0] == s[1] and g[1] == s[2]:
g[2] + " " + shifts.day_name(s[0]) => g[2]
True => found
if not found:
groups + [[s[1], s[2], shifts.day_name(s[0])]] => groups
"" => line
for g in groups:
if line != "":
line + "; " => line
line + g[2] + " " + span([0, g[0], g[1]]) => line
(" " + pad(w[0], 6) + line) ^0
def readings(w):
# [hours, over by shift, over by the shop's day, over by the server's day]
return [shifts.total(w[1]), shifts.over_shifts(w[1]), shifts.over_days(shifts.days(w[1], False)), shifts.over_days(shifts.days(w[1], True))]
def names(list):
# "Ben 2, Cleo 1 and Eve 12"
"" => out
0 => i
while i < len(list):
if i > 0 and i == len(list) - 1:
out + " and " => out
elif i > 0:
out + ", " => out
out + list[i] => out
i + 1 => i
return out
def show_overtime():
"Hours over 8 in a day, with the day read three ways:" ^0
(" " + pad("worker", 8) + lpad("hours", 5) + lpad("by shift", 10) + lpad("shop day", 10) + lpad("server day", 12)) ^0
[0, 0, 0, 0] => sums
[] => lost_shop
[] => lost_server
for w in shifts.workers():
readings(w) => r
(" " + pad(w[0], 8) + lpad(str(r[0]), 5) + lpad(str(r[1]), 10) + lpad(str(r[2]), 10) + lpad(str(r[3]), 12)) ^0
0 => k
while k < 4:
sums[k] + r[k] => sums[k]
k + 1 => k
if r[2] < r[1]:
lost_shop + [w[0] + " " + str(r[1] - r[2])] => lost_shop
if r[3] < r[1]:
lost_server + [w[0] + " " + str(r[1] - r[3])] => lost_server
(" " + pad("all", 8) + lpad(str(sums[0]), 5) + lpad(str(sums[1]), 10) + lpad(str(sums[2]), 10) + lpad(str(sums[3]), 12)) ^0
("Pay for the week: " + money(shifts.pay(sums[0], sums[1])) + " by the shift, " + money(shifts.pay(sums[0], sums[2])) + " by the shop's day, " + money(shifts.pay(sums[0], sums[3])) + " by the server's day.") ^0
if len(lost_shop) > 0:
("Hours over 8 paid as plain hours by the shop's day: " + names(lost_shop) + ". A shift that crosses midnight starts its count again at 0.") ^0
if len(lost_server) > 0:
("Hours over 8 paid as plain hours by the server's day: " + names(lost_server) + ". The server's midnight is 09:00 here, and it cuts the shifts that cross it.") ^0
def day_list(ds):
"" => out
for e in ds:
if out != "":
out + ", " => out
out + shifts.day_name(e[0]) + " " + str(e[1]) => out
return out
def show_worker():
plain(input("worker (1 Ana, 2 Ben, 3 Cleo, 4 Dan, 5 Eve)> ")) => t
if t == "":
"Cancelled." ^0
return
if len(t) != 1 or not digits(t) or int(t) < 1 or int(t) > 5:
"Type a number from 1 to 5." ^0
return
shifts.workers()[int(t) - 1] => w
"" => line
for s in w[1]:
if line != "":
line + ", " => line
line + shifts.day_name(s[0]) + " " + str(s[2]) => line
(w[0] + " by the shift: " + line + " - " + str(shifts.over_shifts(w[1])) + " hours over 8") ^0
shifts.days(w[1], False) => a
(w[0] + " by the shop's day: " + day_list(a) + " - " + str(shifts.over_days(a)) + " hours over 8") ^0
shifts.days(w[1], True) => b
(w[0] + " by the server's day: " + day_list(b) + " - " + str(shifts.over_days(b)) + " hours over 8") ^0
def own():
plain(input("start hour (0 to 23)> ")) => t
if t == "":
"Cancelled." ^0
return
if len(t) > 2 or not digits(t) or int(t) > 23:
"Type a whole number from 0 to 23." ^0
return
plain(input("hours (1 to 16)> ")) => n
if n == "":
"Cancelled." ^0
return
if len(n) > 2 or not digits(n) or int(n) < 1 or int(n) > 16:
"Type a whole number from 1 to 16." ^0
return
[[2, int(t), int(n)]] => one
readings(["yours", one]) => r
shifts.days(one, False) => a
shifts.days(one, True) => b
("A Wednesday shift " + span(one[0]) + ":") ^0
(" by the shift: " + str(r[1]) + " over 8") ^0
(" by the shop's day: " + day_list(a) + " - " + str(r[2]) + " over 8") ^0
(" by the server's day: " + day_list(b) + " - " + str(r[3]) + " over 8") ^0
if r[1] == 0:
"No more than 8 hours: no reading pays any at time and a half." ^0
return
if r[2] < r[1]:
"Midnight in the shop cuts it in two, and each part counts from 0." ^0
if r[3] < r[1]:
"The server's midnight, 09:00 here, cuts it in two." ^0
if r[2] == r[1] and r[3] == r[1]:
"No midnight falls inside it: every reading pays the same." ^0
"== Timesheet ==" ^0
"A week of shifts, and hours over 8 in a day paid at time and a half - by which day?" ^0
True => running
while running:
"" ^0
"1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit" ^0
plain(input("choice> ")) => choice
if choice == "1":
show_shifts()
elif choice == "2":
show_overtime()
elif choice == "3":
show_worker()
elif choice == "4":
own()
elif choice == "5":
False => running
else:
"Pick a number from 1 to 5." ^0
"Bye." ^0
Python projection (main.py)
import shifts
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 digits(t):
for c in t:
if c < "0" or c > "9":
return False
return len(t) > 0
def money(cents):
c = cents % 100
w = int((cents - c) / 100)
s = str(w)
if w >= 1000:
s = str(int((w - w % 1000) / 1000)) + "," + str(w % 1000 + 1000)[1:]
if c < 10:
return s + ".0" + str(c)
return s + "." + str(c)
def clock(h):
h = h % 24
if h < 10:
return "0" + str(h) + ":00"
return str(h) + ":00"
def span(s):
return clock(s[1]) + "-" + clock(s[1] + s[2]) + ", " + str(s[2]) + " hours"
def show_shifts():
print("The week in the shop's time, UTC+9. The payroll server keeps UTC, so its midnight is 09:00 here.")
print("Hours over 8 in a day are paid at time and a half: 20.00 an hour, 30.00 for each hour over 8.")
for w in shifts.workers():
groups = []
for s in w[1]:
found = False
for g in groups:
if g[0] == s[1] and g[1] == s[2]:
g[2] = g[2] + " " + shifts.day_name(s[0])
found = True
if not found:
groups = groups + [[s[1], s[2], shifts.day_name(s[0])]]
line = ""
for g in groups:
if line != "":
line = line + "; "
line = line + g[2] + " " + span([0, g[0], g[1]])
print(" " + pad(w[0], 6) + line)
def readings(w):
return [shifts.total(w[1]), shifts.over_shifts(w[1]), shifts.over_days(shifts.days(w[1], False)), shifts.over_days(shifts.days(w[1], True))]
def names(lst):
out = ""
i = 0
while i < len(lst):
if i > 0 and i == len(lst) - 1:
out = out + " and "
elif i > 0:
out = out + ", "
out = out + lst[i]
i = i + 1
return out
def show_overtime():
print("Hours over 8 in a day, with the day read three ways:")
print(" " + pad("worker", 8) + lpad("hours", 5) + lpad("by shift", 10) + lpad("shop day", 10) + lpad("server day", 12))
sums = [0, 0, 0, 0]
lost_shop = []
lost_server = []
for w in shifts.workers():
r = readings(w)
print(" " + pad(w[0], 8) + lpad(str(r[0]), 5) + lpad(str(r[1]), 10) + lpad(str(r[2]), 10) + lpad(str(r[3]), 12))
k = 0
while k < 4:
sums[k] = sums[k] + r[k]
k = k + 1
if r[2] < r[1]:
lost_shop = lost_shop + [w[0] + " " + str(r[1] - r[2])]
if r[3] < r[1]:
lost_server = lost_server + [w[0] + " " + str(r[1] - r[3])]
print(" " + pad("all", 8) + lpad(str(sums[0]), 5) + lpad(str(sums[1]), 10) + lpad(str(sums[2]), 10) + lpad(str(sums[3]), 12))
print("Pay for the week: " + money(shifts.pay(sums[0], sums[1])) + " by the shift, " + money(shifts.pay(sums[0], sums[2])) + " by the shop's day, " + money(shifts.pay(sums[0], sums[3])) + " by the server's day.")
if len(lost_shop) > 0:
print("Hours over 8 paid as plain hours by the shop's day: " + names(lost_shop) + ". A shift that crosses midnight starts its count again at 0.")
if len(lost_server) > 0:
print("Hours over 8 paid as plain hours by the server's day: " + names(lost_server) + ". The server's midnight is 09:00 here, and it cuts the shifts that cross it.")
def day_list(ds):
out = ""
for e in ds:
if out != "":
out = out + ", "
out = out + shifts.day_name(e[0]) + " " + str(e[1])
return out
def show_worker():
t = plain(input("worker (1 Ana, 2 Ben, 3 Cleo, 4 Dan, 5 Eve)> "))
if t == "":
print("Cancelled.")
return
if len(t) != 1 or not digits(t) or int(t) < 1 or int(t) > 5:
print("Type a number from 1 to 5.")
return
w = shifts.workers()[int(t) - 1]
line = ""
for s in w[1]:
if line != "":
line = line + ", "
line = line + shifts.day_name(s[0]) + " " + str(s[2])
print(w[0] + " by the shift: " + line + " - " + str(shifts.over_shifts(w[1])) + " hours over 8")
a = shifts.days(w[1], False)
print(w[0] + " by the shop's day: " + day_list(a) + " - " + str(shifts.over_days(a)) + " hours over 8")
b = shifts.days(w[1], True)
print(w[0] + " by the server's day: " + day_list(b) + " - " + str(shifts.over_days(b)) + " hours over 8")
def own():
t = plain(input("start hour (0 to 23)> "))
if t == "":
print("Cancelled.")
return
if len(t) > 2 or not digits(t) or int(t) > 23:
print("Type a whole number from 0 to 23.")
return
n = plain(input("hours (1 to 16)> "))
if n == "":
print("Cancelled.")
return
if len(n) > 2 or not digits(n) or int(n) < 1 or int(n) > 16:
print("Type a whole number from 1 to 16.")
return
one = [[2, int(t), int(n)]]
r = readings(["yours", one])
a = shifts.days(one, False)
b = shifts.days(one, True)
print("A Wednesday shift " + span(one[0]) + ":")
print(" by the shift: " + str(r[1]) + " over 8")
print(" by the shop's day: " + day_list(a) + " - " + str(r[2]) + " over 8")
print(" by the server's day: " + day_list(b) + " - " + str(r[3]) + " over 8")
if r[1] == 0:
print("No more than 8 hours: no reading pays any at time and a half.")
return
if r[2] < r[1]:
print("Midnight in the shop cuts it in two, and each part counts from 0.")
if r[3] < r[1]:
print("The server's midnight, 09:00 here, cuts it in two.")
if r[2] == r[1] and r[3] == r[1]:
print("No midnight falls inside it: every reading pays the same.")
print("== Timesheet ==")
print("A week of shifts, and hours over 8 in a day paid at time and a half - by which day?")
running = True
while running:
print("")
print("1) the shifts 2) overtime 3) one worker's days 4) a shift of your own 5) quit")
choice = plain(input("choice> "))
if choice == "1":
show_shifts()
elif choice == "2":
show_overtime()
elif choice == "3":
show_worker()
elif choice == "4":
own()
elif choice == "5":
running = False
else:
print("Pick a number from 1 to 5.")
print("Bye.")
shifts.eml
eml# P162 timesheet - a shop's week of shifts and the rule "hours over 8 in a
# day are paid at time and a half", read three ways. As in the corpus case
# the-counter-reset-at-midnight-and-the-shift-crossed-it, a count that
# returns to zero at midnight starts again in the middle of any shift that
# crosses it, so a long night shift is two short days. And as in
# the-day-boundary-was-the-servers-and-the-user-was-elsewhere, the payroll
# server's midnight is UTC, nine hours behind the shop: its day ends at 09:00
# here. Times are whole hours from Monday 00:00 in the shop.
def zone():
# the shop is UTC+9
return 9
def rate():
# cents an hour, and for each hour over 8
return 2000
def over_rate():
return 3000
def day_name(k):
# k = -1 is the Sunday before
return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"][k + 1]
def workers():
# [name, shifts]; a shift is [day (0 = Monday), start hour, hours]
return [["Ana", [[0, 9, 10], [1, 9, 10], [2, 9, 10], [3, 9, 10], [4, 9, 10]]],
["Ben", [[0, 22, 10], [1, 22, 10], [2, 22, 10], [3, 22, 10], [4, 22, 10]]],
["Cleo", [[0, 15, 10], [1, 15, 10], [2, 15, 10], [3, 15, 10], [4, 15, 10]]],
["Dan", [[0, 7, 16], [2, 7, 8], [3, 7, 8], [4, 7, 8]]],
["Eve", [[0, 20, 12], [2, 20, 12], [4, 20, 12]]]]
def day_of(h):
# the whole day hour h falls in, h >= 0
return int((h - h % 24) / 24)
def days(shifts, server):
# hours worked on each day, as [[day, hours], ...] in order: the shop's
# days, or the server's (UTC) days, where day -1 is the Sunday before
[] => out
for s in shifts:
0 => k
while k < s[2]:
s[0] * 24 + s[1] + k => h
if server:
day_of(h + 24 - zone()) - 1 => d
else:
day_of(h) => d
False => found
for e in out:
if e[0] == d:
e[1] + 1 => e[1]
True => found
if not found:
out + [[d, 1]] => out
k + 1 => k
return out
def over_days(ds):
# hours over 8 in each day, summed
0 => t
for e in ds:
if e[1] > 8:
t + e[1] - 8 => t
return t
def over_shifts(shifts):
0 => t
for s in shifts:
if s[2] > 8:
t + s[2] - 8 => t
return t
def total(shifts):
0 => t
for s in shifts:
t + s[2] => t
return t
def pay(hours, over):
# cents for a week of hours with that many over 8
return (hours - over) * rate() + over * over_rate()
Python projection (shifts.py)
def zone():
return 9
def rate():
return 2000
def over_rate():
return 3000
def day_name(k):
return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"][k + 1]
def workers():
return [["Ana", [[0, 9, 10], [1, 9, 10], [2, 9, 10], [3, 9, 10], [4, 9, 10]]], ["Ben", [[0, 22, 10], [1, 22, 10], [2, 22, 10], [3, 22, 10], [4, 22, 10]]], ["Cleo", [[0, 15, 10], [1, 15, 10], [2, 15, 10], [3, 15, 10], [4, 15, 10]]], ["Dan", [[0, 7, 16], [2, 7, 8], [3, 7, 8], [4, 7, 8]]], ["Eve", [[0, 20, 12], [2, 20, 12], [4, 20, 12]]]]
def day_of(h):
return int((h - h % 24) / 24)
def days(shifts, server):
out = []
for s in shifts:
k = 0
while k < s[2]:
h = s[0] * 24 + s[1] + k
if server:
d = day_of(h + 24 - zone()) - 1
else:
d = day_of(h)
found = False
for e in out:
if e[0] == d:
e[1] = e[1] + 1
found = True
if not found:
out = out + [[d, 1]]
k = k + 1
return out
def over_days(ds):
t = 0
for e in ds:
if e[1] > 8:
t = t + e[1] - 8
return t
def over_shifts(shifts):
t = 0
for s in shifts:
if s[2] > 8:
t = t + s[2] - 8
return t
def total(shifts):
t = 0
for s in shifts:
t = t + s[2]
return t
def pay(hours, over):
return (hours - over) * rate() + over * over_rate()