Files
proc-rota/rota.ipynb
T
2020-04-11 18:33:17 +01:00

103 KiB

In [1]:
from pyomo.environ import *
from pyomo.opt import SolverFactory
from shifts import SingleShift, RotaBuilder, days, sites
from workers import Worker

import math

import scipy.stats as stats

import csv

import statistics

import datetime
import itertools
import operator

from functools import reduce
def prod(iterable):
    return reduce(operator.mul, iterable, 1)
In [2]:
#night_balance_offset = 2
#twilight_balance_offset = 1
weeks_to_rota = 16
use_test_workers = True
In [3]:

Rota = RotaBuilder(weeks_to_rota=weeks_to_rota)

Rota.add_shifts(
    SingleShift(("exeter",), "exeter_twilight", 12.5, days[:5]),
    SingleShift(("truro",), "truro_twilight", 12.5, days[:5]),
    SingleShift(("torquay",), "torquay_twilight", 12.5, days[:5]),
    SingleShift(("plymouth",), "plymouth_twilight", 12.5, days[:5]),  
    SingleShift(("exeter",), "weekend_exeter", 12.5, days[5:]),    
    SingleShift(("truro",), "weekend_truro", 12.5, days[5:]),      
    SingleShift(("torquay",), "weekend_torquay", 12.5, days[5:]),  
    SingleShift(("plymouth",), "weekend_plymouth", 8, days[5:], workers_required=2),  
    SingleShift((sites), "night", 12.5, days, balance_offset=2, balance_weighting=2, workers_required=3, rota_on_nwds=True),   
)

#Rota2 = RotaBuilder(weeks_to_rota=weeks_to_rota)

#Rota2.add_shifts(
#    SingleShift(("exeter",), "exeter_twilight", 12.5, days[:5], balance_offset=3),  
#    SingleShift(("exeter",), "weekend_exeter", 12.5, days[5:], balance_offset=3, balance_weighting=1), 
#    SingleShift((sites), "night", 12.5, days, balance_offset=2, balance_weighting=4, workers_required=3, rota_on_nwds=True),   
#)

#Rota = Rota2


In [4]:
if use_test_workers:
    # Enter workers ids (name, number, ...)
    #workers.extend([Worker(Rota, i, "Test Person {}".format(i), "truro", 3, nwd=['Fri', "Sat", "Sun"]) for i in range(1, 2)])
    Rota.add_workers([Worker(Rota,i, "Test Person {}".format(i), "truro", 4, ) for i in range(1, 15)])
    Rota.add_workers([Worker(Rota,i, "Test Person {}".format(i), "plymouth", 4) for i in range(15, 30)])
    Rota.add_workers([Worker(Rota,i, "Test Person {}".format(i), "exeter", 4) for i in range(30, 45)])
    Rota.add_workers([Worker(Rota,i, "Test Person {}".format(i), "torquay", 4) for i in range(45, 60)])
    #workers.extend([workerClass(i, "Test Person {}".format(i), 4, 200, "") for i in range(48, 49)])
    
    #workers.extend([Worker(Rota,i, "Test Person {}".format(i), "exeter", 4) for i in range(34, 45)])
else:
    # Import trainee data
    import csv

    with open('trainees.csv', newline='') as f:
        reader = csv.reader(f, delimiter=',', quotechar='|')
        next(reader) # skip header

        n = 0
        for row in reader:
            n = n + 1
            #print(row)
            name, site, grade, fte, nwd, end_date, oop, extra = row

            # Ignore trainees if fte == 0 (what are they doing here anyway)
            if fte == 0:
                continue

            nwds = nwd.split(",").title() if nwd else None
            end_date = end_date if end_date else None
            oop = oop.split("-") if oop else None
            #print(nwds, end_date, oop)
            Rota.add_worker(Worker(Rota, n, name, site.lower(), int(grade[2]), int(fte), nwds, end_date, oop))


In [5]:
Rota.build_workers()
Rota.build_model()
In [6]:
opt = SolverFactory('cbc')  # choose a solver
#opt = SolverFactory('ipopt')  # choose a solver

results = opt.solve(Rota.model, tee=True, options={"seconds":600, "allow": 25})  # solve the model with the, options="seconds=60" selected solver
Welcome to the CBC MILP Solver 
Version: 2.9.9 
Build Date: Aug  7 2019 

command line - /usr/bin/cbc -seconds 600 -allow 25 -printingOptions all -import /tmp/tmpua24r1de.pyomo.lp -stat=1 -solve -solu /tmp/tmpua24r1de.pyomo.soln (default strategy 1)
seconds was changed from 1e+100 to 600
allowableGap was changed from 1e-10 to 25
Option for printingOptions changed from normal to all
Presolve 8525 (-7679) rows, 33158 (-28852) columns and 138886 (-135111) elements
Statistics for presolved model
Original problem has 61360 integers (61360 of which binary)
Presolved problem has 33040 integers (33040 of which binary)
==== 33040 zero objective 2 different
33040 variables have objective of 0
118 variables have objective of 1
==== absolute objective values 2 different
33040 variables have objective of 0
118 variables have objective of 1
==== for integers 33040 zero objective 1 different
33040 variables have objective of 0
==== for integers absolute objective values 1 different
33040 variables have objective of 0
===== end objective counts


Problem has 8525 rows, 33158 columns (118 with objective) and 138886 elements
There are 118 singletons with objective 
Column breakdown:
118 of type 0.0->inf, 0 of type 0.0->up, 0 of type lo->inf, 
0 of type lo->up, 0 of type free, 0 of type fixed, 
0 of type -inf->0.0, 0 of type -inf->up, 33040 of type 0.0->1.0 
Row breakdown:
0 of type E 0.0, 416 of type E 1.0, 0 of type E -1.0, 
203 of type E other, 0 of type G 0.0, 0 of type G 1.0, 
177 of type G other, 0 of type L 0.0, 6608 of type L 1.0, 
1121 of type L other, 0 of type Range 0.0->1.0, 0 of type Range other, 
0 of type Free 
Continuous objective value is 0 - 0.33 seconds
Cgl0002I 26432 variables fixed
Cgl0004I processed model has 8348 rows, 21350 columns (21232 integer (21232 of which binary)) and 90246 elements
Cbc0038I Initial state - 159 integers unsatisfied sum - 34.8044
Cbc0038I Pass   1: (0.88 seconds) suminf.    9.38095 (36) obj. 40.5517 iterations 464
Cbc0038I Pass   2: (0.88 seconds) suminf.    9.38095 (36) obj. 40.5517 iterations 0
Cbc0038I Pass   3: (0.90 seconds) suminf.    5.00952 (20) obj. 48.6712 iterations 122
Cbc0038I Pass   4: (0.92 seconds) suminf.    4.34286 (19) obj. 49.0712 iterations 31
Cbc0038I Pass   5: (0.94 seconds) suminf.    2.60952 (10) obj. 51.7785 iterations 80
Cbc0038I Pass   6: (0.96 seconds) suminf.    2.60952 (10) obj. 51.7785 iterations 11
Cbc0038I Pass   7: (0.98 seconds) suminf.    2.60952 (10) obj. 52.9747 iterations 60
Cbc0038I Pass   8: (1.00 seconds) suminf.    2.60952 (10) obj. 51.1296 iterations 60
Cbc0038I Pass   9: (1.04 seconds) suminf.   27.94802 (95) obj. 192.581 iterations 537
Cbc0038I Pass  10: (1.07 seconds) suminf.   16.67684 (61) obj. 186.06 iterations 303
Cbc0038I Pass  11: (1.08 seconds) suminf.   15.67119 (61) obj. 186.941 iterations 34
Cbc0038I Pass  12: (1.10 seconds) suminf.   12.72768 (49) obj. 180.727 iterations 106
Cbc0038I Pass  13: (1.12 seconds) suminf.   12.33785 (48) obj. 179.814 iterations 48
Cbc0038I Pass  14: (1.14 seconds) suminf.   12.33785 (48) obj. 179.814 iterations 2
Cbc0038I Pass  15: (1.16 seconds) suminf.    9.84068 (39) obj. 180.049 iterations 116
Cbc0038I Pass  16: (1.18 seconds) suminf.    9.84068 (39) obj. 180.049 iterations 24
Cbc0038I Pass  17: (1.20 seconds) suminf.    7.43874 (31) obj. 180.126 iterations 58
Cbc0038I Pass  18: (1.22 seconds) suminf.    7.43874 (31) obj. 180.126 iterations 26
Cbc0038I Pass  19: (1.24 seconds) suminf.    5.72446 (25) obj. 179.71 iterations 70
Cbc0038I Pass  20: (1.26 seconds) suminf.    5.72446 (25) obj. 179.71 iterations 26
Cbc0038I Pass  21: (1.29 seconds) suminf.    5.85779 (21) obj. 179.251 iterations 110
Cbc0038I Pass  22: (1.30 seconds) suminf.    5.19112 (21) obj. 179.651 iterations 58
Cbc0038I Pass  23: (1.33 seconds) suminf.    5.59112 (21) obj. 175.843 iterations 122
Cbc0038I Pass  24: (1.34 seconds) suminf.    5.19112 (21) obj. 176.243 iterations 43
Cbc0038I Pass  25: (1.36 seconds) suminf.    5.59112 (21) obj. 176.051 iterations 85
Cbc0038I Pass  26: (1.38 seconds) suminf.    5.19112 (21) obj. 176.451 iterations 52
Cbc0038I Pass  27: (1.40 seconds) suminf.    5.59112 (21) obj. 175.843 iterations 75
Cbc0038I Pass  28: (1.42 seconds) suminf.    5.19112 (21) obj. 176.243 iterations 36
Cbc0038I Pass  29: (1.44 seconds) suminf.    5.59112 (21) obj. 177.518 iterations 118
Cbc0038I Pass  30: (1.46 seconds) suminf.    5.19112 (21) obj. 177.918 iterations 42
Cbc0038I No solution found this major pass
Cbc0038I Before mini branch and bound, 20552 integers at bound fixed and 25 continuous
Cbc0038I Full problem 8348 rows 21350 columns, reduced to 251 rows 531 columns
Cbc0038I Mini branch and bound did not improve solution (1.77 seconds)
Cbc0038I Full problem 8349 rows 21350 columns, reduced to 8349 rows 21350 columns - too large
Cbc0038I After 2.19 seconds - Feasibility pump exiting - took 1.40 seconds
Cbc0012I Integer solution of 19.715254 found by DiveCoefficient after 3604 iterations and 0 nodes (6.37 seconds)
Cbc0031I 124 added rows had average density of 258.29032
Cbc0013I At root node, 124 cuts changed objective from 0 to 10.135593 in 10 passes
Cbc0014I Cut generator 0 (Probing) - 0 row cuts average 0.0 elements, 0 column cuts (0 active)  in 0.389 seconds - new frequency is -100
Cbc0014I Cut generator 1 (Gomory) - 408 row cuts average 742.9 elements, 0 column cuts (0 active)  in 0.874 seconds - new frequency is 1
Cbc0014I Cut generator 2 (Knapsack) - 0 row cuts average 0.0 elements, 0 column cuts (0 active)  in 0.049 seconds - new frequency is -100
Cbc0014I Cut generator 3 (Clique) - 0 row cuts average 0.0 elements, 0 column cuts (0 active)  in 0.007 seconds - new frequency is -100
Cbc0014I Cut generator 4 (MixedIntegerRounding2) - 322 row cuts average 223.6 elements, 0 column cuts (0 active)  in 0.191 seconds - new frequency is 1
Cbc0014I Cut generator 5 (FlowCover) - 0 row cuts average 0.0 elements, 0 column cuts (0 active)  in 0.054 seconds - new frequency is -100
Cbc0014I Cut generator 6 (TwoMirCuts) - 274 row cuts average 277.7 elements, 0 column cuts (0 active)  in 0.458 seconds - new frequency is 1
Cbc0011I Exiting as integer gap of 9.579661 less than 25 or 0%%
Cbc0001I Search completed - best objective 19.71525423728813, took 3604 iterations and 0 nodes (6.39 seconds)
Cbc0035I Maximum depth 0, 0 variables fixed on reduced cost
Cuts at root node changed objective from 0 to 10.1356
Probing was tried 10 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.389 seconds)
Gomory was tried 10 times and created 408 cuts of which 0 were active after adding rounds of cuts (0.874 seconds)
Knapsack was tried 10 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.049 seconds)
Clique was tried 10 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.007 seconds)
MixedIntegerRounding2 was tried 10 times and created 322 cuts of which 0 were active after adding rounds of cuts (0.191 seconds)
FlowCover was tried 10 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.054 seconds)
TwoMirCuts was tried 10 times and created 274 cuts of which 0 were active after adding rounds of cuts (0.458 seconds)

Result - Optimal solution found (within gap tolerance)

Objective value:                19.71525424
Lower bound:                    10.136
Gap:                            0.95
Enumerated nodes:               0
Total iterations:               3604
Time (CPU seconds):             6.56
Time (Wallclock seconds):       6.66

Total time (CPU seconds):       7.38   (Wallclock seconds):       7.51

In [7]:
results.solver.status
Out [7]:
EnumValue(<pyutilib.enum.enum.Enum object at 0x7fbb9a1ae2d0>, 0, 'ok')
In [83]:
#def get_workers_needed(needed):
#    """Extract to a list the needed workers for the optimal solution."""
#    workers_needed = []
#    for worker in workers:
#        if needed[worker.id].value == 1:
#            workers_needed.append(worker.name)
#    return workers_needed


def get_work_table(works):
    """Build a timetable of the week as a dictionary from the model's optimal solution."""
    week_table = {week :{day: {shift: [] for shift in Rota.GetShiftNames()} for day in days} for week in Rota.weeks}
    for week in Rota.weeks:
        for worker in Rota.workers:
            for day, shift in Rota.GetAllDayShiftAsNames():
                        if works[worker.id, week, day, shift].value == 1:
                            week_table[week][day][shift].append(worker.get_details())
    return week_table

def get_worker_timetable(works):
    timetable = { worker.name : { week : { day: "" for day in days } for week in Rota.weeks } for worker in Rota.workers }
    for worker in Rota.workers:
        for week in Rota.weeks:
            for day, shift in Rota.GetAllDayShiftAsNames():
                    if works[worker.id, week, day, shift].value == 1:
                            timetable[worker.name][week][day] = shift
    return timetable

def get_worker_timetable_brief(works):
    timetable = []
    timetable.append("{:20}".format("Worker") + "".join([7*str("{}".format(w))[-1:] for w in Rota.weeks]))
    for worker in Rota.workers:
        w = ["{:20}".format(worker.name)]
        for week, day in Rota.GetAllWeeksDays():
            a = "-"
            for shift in Rota.GetShiftNamesByDay(day):
                if works[worker.id, week, day, shift].value == 1:
                    a = shift[0]
            w.append(a)
        timetable.append("".join(w))
    
    return "\n".join(timetable)

def get_shift_summary(works):
    timetable = { worker.get_details() : { shift[0] : "" for shift in Rota.GetShiftNames() } for worker in Rota.workers }
    #timetable = { worker.get_details() : { shift : "" for shift in ["truro_twilight"] } for worker in workers } 
    t = []
    for worker in Rota.workers:
        l = [worker.get_details()+": "]

        for shift in Rota.GetShiftNames():
        #for shift in ["truro_twilight"]:
            c = [works[worker.id, week, day, shift] for week in Rota.weeks for day in days].count(1)
            if c > 0:
                l.append("{} ({})".format(shift,c))
            #print(worker.id, shift)
            timetable[worker.get_details()][shift[0]] = c
        t.append(",".join(l))
    return "\n".join(t)    

#def get_no_preference(no_pref):
#    """Extract to a list the workers not satisfied with their weekend preference."""
#    return [worker.id for worker in workers if no_pref[worker.id].value == 1]

def get_night_blocks(nights):
    timetable = { worker.name : { week : { block: "" for block in Rota.night_blocks } for week in Rota.weeks } for worker in Rota.workers }
    for worker in Rota.workers:
        for week in Rota.weeks:
            for block in Rota.night_blocks:
                if nights[worker.id, week, block].value == 1:
                    timetable[worker.name][week][block] = "true"
    return timetable



#workers_needed = get_workers_needed(model.needed)  # dict with the optimal timetable
week_table = get_work_table(Rota.model.works)  # list with the required workers
worker_timetable = get_worker_timetable(Rota.model.works)
worker_timetable_brief = get_worker_timetable_brief(Rota.model.works)
#workers_no_pref = get_no_preference(Rota.model.no_pref)  # list with the non-satisfied workers (work on Sat but not on Sun)
worker_night_block = get_night_blocks(Rota.model.nights)
worker_shift_summary = get_shift_summary(Rota.model.works)
In [98]:
def ExportRotaToCSV(works):
    with open("rota.csv", 'w', newline='') as f:
        wr = csv.writer(f, quoting=csv.QUOTE_ALL)
        l = ["Name"]
        l.extend([worker.name for worker in Rota.workers])
        wr.writerow(l)

        l2 = ["Site"]
        l2.extend([worker.site for worker in Rota.workers])
        wr.writerow(l2)

        l3 = ["Grade"]
        l3.extend([worker.grade for worker in Rota.workers])
        wr.writerow(l3)

        l4 = ["FTE"]
        l4.extend([worker.fte for worker in Rota.workers])
        wr.writerow(l4)


        for week, day in Rota.GetAllWeeksDays():
            d = ["Week {} Day {}".format(week, day)]

            for worker in Rota.workers:
                i = ""
                for shift in Rota.GetShiftNamesByDay(day):
                    if works[worker.id, week, day, shift].value == 1:
                        i = shift

                d.append(i)
            wr.writerow(d)

ExportRotaToCSV(Rota.model.works)
In [99]:

print(worker_timetable_brief)
Worker              1111111222222233333334444444555555566666667777777888888899999990000000111111122222223333333444444455555556666666
Test Person 30      eee------n-------------------------n-e--w---e--------n-w------------w----ne------------------------n------------
Test Person 31      ---ee------------------e--nn-n----------------n-------w---e-------------------e------n------------------w-----n-
Test Person 32      -----------n--------ne----------e---------------w----------n----------e-----------w-n--ee--e--n------n----------
Test Person 33      ------------w--------------------n-e-----------------------ee-------n-------nnn--------n--------------ew---e----
Test Person 34      -----wwe-----------nn----------n-------e----------------n------n---------------------e-------n------e-------e---
Test Person 35      -------n-------en--w----------e------n---w------ne--n---e------e-------------------------n----------------------
Test Person 36      ------------------------e-w---n--------------ee---------------n----e------------------n--w---------nn--n--e-----
Test Person 37      -n-n----e--------------------------n-------n----------------nn---------e---w----------------------e--e-------e-w
Test Person 38      -----------e---------------w--n--------n---e--------e------------e---w-ne--n------------n------------n----------
Test Person 39      --------------e---e---------nen---w---n----------------------ww-e-n------------e-------------------e---n--------
Test Person 40      --------n---n---e-----------e---------------n-----e------e------n------------e--e-------------n--w------------w-
Test Person 41      ---------e---w---e---n---n----------e------------------n------n------------------e--------w---en------------n---
Test Person 42      ----------e-----------e-----n---------e---n----------e-------------------e--w---------n-----e--nw------------n--
Test Person 43      -------------------------e-------w--------e----w----------------n-e-------n---------e-e---------n-----n----n-n--
Test Person 44      --------------------w----n-----e-------n-----------e--------n--------n-------------w----nn---e-e---------e------
Test Person 15      pnppn-w--------p---n------w------w----------------n----w--n----------w------------------------p------p----------
Test Person 16      ----pwwp----w--------------w----------------------nn-------n----pn-----n--p-------------pw---p------------------
Test Person 17      --n--w--p-p--w---------np-------n-------w----------------------n--p-------------p-n--------n------------w-------
Test Person 18      --n--------pww-----------p---n------n--------------p-n------p-w------n-----w---------------p-------------n------
Test Person 19      -pn-----------p----w-------------------p----------------nn----w---------p-----------n-----------n------w-----pwn
Test Person 20      ---n------------pppwwp-----wp----------------------------n-----------------w-----n----------n-n-------------n---
Test Person 21      ----------n----------------------n-------w---p--w------------------n---p-------n---w--------p--p-wp-----nn------
Test Person 22      ------------nn---------------------------------wwp--n---p--------p-p-wp------------np-----w-------n-------n-----
Test Person 23      ----------------------p---w--p----wp--nn-wn--n---np-------------------------w--------------------w-p--------p---
Test Person 24      ------n---------------np-------p--w------n----------p--w-------p--------n-------n-w---------n---w---------p-----
Test Person 25      ----n----p--------------------p----------------w-----nw------------n--n------np--p-n--------------------w--p--w-
Test Person 26      ---------------------n----------pn----p----p---------p-------w---n--w-n-----w--------pp--nw-----w---------------
Test Person 27      ---------------------------------wn-p-----n------n-n------p--w-n-n-------p---------w----------------p----p-----w
Test Person 28      --------------------w----------------p--------p------------p-------------n--np----w------wnnn---------pw-------n
Test Person 29      ------n-------------------------n-------w-p-pn--------w--p--n-----n-w---n------p-------p-----------------------w
Test Person 45      t-t--n-------------w-----t------n-----------------------n----n-----------------------n-------t-t--n----w--------
Test Person 46      ----t--n----------------n------n------------n---------------t-------------t-----t-------t-w----n------n-------w-
Test Person 47      -----w---tt------------tn---t------------n--t-t-------n----------------n----w--------n----------n---------------
Test Person 48      ------w-t--------t---------------------------------t-----n----w---------------------------nn--------n-tnn--t----
Test Person 49      ------------w----n---t----w-------------n---------n-------------t------------t--nn-----t--n------------------t--
Test Person 50      --------------nn-----n--t--w------------w-----n-n------n-------t------t-------t-------------------t-------------
Test Person 51      -t--------------t--------------------------n------t-t-n-----------t-ww----------n-n--t-----------n--------------
Test Person 52      -----n-t--------n---------------------t---t----w-n-n---------w-----t-------n----------n--------------t----------
Test Person 53      ---------------n------------n-----------------n--------------n--nt---------------t-------w-ttnt-w---------------
Test Person 54      --------n--t--------w---n------tt----------------------------------n---t---w--n--------------------t--n-n-------
Test Person 55      -----------n-w-nn-------------------tn-t-------nw---------t------------------------wt---------------t----t------
Test Person 56      ------------------nn---------t-------------------t---t-wt------------------nn---------t-n--------w------w---t---
Test Person 57      ------------------t---t------nt----t-----w-----n------n--t---------------------------------------n--------nn---w
Test Person 58      ---------n----tt-----------------ww--t-----n---------------t----------n--t----n-----n--n------------------t-----
Test Person 59      ---t------n------n-----------------------n-t-t--------w----n------------t-n----t-nw-----------------------------
Test Person 1       n---n--tt-n---t-tt---------w----------t-w---n----------------------------------n---------------------------n----
Test Person 10      --------------n---------------------------ttt--------t-------w--------------------------t----------nt----n--nnww
Test Person 11      n-tt-n---t---n---------------------------------n-------n--n-----t-----------------w------------t--------w-------
Test Person 12      ------------------n--t---nnn--------------------w---------n---------------t---tt------t-------------n--w--t-----
Test Person 13      -t--------t-----------n---w-------n----------------t----------w-----n--t-------------------t-nt------n----------
Test Person 14      t---------------------nn-------------------------------w--t-------nt----n---w--n----t-------t--------------t----
Test Person 2       nn--tww----twn-t--n----n--------------------------------t----------------t----------------------------t---------
Test Person 3       ---n--------n----------------t--t------------tt-n---t----------t-----n-----w-------w-------------n--------------
Test Person 4       ------nn-----w------n-t---nn---n----t--------------------------------wt---------t-----------------t-------------
Test Person 5       --------n-----n--n----------t----w-t-------------t-------t--t-n--------------------------w-------------------tnn
Test Person 6       -----------n------t------t--------w--n--n------w------w-----------t----------n---t---t----------------------t-n-
Test Person 7       -----------------------tt---------n-n--------n----------------------w---t-n------------n--------w--t-----tn-----
Test Person 8       ---------n---------ww---------tt---n-t-tn---------t-------------------------------nn------w--t------------------
Test Person 9       ------------------------------------n-n--w----------n------t-----t--n----n---t---------t---------wn--t----------
In [100]:
#workers_needed
print(worker_shift_summary)
30 e 4: ,exeter_twilight (6),weekend_exeter (3),night (5)
31 e 4: ,exeter_twilight (5),weekend_exeter (2),night (6)
32 e 4: ,exeter_twilight (6),weekend_exeter (2),night (6)
33 e 4: ,exeter_twilight (5),weekend_exeter (2),night (6)
34 e 4: ,exeter_twilight (5),weekend_exeter (2),night (6)
35 e 4: ,exeter_twilight (5),weekend_exeter (2),night (6)
36 e 4: ,exeter_twilight (5),weekend_exeter (2),night (6)
37 e 4: ,exeter_twilight (5),weekend_exeter (2),night (6)
38 e 4: ,exeter_twilight (5),weekend_exeter (2),night (6)
39 e 4: ,exeter_twilight (6),weekend_exeter (3),night (5)
40 e 4: ,exeter_twilight (6),weekend_exeter (2),night (5)
41 e 4: ,exeter_twilight (5),weekend_exeter (2),night (6)
42 e 4: ,exeter_twilight (6),weekend_exeter (2),night (5)
43 e 4: ,exeter_twilight (5),weekend_exeter (2),night (6)
44 e 4: ,exeter_twilight (5),weekend_exeter (2),night (6)
15 p 4: ,plymouth_twilight (6),weekend_plymouth (5),night (5)
16 p 4: ,plymouth_twilight (6),weekend_plymouth (5),night (5)
17 p 4: ,plymouth_twilight (5),weekend_plymouth (4),night (6)
18 p 4: ,plymouth_twilight (5),weekend_plymouth (4),night (6)
19 p 4: ,plymouth_twilight (5),weekend_plymouth (4),night (6)
20 p 4: ,plymouth_twilight (5),weekend_plymouth (4),night (6)
21 p 4: ,plymouth_twilight (5),weekend_plymouth (4),night (6)
22 p 4: ,plymouth_twilight (6),weekend_plymouth (4),night (6)
23 p 4: ,plymouth_twilight (6),weekend_plymouth (5),night (5)
24 p 4: ,plymouth_twilight (5),weekend_plymouth (4),night (6)
25 p 4: ,plymouth_twilight (5),weekend_plymouth (4),night (6)
26 p 4: ,plymouth_twilight (6),weekend_plymouth (5),night (5)
27 p 4: ,plymouth_twilight (5),weekend_plymouth (4),night (6)
28 p 4: ,plymouth_twilight (5),weekend_plymouth (4),night (6)
29 p 4: ,plymouth_twilight (5),weekend_plymouth (4),night (6)
45 t 4: ,torquay_twilight (5),weekend_torquay (2),night (6)
46 t 4: ,torquay_twilight (5),weekend_torquay (2),night (6)
47 t 4: ,torquay_twilight (6),weekend_torquay (2),night (6)
48 t 4: ,torquay_twilight (5),weekend_torquay (2),night (6)
49 t 4: ,torquay_twilight (5),weekend_torquay (2),night (6)
50 t 4: ,torquay_twilight (5),weekend_torquay (2),night (6)
51 t 4: ,torquay_twilight (6),weekend_torquay (2),night (5)
52 t 4: ,torquay_twilight (5),weekend_torquay (2),night (6)
53 t 4: ,torquay_twilight (5),weekend_torquay (2),night (6)
54 t 4: ,torquay_twilight (5),weekend_torquay (2),night (6)
55 t 4: ,torquay_twilight (6),weekend_torquay (3),night (5)
56 t 4: ,torquay_twilight (6),weekend_torquay (3),night (5)
57 t 4: ,torquay_twilight (5),weekend_torquay (2),night (6)
58 t 4: ,torquay_twilight (6),weekend_torquay (2),night (6)
59 t 4: ,torquay_twilight (5),weekend_torquay (2),night (6)
1 t 4: ,truro_twilight (6),weekend_truro (2),night (6)
10 t 4: ,truro_twilight (6),weekend_truro (3),night (5)
11 t 4: ,truro_twilight (5),weekend_truro (2),night (6)
12 t 4: ,truro_twilight (6),weekend_truro (2),night (6)
13 t 4: ,truro_twilight (6),weekend_truro (2),night (5)
14 t 4: ,truro_twilight (6),weekend_truro (2),night (5)
2 t 4: ,truro_twilight (6),weekend_truro (3),night (5)
3 t 4: ,truro_twilight (6),weekend_truro (2),night (5)
4 t 4: ,truro_twilight (5),weekend_truro (2),night (6)
5 t 4: ,truro_twilight (6),weekend_truro (2),night (6)
6 t 4: ,truro_twilight (6),weekend_truro (3),night (5)
7 t 4: ,truro_twilight (5),weekend_truro (2),night (6)
8 t 4: ,truro_twilight (6),weekend_truro (3),night (5)
9 t 4: ,truro_twilight (5),weekend_truro (2),night (6)
In [101]:
week_table
Out [101]:
   'truro_twilight': ['10 t 4'],
   'torquay_twilight': ['47 t 4'],
   'plymouth_twilight': ['29 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['40 e 4', '46 t 4', '1 t 4']},
  'Thu': {'exeter_twilight': ['36 e 4'],
   'truro_twilight': ['3 t 4'],
   'torquay_twilight': ['59 t 4'],
   'plymouth_twilight': ['21 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['23 p 4', '29 p 4', '7 t 4']},
  'Fri': {'exeter_twilight': ['36 e 4'],
   'truro_twilight': ['3 t 4'],
   'torquay_twilight': ['47 t 4'],
   'plymouth_twilight': ['28 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['31 e 4', '50 t 4', '53 t 4']},
  'Sat': {'exeter_twilight': [],
   'truro_twilight': [],
   'torquay_twilight': [],
   'plymouth_twilight': [],
   'weekend_exeter': ['43 e 4'],
   'weekend_truro': ['6 t 4'],
   'weekend_torquay': ['52 t 4'],
   'weekend_plymouth': ['22 p 4', '25 p 4'],
   'night': ['55 t 4', '57 t 4', '11 t 4']},
  'Sun': {'exeter_twilight': [],
   'truro_twilight': [],
   'torquay_twilight': [],
   'plymouth_twilight': [],
   'weekend_exeter': ['32 e 4'],
   'weekend_truro': ['12 t 4'],
   'weekend_torquay': ['55 t 4'],
   'weekend_plymouth': ['21 p 4', '22 p 4'],
   'night': ['35 e 4', '50 t 4', '3 t 4']}},
 8: {'Mon': {'exeter_twilight': ['35 e 4'],
   'truro_twilight': ['5 t 4'],
   'torquay_twilight': ['56 t 4'],
   'plymouth_twilight': ['22 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['23 p 4', '27 p 4', '52 t 4']},
  'Tue': {'exeter_twilight': ['40 e 4'],
   'truro_twilight': ['8 t 4'],
   'torquay_twilight': ['51 t 4'],
   'plymouth_twilight': ['23 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['15 p 4', '16 p 4', '49 t 4']},
  'Wed': {'exeter_twilight': ['44 e 4'],
   'truro_twilight': ['13 t 4'],
   'torquay_twilight': ['48 t 4'],
   'plymouth_twilight': ['18 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['16 p 4', '27 p 4', '52 t 4']},
  'Thu': {'exeter_twilight': ['38 e 4'],
   'truro_twilight': ['3 t 4'],
   'torquay_twilight': ['51 t 4'],
   'plymouth_twilight': ['24 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['35 e 4', '22 p 4', '9 t 4']},
  'Fri': {'exeter_twilight': ['42 e 4'],
   'truro_twilight': ['10 t 4'],
   'torquay_twilight': ['56 t 4'],
   'plymouth_twilight': ['26 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['30 e 4', '18 p 4', '25 p 4']},
  'Sat': {'exeter_twilight': [],
   'truro_twilight': [],
   'torquay_twilight': [],
   'plymouth_twilight': [],
   'weekend_exeter': ['31 e 4'],
   'weekend_truro': ['6 t 4'],
   'weekend_torquay': ['59 t 4'],
   'weekend_plymouth': ['25 p 4', '29 p 4'],
   'night': ['47 t 4', '51 t 4', '57 t 4']},
  'Sun': {'exeter_twilight': [],
   'truro_twilight': [],
   'torquay_twilight': [],
   'plymouth_twilight': [],
   'weekend_exeter': ['30 e 4'],
   'weekend_truro': ['14 t 4'],
   'weekend_torquay': ['56 t 4'],
   'weekend_plymouth': ['15 p 4', '24 p 4'],
   'night': ['41 e 4', '50 t 4', '11 t 4']}},
 9: {'Mon': {'exeter_twilight': ['35 e 4'],
   'truro_twilight': ['2 t 4'],
   'torquay_twilight': ['56 t 4'],
   'plymouth_twilight': ['22 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['34 e 4', '19 p 4', '45 t 4']},
  'Tue': {'exeter_twilight': ['40 e 4'],
   'truro_twilight': ['5 t 4'],
   'torquay_twilight': ['57 t 4'],
   'plymouth_twilight': ['29 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['19 p 4', '20 p 4', '48 t 4']},
  'Wed': {'exeter_twilight': ['31 e 4'],
   'truro_twilight': ['14 t 4'],
   'torquay_twilight': ['55 t 4'],
   'plymouth_twilight': ['27 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['15 p 4', '11 t 4', '12 t 4']},
  'Thu': {'exeter_twilight': ['33 e 4'],
   'truro_twilight': ['9 t 4'],
   'torquay_twilight': ['58 t 4'],
   'plymouth_twilight': ['28 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['32 e 4', '16 p 4', '59 t 4']},
  'Fri': {'exeter_twilight': ['33 e 4'],
   'truro_twilight': ['5 t 4'],
   'torquay_twilight': ['46 t 4'],
   'plymouth_twilight': ['18 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['37 e 4', '44 e 4', '29 p 4']},
  'Sat': {'exeter_twilight': [],
   'truro_twilight': [],
   'torquay_twilight': [],
   'plymouth_twilight': [],
   'weekend_exeter': ['39 e 4'],
   'weekend_truro': ['10 t 4'],
   'weekend_torquay': ['52 t 4'],
   'weekend_plymouth': ['26 p 4', '27 p 4'],
   'night': ['37 e 4', '45 t 4', '53 t 4']},
  'Sun': {'exeter_twilight': [],
   'truro_twilight': [],
   'torquay_twilight': [],
   'plymouth_twilight': [],
   'weekend_exeter': ['39 e 4'],
   'weekend_truro': ['13 t 4'],
   'weekend_torquay': ['48 t 4'],
   'weekend_plymouth': ['18 p 4', '19 p 4'],
   'night': ['36 e 4', '41 e 4', '5 t 4']}},
 10: {'Mon': {'exeter_twilight': ['35 e 4'],
   'truro_twilight': ['3 t 4'],
   'torquay_twilight': ['50 t 4'],
   'plymouth_twilight': ['24 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['34 e 4', '17 p 4', '27 p 4']},
  'Tue': {'exeter_twilight': ['39 e 4'],
   'truro_twilight': ['11 t 4'],
   'torquay_twilight': ['49 t 4'],
   'plymouth_twilight': ['16 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['40 e 4', '43 e 4', '53 t 4']},
  'Wed': {'exeter_twilight': ['38 e 4'],
   'truro_twilight': ['9 t 4'],
   'torquay_twilight': ['53 t 4'],
   'plymouth_twilight': ['22 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['16 p 4', '26 p 4', '27 p 4']},
  'Thu': {'exeter_twilight': ['43 e 4'],
   'truro_twilight': ['6 t 4'],
   'torquay_twilight': ['51 t 4'],
   'plymouth_twilight': ['17 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['39 e 4', '29 p 4', '14 t 4']},
  'Fri': {'exeter_twilight': ['36 e 4'],
   'truro_twilight': ['14 t 4'],
   'torquay_twilight': ['52 t 4'],
   'plymouth_twilight': ['22 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['21 p 4', '25 p 4', '54 t 4']},
  'Sat': {'exeter_twilight': [],
   'truro_twilight': [],
   'torquay_twilight': [],
   'plymouth_twilight': [],
   'weekend_exeter': ['30 e 4'],
   'weekend_truro': ['7 t 4'],
   'weekend_torquay': ['51 t 4'],
   'weekend_plymouth': ['26 p 4', '29 p 4'],
   'night': ['33 e 4', '13 t 4', '9 t 4']},
  'Sun': {'exeter_twilight': [],
   'truro_twilight': [],
   'torquay_twilight': [],
   'plymouth_twilight': [],
   'weekend_exeter': ['38 e 4'],
   'weekend_truro': ['4 t 4'],
   'weekend_torquay': ['51 t 4'],
   'weekend_plymouth': ['15 p 4', '22 p 4'],
   'night': ['44 e 4', '18 p 4', '3 t 4']}},
 11: {'Mon': {'exeter_twilight': ['32 e 4'],
   'truro_twilight': ['4 t 4'],
   'torquay_twilight': ['50 t 4'],
   'plymouth_twilight': ['22 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['25 p 4', '26 p 4', '58 t 4']},
  'Tue': {'exeter_twilight': ['37 e 4'],
   'truro_twilight': ['13 t 4'],
   'torquay_twilight': ['54 t 4'],
   'plymouth_twilight': ['21 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['38 e 4', '16 p 4', '47 t 4']},
  'Wed': {'exeter_twilight': ['38 e 4'],
   'truro_twilight': ['7 t 4'],
   'torquay_twilight': ['59 t 4'],
   'plymouth_twilight': ['19 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['24 p 4', '29 p 4', '14 t 4']},
  'Thu': {'exeter_twilight': ['42 e 4'],
   'truro_twilight': ['2 t 4'],
   'torquay_twilight': ['58 t 4'],
   'plymouth_twilight': ['27 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['30 e 4', '28 p 4', '9 t 4']},
  'Fri': {'exeter_twilight': ['30 e 4'],
   'truro_twilight': ['12 t 4'],
   'torquay_twilight': ['46 t 4'],
   'plymouth_twilight': ['16 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['43 e 4', '59 t 4', '7 t 4']},
  'Sat': {'exeter_twilight': [],
   'truro_twilight': [],
   'torquay_twilight': [],
   'plymouth_twilight': [],
   'weekend_exeter': ['37 e 4'],
   'weekend_truro': ['3 t 4'],
   'weekend_torquay': ['54 t 4'],
   'weekend_plymouth': ['18 p 4', '20 p 4'],
   'night': ['38 e 4', '52 t 4', '56 t 4']},
  'Sun': {'exeter_twilight': [],
   'truro_twilight': [],
   'torquay_twilight': [],
   'plymouth_twilight': [],
   'weekend_exeter': ['42 e 4'],
   'weekend_truro': ['14 t 4'],
   'weekend_torquay': ['47 t 4'],
   'weekend_plymouth': ['23 p 4', '26 p 4'],
   'night': ['33 e 4', '28 p 4', '56 t 4']}},
 12: {'Mon': {'exeter_twilight': ['40 e 4'],
   'truro_twilight': ['9 t 4'],
   'torquay_twilight': ['49 t 4'],
   'plymouth_twilight': ['28 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['33 e 4', '25 p 4', '6 t 4']},
  'Tue': {'exeter_twilight': ['31 e 4'],
   'truro_twilight': ['12 t 4'],
   'torquay_twilight': ['50 t 4'],
   'plymouth_twilight': ['25 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['33 e 4', '54 t 4', '58 t 4']},
  'Wed': {'exeter_twilight': ['39 e 4'],
   'truro_twilight': ['12 t 4'],
   'torquay_twilight': ['59 t 4'],
   'plymouth_twilight': ['29 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['21 p 4', '1 t 4', '14 t 4']},
  'Thu': {'exeter_twilight': ['40 e 4'],
   'truro_twilight': ['4 t 4'],
   'torquay_twilight': ['46 t 4'],
   'plymouth_twilight': ['17 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['24 p 4', '49 t 4', '51 t 4']},
  'Fri': {'exeter_twilight': ['41 e 4'],
   'truro_twilight': ['6 t 4'],
   'torquay_twilight': ['53 t 4'],
   'plymouth_twilight': ['25 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['20 p 4', '49 t 4', '59 t 4']},
  'Sat': {'exeter_twilight': [],
   'truro_twilight': [],
   'torquay_twilight': [],
   'plymouth_twilight': [],
   'weekend_exeter': ['32 e 4'],
   'weekend_truro': ['11 t 4'],
   'weekend_torquay': ['59 t 4'],
   'weekend_plymouth': ['24 p 4', '28 p 4'],
   'night': ['17 p 4', '51 t 4', '8 t 4']},
  'Sun': {'exeter_twilight': [],
   'truro_twilight': [],
   'torquay_twilight': [],
   'plymouth_twilight': [],
   'weekend_exeter': ['44 e 4'],
   'weekend_truro': ['3 t 4'],
   'weekend_torquay': ['55 t 4'],
   'weekend_plymouth': ['21 p 4', '27 p 4'],
   'night': ['22 p 4', '25 p 4', '8 t 4']}},
 13: {'Mon': {'exeter_twilight': ['43 e 4'],
   'truro_twilight': ['14 t 4'],
   'torquay_twilight': ['55 t 4'],
   'plymouth_twilight': ['22 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['32 e 4', '19 p 4', '58 t 4']},
  'Tue': {'exeter_twilight': ['34 e 4'],
   'truro_twilight': ['6 t 4'],
   'torquay_twilight': ['51 t 4'],
   'plymouth_twilight': ['26 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['31 e 4', '45 t 4', '47 t 4']},
  'Wed': {'exeter_twilight': ['43 e 4'],
   'truro_twilight': ['12 t 4'],
   'torquay_twilight': ['56 t 4'],
   'plymouth_twilight': ['26 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['36 e 4', '42 e 4', '52 t 4']},
  'Thu': {'exeter_twilight': ['32 e 4'],
   'truro_twilight': ['9 t 4'],
   'torquay_twilight': ['49 t 4'],
   'plymouth_twilight': ['29 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['33 e 4', '58 t 4', '7 t 4']},
  'Fri': {'exeter_twilight': ['32 e 4'],
   'truro_twilight': ['10 t 4'],
   'torquay_twilight': ['46 t 4'],
   'plymouth_twilight': ['16 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['38 e 4', '44 e 4', '56 t 4']},
  'Sat': {'exeter_twilight': [],
   'truro_twilight': [],
   'torquay_twilight': [],
   'plymouth_twilight': [],
   'weekend_exeter': ['36 e 4'],
   'weekend_truro': ['5 t 4'],
   'weekend_torquay': ['53 t 4'],
   'weekend_plymouth': ['16 p 4', '28 p 4'],
   'night': ['35 e 4', '44 e 4', '26 p 4']},
  'Sun': {'exeter_twilight': [],
   'truro_twilight': [],
   'torquay_twilight': [],
   'plymouth_twilight': [],
   'weekend_exeter': ['41 e 4'],
   'weekend_truro': ['8 t 4'],
   'weekend_torquay': ['46 t 4'],
   'weekend_plymouth': ['22 p 4', '26 p 4'],
   'night': ['28 p 4', '48 t 4', '49 t 4']}},
 14: {'Mon': {'exeter_twilight': ['32 e 4'],
   'truro_twilight': ['13 t 4'],
   'torquay_twilight': ['53 t 4'],
   'plymouth_twilight': ['18 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['17 p 4', '28 p 4', '48 t 4']},
  'Tue': {'exeter_twilight': ['42 e 4'],
   'truro_twilight': ['14 t 4'],
   'torquay_twilight': ['53 t 4'],
   'plymouth_twilight': ['21 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['20 p 4', '24 p 4', '28 p 4']},
  'Wed': {'exeter_twilight': ['44 e 4'],
   'truro_twilight': ['8 t 4'],
   'torquay_twilight': ['45 t 4'],
   'plymouth_twilight': ['16 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['34 e 4', '53 t 4', '13 t 4']},
  'Thu': {'exeter_twilight': ['41 e 4'],
   'truro_twilight': ['13 t 4'],
   'torquay_twilight': ['53 t 4'],
   'plymouth_twilight': ['15 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['32 e 4', '40 e 4', '20 p 4']},
  'Fri': {'exeter_twilight': ['44 e 4'],
   'truro_twilight': ['11 t 4'],
   'torquay_twilight': ['45 t 4'],
   'plymouth_twilight': ['21 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['41 e 4', '42 e 4', '46 t 4']},
  'Sat': {'exeter_twilight': [],
   'truro_twilight': [],
   'torquay_twilight': [],
   'plymouth_twilight': [],
   'weekend_exeter': ['42 e 4'],
   'weekend_truro': ['7 t 4'],
   'weekend_torquay': ['53 t 4'],
   'weekend_plymouth': ['24 p 4', '26 p 4'],
   'night': ['43 e 4', '19 p 4', '47 t 4']},
  'Sun': {'exeter_twilight': [],
   'truro_twilight': [],
   'torquay_twilight': [],
   'plymouth_twilight': [],
   'weekend_exeter': ['40 e 4'],
   'weekend_truro': ['9 t 4'],
   'weekend_torquay': ['56 t 4'],
   'weekend_plymouth': ['21 p 4', '23 p 4'],
   'night': ['51 t 4', '57 t 4', '3 t 4']}},
 15: {'Mon': {'exeter_twilight': ['37 e 4'],
   'truro_twilight': ['4 t 4'],
   'torquay_twilight': ['50 t 4'],
   'plymouth_twilight': ['21 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['22 p 4', '45 t 4', '9 t 4']},
  'Tue': {'exeter_twilight': ['39 e 4'],
   'truro_twilight': ['7 t 4'],
   'torquay_twilight': ['54 t 4'],
   'plymouth_twilight': ['23 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['30 e 4', '36 e 4', '10 t 4']},
  'Wed': {'exeter_twilight': ['34 e 4'],
   'truro_twilight': ['10 t 4'],
   'torquay_twilight': ['55 t 4'],
   'plymouth_twilight': ['27 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['36 e 4', '48 t 4', '12 t 4']},
  'Thu': {'exeter_twilight': ['37 e 4'],
   'truro_twilight': ['9 t 4'],
   'torquay_twilight': ['52 t 4'],
   'plymouth_twilight': ['15 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['32 e 4', '38 e 4', '13 t 4']},
  'Fri': {'exeter_twilight': ['33 e 4'],
   'truro_twilight': ['2 t 4'],
   'torquay_twilight': ['48 t 4'],
   'plymouth_twilight': ['28 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['43 e 4', '46 t 4', '54 t 4']},
  'Sat': {'exeter_twilight': [],
   'truro_twilight': [],
   'torquay_twilight': [],
   'plymouth_twilight': [],
   'weekend_exeter': ['33 e 4'],
   'weekend_truro': ['12 t 4'],
   'weekend_torquay': ['45 t 4'],
   'weekend_plymouth': ['19 p 4', '28 p 4'],
   'night': ['36 e 4', '39 e 4', '48 t 4']},
  'Sun': {'exeter_twilight': [],
   'truro_twilight': [],
   'torquay_twilight': [],
   'plymouth_twilight': [],
   'weekend_exeter': ['31 e 4'],
   'weekend_truro': ['11 t 4'],
   'weekend_torquay': ['56 t 4'],
   'weekend_plymouth': ['17 p 4', '25 p 4'],
   'night': ['21 p 4', '48 t 4', '54 t 4']}},
 16: {'Mon': {'exeter_twilight': ['44 e 4'],
   'truro_twilight': ['7 t 4'],
   'torquay_twilight': ['55 t 4'],
   'plymouth_twilight': ['27 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['18 p 4', '21 p 4', '10 t 4']},
  'Tue': {'exeter_twilight': ['36 e 4'],
   'truro_twilight': ['12 t 4'],
   'torquay_twilight': ['58 t 4'],
   'plymouth_twilight': ['24 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['22 p 4', '57 t 4', '7 t 4']},
  'Wed': {'exeter_twilight': ['33 e 4'],
   'truro_twilight': ['14 t 4'],
   'torquay_twilight': ['48 t 4'],
   'plymouth_twilight': ['25 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['43 e 4', '57 t 4', '1 t 4']},
  'Thu': {'exeter_twilight': ['34 e 4'],
   'truro_twilight': ['6 t 4'],
   'torquay_twilight': ['56 t 4'],
   'plymouth_twilight': ['23 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['41 e 4', '20 p 4', '10 t 4']},
  'Fri': {'exeter_twilight': ['37 e 4'],
   'truro_twilight': ['5 t 4'],
   'torquay_twilight': ['49 t 4'],
   'plymouth_twilight': ['19 p 4'],
   'weekend_exeter': [],
   'weekend_truro': [],
   'weekend_torquay': [],
   'weekend_plymouth': [],
   'night': ['42 e 4', '43 e 4', '10 t 4']},
  'Sat': {'exeter_twilight': [],
   'truro_twilight': [],
   'torquay_twilight': [],
   'plymouth_twilight': [],
   'weekend_exeter': ['40 e 4'],
   'weekend_truro': ['10 t 4'],
   'weekend_torquay': ['46 t 4'],
   'weekend_plymouth': ['19 p 4', '25 p 4'],
   'night': ['31 e 4', '5 t 4', '6 t 4']},
  'Sun': {'exeter_twilight': [],
   'truro_twilight': [],
   'torquay_twilight': [],
   'plymouth_twilight': [],
   'weekend_exeter': ['37 e 4'],
   'weekend_truro': ['10 t 4'],
   'weekend_torquay': ['57 t 4'],
   'weekend_plymouth': ['27 p 4', '29 p 4'],
   'night': ['19 p 4', '28 p 4', '5 t 4']}}}
In [102]:
#worker_night_block
In [103]:
#[[worker, week, day, shift] for day in days for shift in ["night"]]
In [104]:
1 if 4 in [4, 1, 0] else 0
Out [104]:
1
In [105]:
(4 + 1) // 7
Out [105]:
0
In [106]:
#for worker in workers:
#        for shift in shifts:
#            a = [model.works[worker.id, week, day, shift].value for week in weeks for day in days]
In [107]:
a
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-107-3f786850e387> in <module>
----> 1 a

NameError: name 'a' is not defined
In [108]:
0 % 7
Out [108]:
0
In [109]:
-1 % 7
Out [109]:
6
In [110]:
model.shift_count[(5, "night")].value
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-110-4d45c109f633> in <module>
----> 1 model.shift_count[(5, "night")].value

NameError: name 'model' is not defined
In [111]:
[w.id for w in workers]
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-111-2f761ca0465d> in <module>
----> 1 [w.id for w in workers]

NameError: name 'workers' is not defined
In [ ]: