Last active
May 11, 2022 18:07
-
-
Save veryeli/f42cfb8a4d42b7a5258025d985d31ffe to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
NUM_SHIFTS_PER_DAY_OF_WEEK = { | |
0: 5, | |
1: 4, | |
2: 7, | |
3: 5, | |
4: 4, | |
} | |
SHIFTNAME = { | |
0: 'LOW 1', | |
1: 'LOW 2', | |
2: 'MED', | |
3: 'CAP', | |
4: 'OUT', | |
5: 'LOW 3', | |
6: 'OUT2' | |
} | |
CAP_SHIFT = 3 | |
MED_SHIFT = 2 | |
OUT_SHIFT = 4 | |
ATTORNEYS = { | |
# censored for privacy | |
} | |
# These attorneys could solo a homicide, possibly with supervision | |
CAP_STAFF = { | |
# censored for privacy | |
} | |
# these attorneys could solo a carjacking, possibly with supervision | |
MID_STAFF = { | |
# censored for privacy | |
} | |
LOW_STAFF = { | |
# censored for privacy | |
} | |
MODIFIED_MAX_SHIFTS = { | |
# censored for privacy | |
} | |
MAX_SHIFTS = 6 | |
MAX_SHIFTS_PER_ATTY = {} | |
for anum, aname in ATTORNEYS.items(): | |
MAX_SHIFTS_PER_ATTY[anum] = MODIFIED_MAX_SHIFTS.get(aname, 4) | |
# 0: Sunday, 6: Saturday, etc | |
DAY_OF_FIRST_OF_MONTH = 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Wednesday, Jun 1, 2022 | |
LOW 1: Richards, Nancy | |
LOW 2: O'Donnell, James | |
MED: Noakes, William | |
CAP: Riggins, Ken | |
OUT: Bickerdt, Tyrone | |
LOW 3: Washington, Earl | |
Thursday, Jun 2, 2022 | |
LOW 1: Riggins, Ken | |
LOW 2: Nelson, Sophia | |
MED: Pilcowitz, Jackie | |
CAP: Noakes, William | |
OUT: Elijah, Emily | |
Friday, Jun 3, 2022 | |
LOW 1: Elijah, Emily | |
LOW 2: Riggins, Ken | |
MED: Anderson, Lauren | |
CAP: Basen-Michon, Kim | |
Monday, Jun 6, 2022 | |
LOW 1: Knoche, Jeffrey | |
LOW 2: Burton-Harris, Robert | |
MED: Longstreet, Kristine | |
CAP: Richards, Nancy | |
OUT: O'Donnell, James | |
Tuesday, Jun 7, 2022 | |
LOW 1: De Mott Grady, Cait | |
LOW 2: Nelson, Sophia | |
MED: Kearney, Blase | |
CAP: Longstreet, Kristine | |
Wednesday, Jun 8, 2022 | |
LOW 1: Parker, James | |
LOW 2: De Mott Grady, Cait | |
MED: Gagniuk, Brian | |
CAP: Procida, Mark | |
OUT: Burton-Harris, Robert | |
LOW 3: New, Emily | |
Thursday, Jun 9, 2022 | |
LOW 1: Bickerdt, Tyrone | |
LOW 2: Baron, Hannah | |
MED: Sullivan, Andrew | |
CAP: Young, Elizabeth | |
OUT: Anderson, Lauren | |
Friday, Jun 10, 2022 | |
LOW 1: New, Emily | |
LOW 2: Gagniuk, Brian | |
MED: Pilcowitz, Jackie | |
CAP: Robinson, Brandy | |
Monday, Jun 13, 2022 | |
LOW 1: Parker, James | |
LOW 2: De Mott Grady, Cait | |
MED: Kastaw, Nanci | |
CAP: Quick, Cheryl | |
OUT: Oh, Glen | |
Tuesday, Jun 14, 2022 | |
LOW 1: Kearney, Blase | |
LOW 2: Kirkland, Keshava A | |
MED: Sullivan, Andrew | |
CAP: Knoche, Jeffrey | |
Wednesday, Jun 15, 2022 | |
LOW 1: O'Donnell, James | |
LOW 2: Nelson, Sophia | |
MED: Anderson, Lauren | |
CAP: Kearney, Blase | |
OUT: Phelps, Natalie | |
LOW 3: Kastaw, Nanci | |
Thursday, Jun 16, 2022 | |
LOW 1: Knoche, Jeffrey | |
LOW 2: Elijah, Emily | |
MED: Burton-Harris, Robert | |
CAP: Bickerdt, Tyrone | |
OUT: Baron, Hannah | |
Friday, Jun 17, 2022 | |
LOW 1: Pilcowitz, Jackie | |
LOW 2: Parker, James | |
MED: Knoche, Jeffrey | |
CAP: O'Donnell, James | |
Monday, Jun 20, 2022 | |
LOW 1: Kastaw, Nanci | |
LOW 2: Washington, Earl | |
MED: New, Emily | |
CAP: Procida, Mark | |
OUT: Anderson, Lauren | |
Tuesday, Jun 21, 2022 | |
LOW 1: Oh, Glen | |
LOW 2: Dial, Dezha | |
MED: De Mott Grady, Cait | |
CAP: Washington, Earl | |
Wednesday, Jun 22, 2022 | |
LOW 1: Oh, Glen | |
LOW 2: Riggins, Ken | |
MED: Baron, Hannah | |
CAP: Young, Elizabeth | |
OUT: Phelps, Natalie | |
LOW 3: Dial, Dezha | |
Thursday, Jun 23, 2022 | |
LOW 1: Dial, Dezha | |
LOW 2: Kirkland, Keshava A | |
MED: Elijah, Emily | |
CAP: Basen-Michon, Kim | |
OUT: Baron, Hannah | |
Friday, Jun 24, 2022 | |
LOW 1: Noakes, William | |
LOW 2: Pilcowitz, Jackie | |
MED: Sullivan, Andrew | |
CAP: Parker, James | |
Monday, Jun 27, 2022 | |
LOW 1: Bickerdt, Tyrone | |
LOW 2: Epps, Roeiah | |
MED: Sullivan, Andrew | |
CAP: Gagniuk, Brian | |
OUT: Young, Elizabeth | |
Tuesday, Jun 28, 2022 | |
LOW 1: Kirkland, Keshava A | |
LOW 2: Epps, Roeiah | |
MED: Phelps, Natalie | |
CAP: Noakes, William | |
Wednesday, Jun 29, 2022 | |
LOW 1: Epps, Roeiah | |
LOW 2: Oh, Glen | |
MED: Robinson, Brandy | |
CAP: Richards, Nancy | |
OUT: Quick, Cheryl | |
LOW 3: Nelson, Sophia | |
Thursday, Jun 30, 2022 | |
LOW 1: Kastaw, Nanci | |
LOW 2: Washington, Earl | |
MED: New, Emily | |
CAP: Gagniuk, Brian | |
OUT: Burton-Harris, Robert | |
Procida, Mark | |
CAP Wednesday, Jun 8, 2022 | |
CAP Monday, Jun 20, 2022 | |
Longstreet, Kristine | |
MED Monday, Jun 6, 2022 | |
CAP Tuesday, Jun 7, 2022 | |
Robinson, Brandy | |
CAP Friday, Jun 10, 2022 | |
MED Wednesday, Jun 29, 2022 | |
Quick, Cheryl | |
CAP Monday, Jun 13, 2022 | |
OUT Wednesday, Jun 29, 2022 | |
Kearney, Blase | |
MED Tuesday, Jun 7, 2022 | |
LOW 1 Tuesday, Jun 14, 2022 | |
CAP Wednesday, Jun 15, 2022 | |
Richards, Nancy | |
LOW 1 Wednesday, Jun 1, 2022 | |
CAP Monday, Jun 6, 2022 | |
CAP Wednesday, Jun 29, 2022 | |
O'Donnell, James | |
LOW 2 Wednesday, Jun 1, 2022 | |
OUT Monday, Jun 6, 2022 | |
LOW 1 Wednesday, Jun 15, 2022 | |
CAP Friday, Jun 17, 2022 | |
Kastaw, Nanci | |
MED Monday, Jun 13, 2022 | |
LOW 3 Wednesday, Jun 15, 2022 | |
LOW 1 Monday, Jun 20, 2022 | |
LOW 1 Thursday, Jun 30, 2022 | |
Oh, Glen | |
OUT Monday, Jun 13, 2022 | |
LOW 1 Tuesday, Jun 21, 2022 | |
LOW 1 Wednesday, Jun 22, 2022 | |
LOW 2 Wednesday, Jun 29, 2022 | |
Riggins, Ken | |
CAP Wednesday, Jun 1, 2022 | |
LOW 1 Thursday, Jun 2, 2022 | |
LOW 2 Friday, Jun 3, 2022 | |
LOW 2 Wednesday, Jun 22, 2022 | |
Parker, James | |
LOW 1 Wednesday, Jun 8, 2022 | |
LOW 1 Monday, Jun 13, 2022 | |
LOW 2 Friday, Jun 17, 2022 | |
CAP Friday, Jun 24, 2022 | |
Basen-Michon, Kim | |
CAP Friday, Jun 3, 2022 | |
CAP Thursday, Jun 23, 2022 | |
Gagniuk, Brian | |
MED Wednesday, Jun 8, 2022 | |
LOW 2 Friday, Jun 10, 2022 | |
CAP Monday, Jun 27, 2022 | |
CAP Thursday, Jun 30, 2022 | |
Knoche, Jeffrey | |
LOW 1 Monday, Jun 6, 2022 | |
CAP Tuesday, Jun 14, 2022 | |
LOW 1 Thursday, Jun 16, 2022 | |
MED Friday, Jun 17, 2022 | |
Noakes, William | |
MED Wednesday, Jun 1, 2022 | |
CAP Thursday, Jun 2, 2022 | |
LOW 1 Friday, Jun 24, 2022 | |
CAP Tuesday, Jun 28, 2022 | |
Pilcowitz, Jackie | |
MED Thursday, Jun 2, 2022 | |
MED Friday, Jun 10, 2022 | |
LOW 1 Friday, Jun 17, 2022 | |
LOW 2 Friday, Jun 24, 2022 | |
Burton-Harris, Robert | |
LOW 2 Monday, Jun 6, 2022 | |
OUT Wednesday, Jun 8, 2022 | |
MED Thursday, Jun 16, 2022 | |
OUT Thursday, Jun 30, 2022 | |
De Mott Grady, Cait | |
LOW 1 Tuesday, Jun 7, 2022 | |
LOW 2 Wednesday, Jun 8, 2022 | |
LOW 2 Monday, Jun 13, 2022 | |
MED Tuesday, Jun 21, 2022 | |
Washington, Earl | |
LOW 3 Wednesday, Jun 1, 2022 | |
LOW 2 Monday, Jun 20, 2022 | |
CAP Tuesday, Jun 21, 2022 | |
LOW 2 Thursday, Jun 30, 2022 | |
Young, Elizabeth | |
CAP Thursday, Jun 9, 2022 | |
CAP Wednesday, Jun 22, 2022 | |
OUT Monday, Jun 27, 2022 | |
New, Emily | |
LOW 3 Wednesday, Jun 8, 2022 | |
LOW 1 Friday, Jun 10, 2022 | |
MED Monday, Jun 20, 2022 | |
MED Thursday, Jun 30, 2022 | |
Anderson, Lauren | |
MED Friday, Jun 3, 2022 | |
OUT Thursday, Jun 9, 2022 | |
MED Wednesday, Jun 15, 2022 | |
OUT Monday, Jun 20, 2022 | |
Phelps, Natalie | |
OUT Wednesday, Jun 15, 2022 | |
OUT Wednesday, Jun 22, 2022 | |
MED Tuesday, Jun 28, 2022 | |
Baron, Hannah | |
LOW 2 Thursday, Jun 9, 2022 | |
OUT Thursday, Jun 16, 2022 | |
MED Wednesday, Jun 22, 2022 | |
OUT Thursday, Jun 23, 2022 | |
Bickerdt, Tyrone | |
OUT Wednesday, Jun 1, 2022 | |
LOW 1 Thursday, Jun 9, 2022 | |
CAP Thursday, Jun 16, 2022 | |
LOW 1 Monday, Jun 27, 2022 | |
Elijah, Emily | |
OUT Thursday, Jun 2, 2022 | |
LOW 1 Friday, Jun 3, 2022 | |
LOW 2 Thursday, Jun 16, 2022 | |
MED Thursday, Jun 23, 2022 | |
Dial, Dezha | |
LOW 2 Tuesday, Jun 21, 2022 | |
LOW 3 Wednesday, Jun 22, 2022 | |
LOW 1 Thursday, Jun 23, 2022 | |
Sullivan, Andrew | |
MED Thursday, Jun 9, 2022 | |
MED Tuesday, Jun 14, 2022 | |
MED Friday, Jun 24, 2022 | |
MED Monday, Jun 27, 2022 | |
Kirkland, Keshava A | |
LOW 2 Tuesday, Jun 14, 2022 | |
LOW 2 Thursday, Jun 23, 2022 | |
LOW 1 Tuesday, Jun 28, 2022 | |
Epps, Roeiah | |
LOW 2 Monday, Jun 27, 2022 | |
LOW 2 Tuesday, Jun 28, 2022 | |
LOW 1 Wednesday, Jun 29, 2022 | |
Nelson, Sophia | |
LOW 2 Thursday, Jun 2, 2022 | |
LOW 2 Tuesday, Jun 7, 2022 | |
LOW 2 Wednesday, Jun 15, 2022 | |
LOW 3 Wednesday, Jun 29, 2022 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from datetime import date | |
from pulp import * | |
from consts import NUM_SHIFTS_PER_DAY_OF_WEEK, SHIFTNAME, CAP_STAFF | |
from consts import MID_STAFF, LOW_STAFF, MAX_SHIFTS, MODIFIED_MAX_SHIFTS | |
from consts import MAX_SHIFTS_PER_ATTY, DAY_OF_FIRST_OF_MONTH, ATTORNEYS | |
from consts import CAP_SHIFT, MED_SHIFT, OUT_SHIFT | |
from utils import day_shifts | |
def get_choices(month, year=2022): | |
shifts_per_day = day_shifts(month, year) | |
choices = LpVariable.dicts("Choice", (ATTORNEYS, shifts_per_day, range(MAX_SHIFTS)), cat="Binary") | |
return choices | |
def schedule_badness(choices, attorney, shifts_per_day): | |
badness = 0 | |
out1_shifts = lpSum(choices[attorney][day][0] for day in shifts_per_day) | |
out2_shifts = lpSum(choices[attorney][day][1] for day in shifts_per_day) | |
return min(out1_shifts, out2_shifts) | |
def set_up_problem(choices, month, year=2022): | |
prob = LpProblem("Scheduling Problem") | |
shifts_per_day = day_shifts(month, year) | |
choices = LpVariable.dicts("Choice", (ATTORNEYS, shifts_per_day, range(MAX_SHIFTS)), cat="Binary") | |
# one attorney per shift | |
for day, num_shifts in shifts_per_day.items(): | |
for shift in range(num_shifts): | |
prob += lpSum([choices[attorney][day][shift]for attorney in ATTORNEYS]) == 1 | |
# no one is scheduled where there's no shifts | |
for attorney in ATTORNEYS: | |
for day, num_shifts in shifts_per_day.items(): | |
for shift in range(num_shifts, MAX_SHIFTS): | |
prob += choices[attorney][day][shift] == 0 | |
# max shifts per month per attorney | |
for attorney in ATTORNEYS: | |
prob += lpSum([choices[attorney][day][shift] | |
for day in shifts_per_day | |
for shift in range(shifts_per_day[day])]) <= MAX_SHIFTS_PER_ATTY[attorney] | |
# min shifts per month per attorney (max - 1) | |
for attorney in ATTORNEYS: | |
prob += lpSum([choices[attorney][day][shift] | |
for day in shifts_per_day | |
for shift in range(shifts_per_day[day])]) >= (MAX_SHIFTS_PER_ATTY[attorney] - 1) | |
# max one shift per atty per day | |
for attorney in ATTORNEYS: | |
for day, num_shifts in shifts_per_day.items(): | |
prob += lpSum([choices[attorney][day][shift] | |
for shift in range(num_shifts)]) <= 1 | |
# only schedule people to the correct level of shift | |
for attorney in LOW_STAFF: | |
for day in shifts_per_day: | |
for shift in [2, 3, 4]: | |
prob += choices[attorney][day][shift] == 0 | |
for attorney in MID_STAFF: | |
for day in shifts_per_day: | |
for shift in [3]: | |
prob += choices[attorney][day][shift] == 0 | |
# all cap attorneys get a cap shift | |
for attorney in CAP_STAFF: | |
prob += lpSum([choices[attorney][day][shift] | |
for day in shifts_per_day | |
for shift in [CAP_SHIFT]]) >= 1 | |
# all med attorneys get a med+ shift | |
for attorney in {**CAP_STAFF, **MID_STAFF}: | |
prob += lpSum([choices[attorney][day][shift] | |
for day in shifts_per_day | |
for shift in [CAP_SHIFT, MED_SHIFT, OUT_SHIFT]]) >= 1 | |
# The problem data is written to an .lp file | |
prob.writeLP("Schedule.lp") | |
prob.solve() | |
print_results(choices, month, year) | |
return prob | |
def print_results(choices, month, year): | |
shifts_per_day = day_shifts(month, year) | |
for day, num_shifts in shifts_per_day.items(): | |
if num_shifts == 0: | |
continue | |
print (date(year, month, day).strftime("%A, %b %-d, %Y")) | |
for shift in range(num_shifts): | |
print ('\t', SHIFTNAME[shift], end=': ') | |
for a, name in ATTORNEYS.items(): | |
if value(choices[a][day][shift]) == 1: | |
print ('\t\t', name) | |
print() | |
for a, name in ATTORNEYS.items(): | |
print(name) | |
for day, num_shifts in shifts_per_day.items(): | |
if num_shifts == 0: | |
continue | |
for shift in range(num_shifts): | |
if value(choices[a][day][shift]) == 1: | |
print (SHIFTNAME[shift], date(year, month, day).strftime("%A, %b %-d, %Y")) | |
print() | |
if __name__ == '__main__': | |
choices = get_choices(2) | |
prob = set_up_problem(2) | |
# The problem is solved using PuLP's choice of Solver | |
prob.solve() | |
for d in shifts_per_day: | |
if SHIFTS_PER_DAY[d] == 0: | |
continue | |
print ('DAY: %d' % d) | |
for shift in range(MAX_SHIFTS): | |
if shift >= SHIFTS_PER_DAY[d]: | |
continue | |
print ('\t', SHIFTNAME[shift]) | |
for a in ATTORNEYS: | |
if value(choices[a][d][shift]) == 1: | |
print ('\t\t', STAFF[a]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from calendar import monthrange | |
from consts import NUM_SHIFTS_PER_DAY_OF_WEEK | |
def day_shifts(month, year=2022): | |
day_of_first_of_month, days_in_month = monthrange(year, month) | |
days = range(1, days_in_month + 1) | |
shifts_per_day = {} | |
for day in days: | |
shifts_per_day[day] = NUM_SHIFTS_PER_DAY_OF_WEEK.get((day + day_of_first_of_month - 1) % 7, 0) | |
return shifts_per_day |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment