{ "nbformat": 4, "nbformat_minor": 2, "metadata": { "language_info": { "name": "python", "codemirror_mode": { "name": "ipython", "version": 3 }, "version": "3.7.5-final" }, "orig_nbformat": 2, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "npconvert_exporter": "python", "pygments_lexer": "ipython3", "version": 3, "kernelspec": { "name": "python37564bit5ba0d1a6f89f489d9799b0f9b976a56e", "display_name": "Python 3.7.5 64-bit" } }, "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from pyomo.environ import *\n", "from pyomo.opt import SolverFactory\n", "\n", "import datetime\n", "import itertools" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "night_balance_offset = 2\n", "twighlight_balance_offset = 1\n", "weeks_to_rota = 26" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# Import trainee data\n", "#import csv\n", "#with open('trainees.csv', newline='') as f:\n", "# lines = csv.reader(f, delimiter=',', quotechar='|')\n", "# for row in lines[1:]:\n", "# name, site, grade, fte, end_date, nwds = line.split(\",\")" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "# Define days (1 week)\n", "days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']\n", "\n", "weeks = [i for i in range(1,weeks_to_rota+1)]\n", "\n", "weeks_days_product = list(itertools.product(weeks, days))\n", "\n", "sites = (\"truro\", \"exeter\", \"torbay\", \"barnstaple\", \"plymouth\")\n", "\n", "class shiftsClass:\n", " def __init__(self, sites, shift, length, days, balance_by_site=True)\n", " self.site = sites\n", " self.shift = shift\n", " self.length = length\n", " self.days = days\n", "\n", " def GetShift(self):\n", " return self.shift\n", "\n", "shifts = []\n", "shifts.append(\n", " shiftsClass((\"truro\"), \"truro_twighlight\", 12.5, days[:5]),\n", " shiftsClass((\"plymouth\"), \"plymouth_twighlight\", 12.5, days[:5]), \n", " shiftsClass((sites), \"night\", 12.5, days, balance_by_site=False), \n", ")\n", "\n", "# Enter shifts of each day\n", "#shifts = ['morning', 'evening', 'night', 'truro_twighlight'] # 3 shifts of 8 hours\n", "#shifts = ['night', 'truro_twighlight', 'plymouth_twighlight'] # 3 shifts of 8 hours\n", "\n", "# Generate\n", "\n", "#site_and_shifts_to_balance = ((\"truro\", \"truro_twighlight\"),\n", "# (\"plymouth\", \"plymouth_twighlight\"))\n", "\n", "#shift_len = [12.5, 12.5, 12.5]\n", "\n", "night_blocks = [\"weekday\", \"weekend\", \"none\"]\n", "\n", "shift_lengths = dict(zip(shifts, shift_len))\n", "\n", "days_shifts = {day: shifts for day in days} # dict with day as key and list of its shifts as value\n", "\n", "start_date = datetime.date(2020,9,7)\n", "rota_days_length = len(weeks) * 7\n", "rota_end_date = start_date + datetime.timedelta(rota_days_length)\n", "\n", "unavailable_to_work = set()\n", "\n", "class workerClass:\n", " def __init__(self, id, name, site, grade, fte=100, nwd=\"\", end_date=None, oop=None):\n", " self.id = id\n", " self.name = name\n", " self.site = site\n", " self.grade = grade\n", " self.fte = fte\n", " self.nwd = nwd\n", " self.proportion_rota_to_work = 1\n", "\n", " days_to_work = rota_days_length\n", "\n", " if end_date is None:\n", " self.end_date = None\n", " else:\n", " y, m, d = [int(i) for i in end_date.split(\"/\")]\n", " self.end_date = datetime.date(y, m, d)\n", "\n", " if self.end_date > rota_end_date:\n", " self.end_date = rota_end_date\n", " else:\n", " days_to_work = (self.end_date - start_date).days\n", "\n", " # add unavalabilities\n", " for weeks_days in weeks_days_product[days_to_work:]:\n", " week, day = weeks_days\n", " unavailable_to_work.add((self.id, week, day))\n", " \n", " if oop is not None:\n", " start_oop, end_oop = oop\n", " start_oop_date = datetime.datetime.strptime(start_oop, '%Y/%m/%d').date()\n", " end_oop_date = datetime.datetime.strptime(end_oop, '%Y/%m/%d').date()\n", "\n", " if end_oop_date > rota_end_date:\n", " end_oop_date = rota_end_date\n", " \n", " oop_length = (end_oop_date - start_oop_date).days\n", " days_to_work = days_to_work - oop_length\n", "\n", " days_until_oop = (start_oop_date - start_date).days\n", "\n", " for weeks_days in weeks_days_product[days_until_oop:days_until_oop+oop_length]:\n", " week, day = weeks_days\n", " unavailable_to_work.add((self.id, week, day))\n", "\n", " \n", " self.proportion_rota_to_work = days_to_work / rota_days_length\n", "\n", " # We had to adjust the full time equivalent for people who CCT / leave the rota early\n", " self.fte_adj = self.fte * self.proportion_rota_to_work\n", " \n", " def GetDetails(self):\n", " return \"{} {} {}\".format(self.id, self.site[0], self.grade)\n", "\n", "# Enter workers ids (name, number, ...)\n", "workers = [workerClass(i, \"Test Person {}\".format(i), \"truro\", 4) for i in range(1, 20)] \n", "workers.extend([workerClass(i, \"Test Person {}\".format(i), \"plymouth\", 4) for i in range(21, 58)])\n", "#workers.extend([workerClass(i, \"Test Person {}\".format(i), 4, 200, \"\") for i in range(48, 49)])\n", "\n", "\n", "#unavailable_to_work.update([(\"W1\", 1, \"Mon\"),(\"W1\", 1, \"Fri\"),(\"W1\", 1, \"Thu\"),(\"W3\", 1, \"Mon\"),(\"W2\", 1, \"Mon\"),(\"W2\", 1, \"Tue\"),(\"W2\", 1, \"Wed\"),(\"W6\", 1, \"Thu\"),(\"W5\", 1, \"Fri\"),(\"W9\", 1, \"Fri\"),(\"W14\", 1, \"Fri\"),(\"W21\", 1, \"Fri\")])\n", "\n", "full_time_equivalent = sum([w.fte_adj for w in workers])\n", "full_time_equivalent_sites = {}\n", "\n", "for site in [\"truro\", \"plymouth\"]:\n", " full_time_equivalent_sites[site] = sum([w.fte_adj for w in workers if w.site == site])\n", "\n", "\n", "#for i in range(11,30):" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "{'truro': 1900.0, 'plymouth': 3700.0}" }, "metadata": {}, "execution_count": 5 } ], "source": [ "weeks_days_product = list(itertools.product(weeks, days))\n", "full_time_equivalent_sites" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "tags": [ "outputPrepend", "outputPrepend", "outputPrepend", "outputPrepend", "outputPrepend", "outputPrepend", "outputPrepend", "outputPrepend", "outputPrepend" ] }, "outputs": [], "source": [ "# Initialize model\n", "model = ConcreteModel()\n", "\n", "# binary variables representing if a worker is scheduled somewhere\n", "model.works = Var(((worker.id, week, day, shift) for worker in workers for week in weeks for day in days for shift.GetShift() in days_shifts[day]),\n", " within=Binary, initialize=0)\n", "\n", "model.nights = Var(((worker.id, week, block) for worker in workers for week in weeks for block in night_blocks),\n", " within=Binary, initialize=0)\n", "\n", "#model.unavailable = Var(((worker, week, day) for worker in workers for week in weeks for day in days),\n", "# within=Binary, initialize=0)\n", "def availability_init(model, wid, week, day):\n", " if (wid, week, day) in unavailable_to_work:\n", " print((wid, week, day))\n", " return 0\n", " return 1\n", "model.available = Param(((worker.id, week, day) for worker in workers for week in weeks for day in days), initialize=availability_init) \n", "\n", "# binary variables representing if a worker is necessary\n", "model.needed = Var([worker.id for worker in workers], within=Binary, initialize=0)\n", "\n", "# binary variables representing if a worker worked on sunday but not on saturday (avoid if possible)\n", "model.no_pref = Var([worker.id for worker in workers], within=Binary, initialize=0)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "# Define an objective function with model as input, to pass later\n", "def obj_rule(m):\n", " c = len(workers)\n", " return sum(m.no_pref[worker.id] for worker in workers)\n", " #return sum(m.no_pref[worker] for worker in workers) + sum(c * m.needed[worker] for worker in workers)\n", "# we multiply the second term by a constant to make sure that it is the primary objective\n", "# since sum(m.no_prefer) is at most len(workers), len(workers) + 1 is a valid constant.\n", "\n", "\n", "# add objective function to the model. rule (pass function) or expr (pass expression directly)\n", "model.obj = Objective(rule=obj_rule, sense=minimize)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "[(1, 100.0, ('truro', 'truro_twighlight')),\n (2, 100.0, ('truro', 'truro_twighlight')),\n (3, 100.0, ('truro', 'truro_twighlight')),\n (4, 100.0, ('truro', 'truro_twighlight')),\n (5, 100.0, ('truro', 'truro_twighlight')),\n (6, 100.0, ('truro', 'truro_twighlight')),\n (7, 100.0, ('truro', 'truro_twighlight')),\n (8, 100.0, ('truro', 'truro_twighlight')),\n (9, 100.0, ('truro', 'truro_twighlight')),\n (10, 100.0, ('truro', 'truro_twighlight')),\n (11, 100.0, ('truro', 'truro_twighlight')),\n (12, 100.0, ('truro', 'truro_twighlight')),\n (13, 100.0, ('truro', 'truro_twighlight')),\n (14, 100.0, ('truro', 'truro_twighlight')),\n (15, 100.0, ('truro', 'truro_twighlight')),\n (16, 100.0, ('truro', 'truro_twighlight')),\n (17, 100.0, ('truro', 'truro_twighlight')),\n (18, 100.0, ('truro', 'truro_twighlight')),\n (19, 100.0, ('truro', 'truro_twighlight')),\n (21, 100.0, ('plymouth', 'plymouth_twighlight')),\n (22, 100.0, ('plymouth', 'plymouth_twighlight')),\n (23, 100.0, ('plymouth', 'plymouth_twighlight')),\n (24, 100.0, ('plymouth', 'plymouth_twighlight')),\n (25, 100.0, ('plymouth', 'plymouth_twighlight')),\n (26, 100.0, ('plymouth', 'plymouth_twighlight')),\n (27, 100.0, ('plymouth', 'plymouth_twighlight')),\n (28, 100.0, ('plymouth', 'plymouth_twighlight')),\n (29, 100.0, ('plymouth', 'plymouth_twighlight')),\n (30, 100.0, ('plymouth', 'plymouth_twighlight')),\n (31, 100.0, ('plymouth', 'plymouth_twighlight')),\n (32, 100.0, ('plymouth', 'plymouth_twighlight')),\n (33, 100.0, ('plymouth', 'plymouth_twighlight')),\n (34, 100.0, ('plymouth', 'plymouth_twighlight')),\n (35, 100.0, ('plymouth', 'plymouth_twighlight')),\n (36, 100.0, ('plymouth', 'plymouth_twighlight')),\n (37, 100.0, ('plymouth', 'plymouth_twighlight')),\n (38, 100.0, ('plymouth', 'plymouth_twighlight')),\n (39, 100.0, ('plymouth', 'plymouth_twighlight')),\n (40, 100.0, ('plymouth', 'plymouth_twighlight')),\n (41, 100.0, ('plymouth', 'plymouth_twighlight')),\n (42, 100.0, ('plymouth', 'plymouth_twighlight')),\n (43, 100.0, ('plymouth', 'plymouth_twighlight')),\n (44, 100.0, ('plymouth', 'plymouth_twighlight')),\n (45, 100.0, ('plymouth', 'plymouth_twighlight')),\n (46, 100.0, ('plymouth', 'plymouth_twighlight')),\n (47, 100.0, ('plymouth', 'plymouth_twighlight')),\n (48, 100.0, ('plymouth', 'plymouth_twighlight')),\n (49, 100.0, ('plymouth', 'plymouth_twighlight')),\n (50, 100.0, ('plymouth', 'plymouth_twighlight')),\n (51, 100.0, ('plymouth', 'plymouth_twighlight')),\n (52, 100.0, ('plymouth', 'plymouth_twighlight')),\n (53, 100.0, ('plymouth', 'plymouth_twighlight')),\n (54, 100.0, ('plymouth', 'plymouth_twighlight')),\n (55, 100.0, ('plymouth', 'plymouth_twighlight')),\n (56, 100.0, ('plymouth', 'plymouth_twighlight')),\n (57, 100.0, ('plymouth', 'plymouth_twighlight'))]" }, "metadata": {}, "execution_count": 8 } ], "source": [ "#[(worker.id, worker.fte_adj, ss) for ss in site_and_shifts for worker in workers if worker.site == ss[0]]" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "model.constraints = ConstraintList() # Create a set of constraints\n", "\n", "# Constraint: all shifts are assigned\n", "for week in weeks:\n", " for day in days:\n", " for shiftClass in days_shifts[day]:\n", " shift = shiftClass.GetShift()\n", " # 3 people per night\n", " if shift in ['night']:\n", " model.constraints.add( # to add a constraint to model.constraints set\n", " 3 == sum(model.works[worker.id, week, day, shift] for worker in workers)\n", " )\n", " elif day in days[:5] and shift in ['truro_twighlight']: \n", " model.constraints.add( # to add a constraint to model.constraints set\n", " 1 == sum(model.works[worker.id, week, day, shift] for worker in workers if worker.site == \"truro\")\n", " )\n", " elif day in days[:5] and shift in ['plymouth_twighlight']: \n", " model.constraints.add( # to add a constraint to model.constraints set\n", " 1 == sum(model.works[worker.id, week, day, shift] for worker in workers if worker.site == \"plymouth\")\n", " ) \n", " # make sure twighlights are not assigned on a weekend\n", " elif day in days[5:] and shift in ['truro_twighlight', 'plymouth_twighlight']:\n", " model.constraints.add( # to add a constraint to model.constraints set\n", " 0 == sum(model.works[worker.id, week, day, shift] for worker in workers)\n", " )\n", " \n", " #elif day in days[:-1] and shift in ['truro_twighlight', 'plymouth_twighlight']:\n", " # # weekdays' and Saturdays' day shifts have exactly two workers\n", " # model.constraints.add( # to add a constraint to model.constraints set\n", " # 1 == sum(model.works[worker.id, week, day, shift] for worker in workers)\n", " # )\n", " #else:\n", " # # Sunday shifts have exactly one worker\n", " # model.constraints.add(\n", " # 1 == sum(model.works[worker.id, week, day, shift] for worker in workers)\n", " # )\n", "\n", "# Constraint: total hours worked hours worked\n", "#for worker in workers:\n", "# model.constraints.add(\n", "# 1200 >= sum(shift_lengths[shift] * model.works[worker, week, day, shift] for week in weeks for day in days for shift in days_shifts[day])\n", "# )\n", "\n", "#for week in weeks:\n", "# for worker in workers:\n", "# model.constraints.add(\n", "# 48 >= sum(shift_lengths[shift] * model.works[worker, week, day, shift] for day in days for shift in days_shifts[day])\n", "# )\n", "\n", "def maxHoursPerWeekRule(model, wid, week):\n", " return(72 >= sum(shift_lengths[shift] * model.works[wid, week, day, shift.GetShift()]for day in days for shift in days_shifts[day]))\n", "model.max_hours_per_week_constraint = Constraint([worker.id for worker in workers], weeks, rule=maxHoursPerWeekRule)\n", "\n", "def maxHoursRule(model, wid):\n", " return(8000 >= sum(shift_lengths[shift] * model.works[wid, week, day, shift.GetShift()] for week in weeks for day in days for shift in days_shifts[day]))\n", "model.max_hours_constraint = Constraint([worker.id for worker in workers], rule=maxHoursRule)\n", "\n", "# Shifts should not be allocated on nwds (excepting night shifts)\n", "def nwdRule(model, wid, nwds):\n", " if nwds != \"\":\n", " return(1 >= sum(model.works[wid, week, day, shift.GetShift()] for week in weeks for day in nwds.split(\",\") for shift in [\"truro_twighlight\",\"plymouth_twighlight\"]) + 1)\n", " return Constraint.Skip\n", "model.nwd_constraint = Constraint([(worker.id, worker.nwd) for worker in workers], rule=nwdRule)\n", "\n", "# set a rule to even night shifts (could also be done in blocks, which may be quicker?)\n", "# takes into accound both LTFT, OOP and leaving the training scheme\n", "total_night_shifts = len(weeks) * 7 * 3\n", "\n", "def nightShiftNumberRule(model, wid, fte_adj):\n", " max_shifts = total_night_shifts / full_time_equivalent * fte_adj + night_balance_offset\n", " min_shifts = total_night_shifts / full_time_equivalent * fte_adj - night_balance_offset\n", " \n", " return inequality(min_shifts, sum(model.works[wid, week, day, \"night\"] for week in weeks for day in days), max_shifts)\n", "model.night_shifts_constraint = Constraint([(worker.id, worker.fte_adj) for worker in workers], rule=nightShiftNumberRule)\n", "\n", "\n", "twighlight_constraints = {}\n", "\n", "# Set a rule to even twighlight shifts:\n", "total_twighlight_shifts = len(weeks) * 5 * 1 # One person per site\n", "\n", "def truroTwighlightShiftNumberRule(model, wid, fte_adj, site, shift):\n", " #site, shift = site_and_shift\n", " max_shifts = total_twighlight_shifts / full_time_equivalent_sites[site] * fte_adj + twighlight_balance_offset\n", " min_shifts = total_twighlight_shifts / full_time_equivalent_sites[site] * fte_adj - twighlight_balance_offset\n", " \n", " return inequality(min_shifts, sum(model.works[wid, week, day, shift.GetShift()] for week in weeks for day in days), max_shifts)\n", "model.twighlight_shifts_truro_constraint = Constraint([(worker.id, worker.fte_adj, ss[0], ss[1]) for ss in site_and_shifts for worker in workers if worker.site == ss[0]], rule=truroTwighlightShiftNumberRule)\n", "\n", "#def plymouthTwighlightShiftNumberRule(model, wid, fte_adj):\n", "# max_shifts = total_twighlight_shifts / full_time_equivalent_sites[\"plymouth\"] * fte_adj + twighlight_balance_offset\n", "# min_shifts = total_twighlight_shifts / full_time_equivalent_sites[\"plymouth\"] * fte_adj - twighlight_balance_offset\n", "# \n", "# return inequality(min_shifts, sum(model.works[wid, week, day, \"plymouth_twighlight\"] for week in weeks for day in days), max_shifts)\n", "#model.twighlight_shifts_plymouth_constraint = Constraint([(worker.id, worker.fte_adj) for worker in workers if worker.site == \"plymouth\"], #rule=plymouthTwighlightShiftNumberRule)\n", "\n", "\n", "# Limit to 1 ST2 (or below) on a night shift\n", "def nightShiftMaxSTRule(model, week, block):\n", " single_workers = [w for w in workers if w.grade < 3]\n", " if not single_workers:\n", " return Constraint.Skip\n", " return (sum(model.nights[w.id, week, block] for w in single_workers) <= 1)\n", "model.night_shifts_max_st_constraint = Constraint([week for week in weeks], [block for block in [\"weekday\", \"weekend\"]], rule=nightShiftMaxSTRule)\n", "\n", "# Enusre at least 1 ST4+ on a night shift\n", "def nightShiftMinST4Rule(model, week, block):\n", " single_workers = [w for w in workers if w.grade > 3]\n", " if not single_workers:\n", " return Constraint.Skip\n", " return (sum(model.nights[w.id, week, block] for w in single_workers) >= 1)\n", "model.night_shifts_min_st4_constraint = Constraint([week for week in weeks], [block for block in [\"weekday\", \"weekend\"]], rule=nightShiftMinST4Rule)\n", "\n", "# Constraint: rest between two shifts is of 12 hours (i.e., at least two shifts)\n", "for worker in workers:\n", " for week in weeks:\n", " #model.constraints.add(\n", " #0 == sum(model.works[worker, week, day, shift] for day in days for shift in [\"night\"]) * (sum(model.works[worker, week, day, shift] for day in days for shift in [\"night\"]) - 4) * (sum(model.works[worker, week, day, shift] for day in days for shift in [\"night\"]) - 3)\n", " # 0 == (sum(model.works[worker, week, day, shift] for day in days for shift in [\"night\"]) - 4) * (sum(model.works[worker, week, day, shift] for day in days for shift in [\"night\"]) - 3)\n", " # )\n", "\n", " # Force nights to be assigned in blocks\n", " model.constraints.add(\n", " 1 == sum(model.nights[worker.id, week, block] for block in night_blocks)\n", " )\n", " \n", " # if night block is weekday make sure Mon - Thurs is assigned as nights\n", " model.constraints.add(\n", " model.nights[worker.id, week, \"weekday\"] * 4 == sum(model.works[worker.id, week, day, \"night\"] for day in days[:4])\n", " )\n", " model.constraints.add(\n", " model.nights[worker.id, week, \"weekend\"] * 3 == sum(model.works[worker.id, week, day, \"night\"] for day in days[4:])\n", " )\n", " \n", "\n", "\n", " for j in range(len(days)):\n", " nweek = min(week + (j + 1) // 7, weeks[-1])\n", " pweek = week\n", " if j == 0:\n", " pweek = max(week - 1, 1)\n", "\n", " # Unable to work (hard constraint not preference)\n", " model.constraints.add(\n", " model.available[(worker.id, week, days[j])] >= \\\n", " sum(model.works[worker.id, week, days[j], shift] for shift in days_shifts[days[j]]) \n", " )\n", " model.constraints.add(\n", " model.available[(worker.id, week, days[j])] >= \\\n", " model.works[worker.id, pweek, days[(j - 1) % 7], \"night\"]\n", " )\n", "\n", "\n", " # single shift per day\n", " model.constraints.add(\n", " 1 >= sum(model.works[worker.id, week, days[j], shift] for shift in days_shifts[days[j]])\n", " )\n", " ## if working in evening, until next evening (note that after sunday comes next monday)\n", " #model.constraints.add(\n", " # 1 >= sum(model.works[worker.id, week, days[j], shift] for shift in ['evening', 'night']) +\n", " # model.works[worker.id, nweek, days[(j + 1) % 7], 'truro_twighlight']\n", " #)\n", "\n", " # if working a night ensure preceeding (1) or subsequent (2) shifts can only be nights\n", " model.constraints.add(\n", " 1 >= model.works[worker.id, week, days[j], \"night\"] +\n", " sum(model.works[worker.id, nweek, days[(j + 1) % 7], shift] for shift in ['truro_twighlight', 'plymouth_twighlight']) +\n", " sum(model.works[worker.id, nweek, days[(j + 2) % 7], shift] for shift in ['truro_twighlight', 'plymouth_twighlight']) +\n", " sum(model.works[worker.id, pweek, days[(j - 1) % 7], shift] for shift in ['truro_twighlight', 'plymouth_twighlight'])\n", " )\n", "\n", "# Constraint (def of model.needed)\n", "#for worker in workers:\n", "# model.constraints.add(\n", "# 10000 * model.needed[worker.id] >= sum(model.works[worker.id, week, day, shift] for week in weeks for day in days for shift in days_shifts[day])\n", "# ) # if any model.works[worker, ·, ·] non-zero, model.needed[worker] must be one; else is zero to reduce the obj function\n", " # 10000 is to remark, but 5 was enough since max of 40 hours yields max of 5 shifts, the maximum possible sum\n", "\n", "# Constraint (def of model.no_pref)\n", "#for worker in workers:\n", "# for week in weeks:\n", "# model.constraints.add(\n", "# model.no_pref[worker.id] >= sum(model.works[worker.id, week, 'Sat', shift] for shift in days_shifts['Sat'])\n", "# - sum(model.works[worker.id, week, 'Sun', shift] for shift in days_shifts['Sun'])\n", "# ) # if not working on sunday but working saturday model.needed must be 1; else will be zero to reduce the obj function\n" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "opt = SolverFactory('cbc') # choose a solver\n", "results = opt.solve(model) # solve the model with the selected solver" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "tags": [ "outputPrepend", "outputPrepend" ] }, "outputs": [], "source": [ "def get_workers_needed(needed):\n", " \"\"\"Extract to a list the needed workers for the optimal solution.\"\"\"\n", " workers_needed = []\n", " for worker in workers:\n", " if needed[worker.id].value == 1:\n", " workers_needed.append(worker.name)\n", " return workers_needed\n", "\n", "\n", "def get_work_table(works):\n", " \"\"\"Build a timetable of the week as a dictionary from the model's optimal solution.\"\"\"\n", " week_table = {week :{day: {shift: [] for shift in days_shifts[day]} for day in days} for week in weeks}\n", " for week in weeks:\n", " for worker in workers:\n", " for day in days:\n", " for shift in days_shifts[day]:\n", " if works[worker.id, week, day, shift].value == 1:\n", " week_table[week][day][shift].append(worker.GetDetails())\n", " return week_table\n", "\n", "def get_worker_timetable(works):\n", " timetable = { worker.name : { week : { day: \"\" for day in days } for week in weeks } for worker in workers }\n", " for worker in workers:\n", " for week in weeks:\n", " for day in days:\n", " for shift in days_shifts[day]:\n", " if works[worker.id, week, day, shift].value == 1:\n", " timetable[worker.name][week][day] = shift\n", " return timetable\n", "\n", "def get_shift_summary(works):\n", " timetable = { worker.GetDetails() : { shift : \"\" for shift in shifts } for worker in workers }\n", " #timetable = { worker.GetDetails() : { shift : \"\" for shift in [\"truro_twighlight\"] } for worker in workers } \n", " for worker in workers:\n", "\n", " for shift in shifts:\n", " #for shift in [\"truro_twighlight\"]:\n", " timetable[worker.GetDetails()][shift] = [works[worker.id, week, day, shift] for week in weeks for day in days].count(1)\n", " return timetable \n", "\n", "def get_no_preference(no_pref):\n", " \"\"\"Extract to a list the workers not satisfied with their weekend preference.\"\"\"\n", " return [worker.id for worker in workers if no_pref[worker.id].value == 1]\n", "\n", "def get_night_blocks(nights):\n", " timetable = { worker.name : { week : { block: \"\" for block in night_blocks } for week in weeks } for worker in workers }\n", " for worker in workers:\n", " for week in weeks:\n", " for block in night_blocks:\n", " if nights[worker.id, week, block].value == 1:\n", " timetable[worker.name][week][block] = \"true\"\n", " return timetable\n", "\n", "\n", "workers_needed = get_workers_needed(model.needed) # dict with the optimal timetable\n", "week_table = get_work_table(model.works) # list with the required workers\n", "worker_timetable = get_worker_timetable(model.works)\n", "workers_no_pref = get_no_preference(model.no_pref) # list with the non-satisfied workers (work on Sat but not on Sun)\n", "worker_night_block = get_night_blocks(model.nights)\n", "worker_shift_summary = get_shift_summary(model.works)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "{'1 t 4': {'night': 10, 'truro_twighlight': 7, 'plymouth_twighlight': 0},\n '2 t 4': {'night': 10, 'truro_twighlight': 7, 'plymouth_twighlight': 0},\n '3 t 4': {'night': 9, 'truro_twighlight': 6, 'plymouth_twighlight': 0},\n '4 t 4': {'night': 9, 'truro_twighlight': 7, 'plymouth_twighlight': 0},\n '5 t 4': {'night': 11, 'truro_twighlight': 7, 'plymouth_twighlight': 0},\n '6 t 4': {'night': 11, 'truro_twighlight': 7, 'plymouth_twighlight': 0},\n '7 t 4': {'night': 9, 'truro_twighlight': 7, 'plymouth_twighlight': 0},\n '8 t 4': {'night': 10, 'truro_twighlight': 7, 'plymouth_twighlight': 0},\n '9 t 4': {'night': 9, 'truro_twighlight': 7, 'plymouth_twighlight': 0},\n '10 t 4': {'night': 10, 'truro_twighlight': 7, 'plymouth_twighlight': 0},\n '11 t 4': {'night': 11, 'truro_twighlight': 6, 'plymouth_twighlight': 0},\n '12 t 4': {'night': 10, 'truro_twighlight': 7, 'plymouth_twighlight': 0},\n '13 t 4': {'night': 11, 'truro_twighlight': 7, 'plymouth_twighlight': 0},\n '14 t 4': {'night': 8, 'truro_twighlight': 7, 'plymouth_twighlight': 0},\n '15 t 4': {'night': 8, 'truro_twighlight': 7, 'plymouth_twighlight': 0},\n '16 t 4': {'night': 8, 'truro_twighlight': 6, 'plymouth_twighlight': 0},\n '17 t 4': {'night': 11, 'truro_twighlight': 7, 'plymouth_twighlight': 0},\n '18 t 4': {'night': 10, 'truro_twighlight': 7, 'plymouth_twighlight': 0},\n '19 t 4': {'night': 10, 'truro_twighlight': 7, 'plymouth_twighlight': 0},\n '21 p 4': {'night': 10, 'truro_twighlight': 0, 'plymouth_twighlight': 3},\n '22 p 4': {'night': 8, 'truro_twighlight': 0, 'plymouth_twighlight': 4},\n '23 p 4': {'night': 11, 'truro_twighlight': 0, 'plymouth_twighlight': 4},\n '24 p 4': {'night': 10, 'truro_twighlight': 0, 'plymouth_twighlight': 4},\n '25 p 4': {'night': 9, 'truro_twighlight': 0, 'plymouth_twighlight': 4},\n '26 p 4': {'night': 10, 'truro_twighlight': 0, 'plymouth_twighlight': 4},\n '27 p 4': {'night': 9, 'truro_twighlight': 0, 'plymouth_twighlight': 4},\n '28 p 4': {'night': 9, 'truro_twighlight': 0, 'plymouth_twighlight': 3},\n '29 p 4': {'night': 11, 'truro_twighlight': 0, 'plymouth_twighlight': 4},\n '30 p 4': {'night': 10, 'truro_twighlight': 0, 'plymouth_twighlight': 4},\n '31 p 4': {'night': 10, 'truro_twighlight': 0, 'plymouth_twighlight': 3},\n '32 p 4': {'night': 10, 'truro_twighlight': 0, 'plymouth_twighlight': 4},\n '33 p 4': {'night': 11, 'truro_twighlight': 0, 'plymouth_twighlight': 3},\n '34 p 4': {'night': 11, 'truro_twighlight': 0, 'plymouth_twighlight': 4},\n '35 p 4': {'night': 11, 'truro_twighlight': 0, 'plymouth_twighlight': 4},\n '36 p 4': {'night': 9, 'truro_twighlight': 0, 'plymouth_twighlight': 4},\n '37 p 4': {'night': 11, 'truro_twighlight': 0, 'plymouth_twighlight': 3},\n '38 p 4': {'night': 8, 'truro_twighlight': 0, 'plymouth_twighlight': 3},\n '39 p 4': {'night': 11, 'truro_twighlight': 0, 'plymouth_twighlight': 4},\n '40 p 4': {'night': 10, 'truro_twighlight': 0, 'plymouth_twighlight': 3},\n '41 p 4': {'night': 10, 'truro_twighlight': 0, 'plymouth_twighlight': 3},\n '42 p 4': {'night': 11, 'truro_twighlight': 0, 'plymouth_twighlight': 3},\n '43 p 4': {'night': 10, 'truro_twighlight': 0, 'plymouth_twighlight': 4},\n '44 p 4': {'night': 8, 'truro_twighlight': 0, 'plymouth_twighlight': 3},\n '45 p 4': {'night': 11, 'truro_twighlight': 0, 'plymouth_twighlight': 4},\n '46 p 4': {'night': 11, 'truro_twighlight': 0, 'plymouth_twighlight': 3},\n '47 p 4': {'night': 10, 'truro_twighlight': 0, 'plymouth_twighlight': 3},\n '48 p 4': {'night': 11, 'truro_twighlight': 0, 'plymouth_twighlight': 4},\n '49 p 4': {'night': 8, 'truro_twighlight': 0, 'plymouth_twighlight': 3},\n '50 p 4': {'night': 8, 'truro_twighlight': 0, 'plymouth_twighlight': 3},\n '51 p 4': {'night': 8, 'truro_twighlight': 0, 'plymouth_twighlight': 3},\n '52 p 4': {'night': 11, 'truro_twighlight': 0, 'plymouth_twighlight': 3},\n '53 p 4': {'night': 8, 'truro_twighlight': 0, 'plymouth_twighlight': 4},\n '54 p 4': {'night': 10, 'truro_twighlight': 0, 'plymouth_twighlight': 3},\n '55 p 4': {'night': 11, 'truro_twighlight': 0, 'plymouth_twighlight': 3},\n '56 p 4': {'night': 8, 'truro_twighlight': 0, 'plymouth_twighlight': 4},\n '57 p 4': {'night': 8, 'truro_twighlight': 0, 'plymouth_twighlight': 4}}" }, "metadata": {}, "execution_count": 12 } ], "source": [ "#worker_timetable\n", "worker_shift_summary" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "[]" }, "metadata": {}, "execution_count": 13 } ], "source": [ "workers_needed" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": " t 4'],\n 'plymouth_twighlight': ['43 p 4']},\n 'Wed': {'night': ['11 t 4', '16 t 4', '46 p 4'],\n 'truro_twighlight': ['18 t 4'],\n 'plymouth_twighlight': ['24 p 4']},\n 'Thu': {'night': ['11 t 4', '16 t 4', '46 p 4'],\n 'truro_twighlight': ['8 t 4'],\n 'plymouth_twighlight': ['40 p 4']},\n 'Fri': {'night': ['12 t 4', '21 p 4', '43 p 4'],\n 'truro_twighlight': ['4 t 4'],\n 'plymouth_twighlight': ['31 p 4']},\n 'Sat': {'night': ['12 t 4', '21 p 4', '43 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['12 t 4', '21 p 4', '43 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 4: {'Mon': {'night': ['8 t 4', '37 p 4', '41 p 4'],\n 'truro_twighlight': ['6 t 4'],\n 'plymouth_twighlight': ['24 p 4']},\n 'Tue': {'night': ['8 t 4', '37 p 4', '41 p 4'],\n 'truro_twighlight': ['7 t 4'],\n 'plymouth_twighlight': ['46 p 4']},\n 'Wed': {'night': ['8 t 4', '37 p 4', '41 p 4'],\n 'truro_twighlight': ['4 t 4'],\n 'plymouth_twighlight': ['35 p 4']},\n 'Thu': {'night': ['8 t 4', '37 p 4', '41 p 4'],\n 'truro_twighlight': ['12 t 4'],\n 'plymouth_twighlight': ['54 p 4']},\n 'Fri': {'night': ['32 p 4', '42 p 4', '48 p 4'],\n 'truro_twighlight': ['11 t 4'],\n 'plymouth_twighlight': ['29 p 4']},\n 'Sat': {'night': ['32 p 4', '42 p 4', '48 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['32 p 4', '42 p 4', '48 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 5: {'Mon': {'night': ['12 t 4', '35 p 4', '44 p 4'],\n 'truro_twighlight': ['8 t 4'],\n 'plymouth_twighlight': ['56 p 4']},\n 'Tue': {'night': ['12 t 4', '35 p 4', '44 p 4'],\n 'truro_twighlight': ['18 t 4'],\n 'plymouth_twighlight': ['34 p 4']},\n 'Wed': {'night': ['12 t 4', '35 p 4', '44 p 4'],\n 'truro_twighlight': ['11 t 4'],\n 'plymouth_twighlight': ['41 p 4']},\n 'Thu': {'night': ['12 t 4', '35 p 4', '44 p 4'],\n 'truro_twighlight': ['13 t 4'],\n 'plymouth_twighlight': ['51 p 4']},\n 'Fri': {'night': ['34 p 4', '45 p 4', '54 p 4'],\n 'truro_twighlight': ['3 t 4'],\n 'plymouth_twighlight': ['50 p 4']},\n 'Sat': {'night': ['34 p 4', '45 p 4', '54 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['34 p 4', '45 p 4', '54 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 6: {'Mon': {'night': ['32 p 4', '37 p 4', '45 p 4'],\n 'truro_twighlight': ['6 t 4'],\n 'plymouth_twighlight': ['26 p 4']},\n 'Tue': {'night': ['32 p 4', '37 p 4', '45 p 4'],\n 'truro_twighlight': ['11 t 4'],\n 'plymouth_twighlight': ['36 p 4']},\n 'Wed': {'night': ['32 p 4', '37 p 4', '45 p 4'],\n 'truro_twighlight': ['10 t 4'],\n 'plymouth_twighlight': ['39 p 4']},\n 'Thu': {'night': ['32 p 4', '37 p 4', '45 p 4'],\n 'truro_twighlight': ['2 t 4'],\n 'plymouth_twighlight': ['42 p 4']},\n 'Fri': {'night': ['9 t 4', '13 t 4', '27 p 4'],\n 'truro_twighlight': ['1 t 4'],\n 'plymouth_twighlight': ['49 p 4']},\n 'Sat': {'night': ['9 t 4', '13 t 4', '27 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['9 t 4', '13 t 4', '27 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 7: {'Mon': {'night': ['13 t 4', '23 p 4', '24 p 4'],\n 'truro_twighlight': ['1 t 4'],\n 'plymouth_twighlight': ['33 p 4']},\n 'Tue': {'night': ['13 t 4', '23 p 4', '24 p 4'],\n 'truro_twighlight': ['14 t 4'],\n 'plymouth_twighlight': ['37 p 4']},\n 'Wed': {'night': ['13 t 4', '23 p 4', '24 p 4'],\n 'truro_twighlight': ['9 t 4'],\n 'plymouth_twighlight': ['56 p 4']},\n 'Thu': {'night': ['13 t 4', '23 p 4', '24 p 4'],\n 'truro_twighlight': ['2 t 4'],\n 'plymouth_twighlight': ['53 p 4']},\n 'Fri': {'night': ['10 t 4', '28 p 4', '30 p 4'],\n 'truro_twighlight': ['15 t 4'],\n 'plymouth_twighlight': ['39 p 4']},\n 'Sat': {'night': ['10 t 4', '28 p 4', '30 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['10 t 4', '28 p 4', '30 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 8: {'Mon': {'night': ['45 p 4', '51 p 4', '56 p 4'],\n 'truro_twighlight': ['15 t 4'],\n 'plymouth_twighlight': ['26 p 4']},\n 'Tue': {'night': ['45 p 4', '51 p 4', '56 p 4'],\n 'truro_twighlight': ['6 t 4'],\n 'plymouth_twighlight': ['23 p 4']},\n 'Wed': {'night': ['45 p 4', '51 p 4', '56 p 4'],\n 'truro_twighlight': ['12 t 4'],\n 'plymouth_twighlight': ['28 p 4']},\n 'Thu': {'night': ['45 p 4', '51 p 4', '56 p 4'],\n 'truro_twighlight': ['17 t 4'],\n 'plymouth_twighlight': ['32 p 4']},\n 'Fri': {'night': ['18 t 4', '24 p 4', '37 p 4'],\n 'truro_twighlight': ['3 t 4'],\n 'plymouth_twighlight': ['42 p 4']},\n 'Sat': {'night': ['18 t 4', '24 p 4', '37 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['18 t 4', '24 p 4', '37 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 9: {'Mon': {'night': ['16 t 4', '22 p 4', '54 p 4'],\n 'truro_twighlight': ['3 t 4'],\n 'plymouth_twighlight': ['32 p 4']},\n 'Tue': {'night': ['16 t 4', '22 p 4', '54 p 4'],\n 'truro_twighlight': ['2 t 4'],\n 'plymouth_twighlight': ['25 p 4']},\n 'Wed': {'night': ['16 t 4', '22 p 4', '54 p 4'],\n 'truro_twighlight': ['5 t 4'],\n 'plymouth_twighlight': ['43 p 4']},\n 'Thu': {'night': ['16 t 4', '22 p 4', '54 p 4'],\n 'truro_twighlight': ['1 t 4'],\n 'plymouth_twighlight': ['52 p 4']},\n 'Fri': {'night': ['4 t 4', '23 p 4', '27 p 4'],\n 'truro_twighlight': ['14 t 4'],\n 'plymouth_twighlight': ['57 p 4']},\n 'Sat': {'night': ['4 t 4', '23 p 4', '27 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['4 t 4', '23 p 4', '27 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 10: {'Mon': {'night': ['31 p 4', '48 p 4', '55 p 4'],\n 'truro_twighlight': ['2 t 4'],\n 'plymouth_twighlight': ['42 p 4']},\n 'Tue': {'night': ['31 p 4', '48 p 4', '55 p 4'],\n 'truro_twighlight': ['15 t 4'],\n 'plymouth_twighlight': ['25 p 4']},\n 'Wed': {'night': ['31 p 4', '48 p 4', '55 p 4'],\n 'truro_twighlight': ['18 t 4'],\n 'plymouth_twighlight': ['27 p 4']},\n 'Thu': {'night': ['31 p 4', '48 p 4', '55 p 4'],\n 'truro_twighlight': ['10 t 4'],\n 'plymouth_twighlight': ['36 p 4']},\n 'Fri': {'night': ['8 t 4', '17 t 4', '47 p 4'],\n 'truro_twighlight': ['13 t 4'],\n 'plymouth_twighlight': ['34 p 4']},\n 'Sat': {'night': ['8 t 4', '17 t 4', '47 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['8 t 4', '17 t 4', '47 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 11: {'Mon': {'night': ['5 t 4', '14 t 4', '43 p 4'],\n 'truro_twighlight': ['7 t 4'],\n 'plymouth_twighlight': ['27 p 4']},\n 'Tue': {'night': ['5 t 4', '14 t 4', '43 p 4'],\n 'truro_twighlight': ['4 t 4'],\n 'plymouth_twighlight': ['31 p 4']},\n 'Wed': {'night': ['5 t 4', '14 t 4', '43 p 4'],\n 'truro_twighlight': ['1 t 4'],\n 'plymouth_twighlight': ['52 p 4']},\n 'Thu': {'night': ['5 t 4', '14 t 4', '43 p 4'],\n 'truro_twighlight': ['10 t 4'],\n 'plymouth_twighlight': ['57 p 4']},\n 'Fri': {'night': ['3 t 4', '26 p 4', '28 p 4'],\n 'truro_twighlight': ['11 t 4'],\n 'plymouth_twighlight': ['23 p 4']},\n 'Sat': {'night': ['3 t 4', '26 p 4', '28 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['3 t 4', '26 p 4', '28 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 12: {'Mon': {'night': ['5 t 4', '26 p 4', '49 p 4'],\n 'truro_twighlight': ['19 t 4'],\n 'plymouth_twighlight': ['36 p 4']},\n 'Tue': {'night': ['5 t 4', '26 p 4', '49 p 4'],\n 'truro_twighlight': ['10 t 4'],\n 'plymouth_twighlight': ['41 p 4']},\n 'Wed': {'night': ['5 t 4', '26 p 4', '49 p 4'],\n 'truro_twighlight': ['11 t 4'],\n 'plymouth_twighlight': ['30 p 4']},\n 'Thu': {'night': ['5 t 4', '26 p 4', '49 p 4'],\n 'truro_twighlight': ['8 t 4'],\n 'plymouth_twighlight': ['34 p 4']},\n 'Fri': {'night': ['11 t 4', '39 p 4', '40 p 4'],\n 'truro_twighlight': ['16 t 4'],\n 'plymouth_twighlight': ['48 p 4']},\n 'Sat': {'night': ['11 t 4', '39 p 4', '40 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['11 t 4', '39 p 4', '40 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 13: {'Mon': {'night': ['38 p 4', '49 p 4', '51 p 4'],\n 'truro_twighlight': ['9 t 4'],\n 'plymouth_twighlight': ['52 p 4']},\n 'Tue': {'night': ['38 p 4', '49 p 4', '51 p 4'],\n 'truro_twighlight': ['1 t 4'],\n 'plymouth_twighlight': ['21 p 4']},\n 'Wed': {'night': ['38 p 4', '49 p 4', '51 p 4'],\n 'truro_twighlight': ['13 t 4'],\n 'plymouth_twighlight': ['40 p 4']},\n 'Thu': {'night': ['38 p 4', '49 p 4', '51 p 4'],\n 'truro_twighlight': ['16 t 4'],\n 'plymouth_twighlight': ['44 p 4']},\n 'Fri': {'night': ['4 t 4', '7 t 4', '30 p 4'],\n 'truro_twighlight': ['18 t 4'],\n 'plymouth_twighlight': ['54 p 4']},\n 'Sat': {'night': ['4 t 4', '7 t 4', '30 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['4 t 4', '7 t 4', '30 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 14: {'Mon': {'night': ['1 t 4', '22 p 4', '40 p 4'],\n 'truro_twighlight': ['9 t 4'],\n 'plymouth_twighlight': ['55 p 4']},\n 'Tue': {'night': ['1 t 4', '22 p 4', '40 p 4'],\n 'truro_twighlight': ['8 t 4'],\n 'plymouth_twighlight': ['37 p 4']},\n 'Wed': {'night': ['1 t 4', '22 p 4', '40 p 4'],\n 'truro_twighlight': ['15 t 4'],\n 'plymouth_twighlight': ['30 p 4']},\n 'Thu': {'night': ['1 t 4', '22 p 4', '40 p 4'],\n 'truro_twighlight': ['4 t 4'],\n 'plymouth_twighlight': ['33 p 4']},\n 'Fri': {'night': ['3 t 4', '7 t 4', '29 p 4'],\n 'truro_twighlight': ['14 t 4'],\n 'plymouth_twighlight': ['43 p 4']},\n 'Sat': {'night': ['3 t 4', '7 t 4', '29 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['3 t 4', '7 t 4', '29 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 15: {'Mon': {'night': ['17 t 4', '42 p 4', '53 p 4'],\n 'truro_twighlight': ['9 t 4'],\n 'plymouth_twighlight': ['21 p 4']},\n 'Tue': {'night': ['17 t 4', '42 p 4', '53 p 4'],\n 'truro_twighlight': ['10 t 4'],\n 'plymouth_twighlight': ['45 p 4']},\n 'Wed': {'night': ['17 t 4', '42 p 4', '53 p 4'],\n 'truro_twighlight': ['12 t 4'],\n 'plymouth_twighlight': ['44 p 4']},\n 'Thu': {'night': ['17 t 4', '42 p 4', '53 p 4'],\n 'truro_twighlight': ['19 t 4'],\n 'plymouth_twighlight': ['46 p 4']},\n 'Fri': {'night': ['1 t 4', '3 t 4', '31 p 4'],\n 'truro_twighlight': ['16 t 4'],\n 'plymouth_twighlight': ['50 p 4']},\n 'Sat': {'night': ['1 t 4', '3 t 4', '31 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['1 t 4', '3 t 4', '31 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 16: {'Mon': {'night': ['2 t 4', '15 t 4', '57 p 4'],\n 'truro_twighlight': ['18 t 4'],\n 'plymouth_twighlight': ['25 p 4']},\n 'Tue': {'night': ['2 t 4', '15 t 4', '57 p 4'],\n 'truro_twighlight': ['19 t 4'],\n 'plymouth_twighlight': ['24 p 4']},\n 'Wed': {'night': ['2 t 4', '15 t 4', '57 p 4'],\n 'truro_twighlight': ['17 t 4'],\n 'plymouth_twighlight': ['28 p 4']},\n 'Thu': {'night': ['2 t 4', '15 t 4', '57 p 4'],\n 'truro_twighlight': ['13 t 4'],\n 'plymouth_twighlight': ['50 p 4']},\n 'Fri': {'night': ['7 t 4', '19 t 4', '36 p 4'],\n 'truro_twighlight': ['6 t 4'],\n 'plymouth_twighlight': ['27 p 4']},\n 'Sat': {'night': ['7 t 4', '19 t 4', '36 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['7 t 4', '19 t 4', '36 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 17: {'Mon': {'night': ['6 t 4', '15 t 4', '34 p 4'],\n 'truro_twighlight': ['17 t 4'],\n 'plymouth_twighlight': ['38 p 4']},\n 'Tue': {'night': ['6 t 4', '15 t 4', '34 p 4'],\n 'truro_twighlight': ['5 t 4'],\n 'plymouth_twighlight': ['23 p 4']},\n 'Wed': {'night': ['6 t 4', '15 t 4', '34 p 4'],\n 'truro_twighlight': ['14 t 4'],\n 'plymouth_twighlight': ['45 p 4']},\n 'Thu': {'night': ['6 t 4', '15 t 4', '34 p 4'],\n 'truro_twighlight': ['9 t 4'],\n 'plymouth_twighlight': ['55 p 4']},\n 'Fri': {'night': ['4 t 4', '25 p 4', '36 p 4'],\n 'truro_twighlight': ['16 t 4'],\n 'plymouth_twighlight': ['33 p 4']},\n 'Sat': {'night': ['4 t 4', '25 p 4', '36 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['4 t 4', '25 p 4', '36 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 18: {'Mon': {'night': ['6 t 4', '10 t 4', '42 p 4'],\n 'truro_twighlight': ['7 t 4'],\n 'plymouth_twighlight': ['40 p 4']},\n 'Tue': {'night': ['6 t 4', '10 t 4', '42 p 4'],\n 'truro_twighlight': ['5 t 4'],\n 'plymouth_twighlight': ['35 p 4']},\n 'Wed': {'night': ['6 t 4', '10 t 4', '42 p 4'],\n 'truro_twighlight': ['13 t 4'],\n 'plymouth_twighlight': ['38 p 4']},\n 'Thu': {'night': ['6 t 4', '10 t 4', '42 p 4'],\n 'truro_twighlight': ['11 t 4'],\n 'plymouth_twighlight': ['23 p 4']},\n 'Fri': {'night': ['18 t 4', '28 p 4', '35 p 4'],\n 'truro_twighlight': ['17 t 4'],\n 'plymouth_twighlight': ['26 p 4']},\n 'Sat': {'night': ['18 t 4', '28 p 4', '35 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['18 t 4', '28 p 4', '35 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 19: {'Mon': {'night': ['13 t 4', '18 t 4', '19 t 4'],\n 'truro_twighlight': ['10 t 4'],\n 'plymouth_twighlight': ['53 p 4']},\n 'Tue': {'night': ['13 t 4', '18 t 4', '19 t 4'],\n 'truro_twighlight': ['4 t 4'],\n 'plymouth_twighlight': ['47 p 4']},\n 'Wed': {'night': ['13 t 4', '18 t 4', '19 t 4'],\n 'truro_twighlight': ['15 t 4'],\n 'plymouth_twighlight': ['29 p 4']},\n 'Thu': {'night': ['13 t 4', '18 t 4', '19 t 4'],\n 'truro_twighlight': ['9 t 4'],\n 'plymouth_twighlight': ['54 p 4']},\n 'Fri': {'night': ['5 t 4', '12 t 4', '25 p 4'],\n 'truro_twighlight': ['3 t 4'],\n 'plymouth_twighlight': ['51 p 4']},\n 'Sat': {'night': ['5 t 4', '12 t 4', '25 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['5 t 4', '12 t 4', '25 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 20: {'Mon': {'night': ['38 p 4', '44 p 4', '53 p 4'],\n 'truro_twighlight': ['9 t 4'],\n 'plymouth_twighlight': ['22 p 4']},\n 'Tue': {'night': ['38 p 4', '44 p 4', '53 p 4'],\n 'truro_twighlight': ['6 t 4'],\n 'plymouth_twighlight': ['39 p 4']},\n 'Wed': {'night': ['38 p 4', '44 p 4', '53 p 4'],\n 'truro_twighlight': ['7 t 4'],\n 'plymouth_twighlight': ['34 p 4']},\n 'Thu': {'night': ['38 p 4', '44 p 4', '53 p 4'],\n 'truro_twighlight': ['19 t 4'],\n 'plymouth_twighlight': ['29 p 4']},\n 'Fri': {'night': ['6 t 4', '8 t 4', '31 p 4'],\n 'truro_twighlight': ['14 t 4'],\n 'plymouth_twighlight': ['32 p 4']},\n 'Sat': {'night': ['6 t 4', '8 t 4', '31 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['6 t 4', '8 t 4', '31 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 21: {'Mon': {'night': ['14 t 4', '21 p 4', '23 p 4'],\n 'truro_twighlight': ['12 t 4'],\n 'plymouth_twighlight': ['57 p 4']},\n 'Tue': {'night': ['14 t 4', '21 p 4', '23 p 4'],\n 'truro_twighlight': ['2 t 4'],\n 'plymouth_twighlight': ['38 p 4']},\n 'Wed': {'night': ['14 t 4', '21 p 4', '23 p 4'],\n 'truro_twighlight': ['18 t 4'],\n 'plymouth_twighlight': ['53 p 4']},\n 'Thu': {'night': ['14 t 4', '21 p 4', '23 p 4'],\n 'truro_twighlight': ['5 t 4'],\n 'plymouth_twighlight': ['45 p 4']},\n 'Fri': {'night': ['9 t 4', '32 p 4', '43 p 4'],\n 'truro_twighlight': ['19 t 4'],\n 'plymouth_twighlight': ['56 p 4']},\n 'Sat': {'night': ['9 t 4', '32 p 4', '43 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['9 t 4', '32 p 4', '43 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 22: {'Mon': {'night': ['17 t 4', '55 p 4', '57 p 4'],\n 'truro_twighlight': ['1 t 4'],\n 'plymouth_twighlight': ['41 p 4']},\n 'Tue': {'night': ['17 t 4', '55 p 4', '57 p 4'],\n 'truro_twighlight': ['16 t 4'],\n 'plymouth_twighlight': ['30 p 4']},\n 'Wed': {'night': ['17 t 4', '55 p 4', '57 p 4'],\n 'truro_twighlight': ['12 t 4'],\n 'plymouth_twighlight': ['45 p 4']},\n 'Thu': {'night': ['17 t 4', '55 p 4', '57 p 4'],\n 'truro_twighlight': ['18 t 4'],\n 'plymouth_twighlight': ['29 p 4']},\n 'Fri': {'night': ['19 t 4', '27 p 4', '40 p 4'],\n 'truro_twighlight': ['6 t 4'],\n 'plymouth_twighlight': ['35 p 4']},\n 'Sat': {'night': ['19 t 4', '27 p 4', '40 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['19 t 4', '27 p 4', '40 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 23: {'Mon': {'night': ['39 p 4', '48 p 4', '50 p 4'],\n 'truro_twighlight': ['6 t 4'],\n 'plymouth_twighlight': ['35 p 4']},\n 'Tue': {'night': ['39 p 4', '48 p 4', '50 p 4'],\n 'truro_twighlight': ['8 t 4'],\n 'plymouth_twighlight': ['46 p 4']},\n 'Wed': {'night': ['39 p 4', '48 p 4', '50 p 4'],\n 'truro_twighlight': ['7 t 4'],\n 'plymouth_twighlight': ['31 p 4']},\n 'Thu': {'night': ['39 p 4', '48 p 4', '50 p 4'],\n 'truro_twighlight': ['14 t 4'],\n 'plymouth_twighlight': ['24 p 4']},\n 'Fri': {'night': ['33 p 4', '52 p 4', '55 p 4'],\n 'truro_twighlight': ['2 t 4'],\n 'plymouth_twighlight': ['26 p 4']},\n 'Sat': {'night': ['33 p 4', '52 p 4', '55 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['33 p 4', '52 p 4', '55 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 24: {'Mon': {'night': ['35 p 4', '46 p 4', '52 p 4'],\n 'truro_twighlight': ['12 t 4'],\n 'plymouth_twighlight': ['48 p 4']},\n 'Tue': {'night': ['35 p 4', '46 p 4', '52 p 4'],\n 'truro_twighlight': ['5 t 4'],\n 'plymouth_twighlight': ['53 p 4']},\n 'Wed': {'night': ['35 p 4', '46 p 4', '52 p 4'],\n 'truro_twighlight': ['17 t 4'],\n 'plymouth_twighlight': ['55 p 4']},\n 'Thu': {'night': ['35 p 4', '46 p 4', '52 p 4'],\n 'truro_twighlight': ['15 t 4'],\n 'plymouth_twighlight': ['47 p 4']},\n 'Fri': {'night': ['9 t 4', '25 p 4', '41 p 4'],\n 'truro_twighlight': ['8 t 4'],\n 'plymouth_twighlight': ['57 p 4']},\n 'Sat': {'night': ['9 t 4', '25 p 4', '41 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['9 t 4', '25 p 4', '41 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 25: {'Mon': {'night': ['29 p 4', '33 p 4', '39 p 4'],\n 'truro_twighlight': ['17 t 4'],\n 'plymouth_twighlight': ['30 p 4']},\n 'Tue': {'night': ['29 p 4', '33 p 4', '39 p 4'],\n 'truro_twighlight': ['14 t 4'],\n 'plymouth_twighlight': ['48 p 4']},\n 'Wed': {'night': ['29 p 4', '33 p 4', '39 p 4'],\n 'truro_twighlight': ['16 t 4'],\n 'plymouth_twighlight': ['51 p 4']},\n 'Thu': {'night': ['29 p 4', '33 p 4', '39 p 4'],\n 'truro_twighlight': ['8 t 4'],\n 'plymouth_twighlight': ['22 p 4']},\n 'Fri': {'night': ['2 t 4', '10 t 4', '36 p 4'],\n 'truro_twighlight': ['19 t 4'],\n 'plymouth_twighlight': ['47 p 4']},\n 'Sat': {'night': ['2 t 4', '10 t 4', '36 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['2 t 4', '10 t 4', '36 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}},\n 26: {'Mon': {'night': ['47 p 4', '52 p 4', '56 p 4'],\n 'truro_twighlight': ['12 t 4'],\n 'plymouth_twighlight': ['39 p 4']},\n 'Tue': {'night': ['47 p 4', '52 p 4', '56 p 4'],\n 'truro_twighlight': ['19 t 4'],\n 'plymouth_twighlight': ['25 p 4']},\n 'Wed': {'night': ['47 p 4', '52 p 4', '56 p 4'],\n 'truro_twighlight': ['2 t 4'],\n 'plymouth_twighlight': ['27 p 4']},\n 'Thu': {'night': ['47 p 4', '52 p 4', '56 p 4'],\n 'truro_twighlight': ['17 t 4'],\n 'plymouth_twighlight': ['44 p 4']},\n 'Fri': {'night': ['2 t 4', '24 p 4', '41 p 4'],\n 'truro_twighlight': ['13 t 4'],\n 'plymouth_twighlight': ['49 p 4']},\n 'Sat': {'night': ['2 t 4', '24 p 4', '41 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []},\n 'Sun': {'night': ['2 t 4', '24 p 4', '41 p 4'],\n 'truro_twighlight': [],\n 'plymouth_twighlight': []}}}" }, "metadata": {}, "execution_count": 14 } ], "source": [ "week_table" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "#worker_night_block" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "#[[worker, week, day, shift] for day in days for shift in [\"night\"]]" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "1" }, "metadata": {}, "execution_count": 17 } ], "source": [ "1 if 4 in [4, 1, 0] else 0" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "0" }, "metadata": {}, "execution_count": 18 } ], "source": [ "(4 + 1) // 7" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "#for worker in workers:\n", "# for shift in shifts:\n", "# a = [model.works[worker.id, week, day, shift].value for week in weeks for day in days]" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "output_type": "error", "ename": "NameError", "evalue": "name 'a' is not defined", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0ma\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'a' is not defined" ] } ], "source": [ "a" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "0" }, "metadata": {}, "execution_count": 21 } ], "source": [ "0 % 7" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "6" }, "metadata": {}, "execution_count": 22 } ], "source": [ "-1 % 7" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ] }