From 881c90df2d0f4cf1fbdd7e648f06b011ed1bc750 Mon Sep 17 00:00:00 2001 From: Sebastian Quintero Date: Wed, 24 Jan 2024 20:56:47 -0500 Subject: [PATCH] Add vehicle-routing and shift-assignment workflows --- .github/workflows/shift-assignment.yml | 15 +++ .github/workflows/vehicle-routing.yml | 15 +++ shift-assignment/input1.json | 137 +++++++++++++++++++++++++ shift-assignment/input2.json | 137 +++++++++++++++++++++++++ shift-assignment/input3.json | 137 +++++++++++++++++++++++++ shift-assignment/main.py | 11 +- 6 files changed, 446 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/shift-assignment.yml create mode 100644 .github/workflows/vehicle-routing.yml create mode 100644 shift-assignment/input1.json create mode 100644 shift-assignment/input2.json create mode 100644 shift-assignment/input3.json diff --git a/.github/workflows/shift-assignment.yml b/.github/workflows/shift-assignment.yml new file mode 100644 index 0000000..f0ff078 --- /dev/null +++ b/.github/workflows/shift-assignment.yml @@ -0,0 +1,15 @@ +name: shift-assignment + +on: + push: + paths: + - "./shift-assignment/**.py" + branches: + - stable + +jobs: + shift-assignment: + uses: ./.github/workflows/nextmv.yml + with: + APP_ID: shift-assignment-pyomo + APP_DIRECTORY: ./shift-assignment diff --git a/.github/workflows/vehicle-routing.yml b/.github/workflows/vehicle-routing.yml new file mode 100644 index 0000000..128c6bc --- /dev/null +++ b/.github/workflows/vehicle-routing.yml @@ -0,0 +1,15 @@ +name: vehicle-routing + +on: + push: + paths: + - "./vehicle-routing/**.py" + branches: + - stable + +jobs: + vehicle-routing: + uses: ./.github/workflows/nextmv.yml + with: + APP_ID: vehicle-routing-pyomo + APP_DIRECTORY: ./vehicle-routing diff --git a/shift-assignment/input1.json b/shift-assignment/input1.json new file mode 100644 index 0000000..61774bf --- /dev/null +++ b/shift-assignment/input1.json @@ -0,0 +1,137 @@ +{ + "workers": [ + { + "id": "Louis Hardy", + "qualifications": ["dairy"], + "rules": "standard", + "preferences": { + "dairy_monday-early": 1, + "dairy_monday-late": 1 + }, + "availability": [ + { + "start_time": "2023-11-20T00:00:00+02:00", + "end_time": "2023-11-21T00:00:00+02:00" + }, + { + "start_time": "2023-11-21T00:00:00+02:00", + "end_time": "2023-11-22T00:00:00+02:00" + } + ] + }, + { + "id": "Jacob Cunningham", + "qualifications": ["dairy"], + "rules": "standard", + "preferences": { + "dairy_monday-early": 1, + "normal_monday-early": 1 + }, + "availability": [ + { + "start_time": "2023-11-20T00:00:00+02:00", + "end_time": "2023-11-21T00:00:00+02:00" + }, + { + "start_time": "2023-11-21T00:00:00+02:00", + "end_time": "2023-11-22T00:00:00+02:00" + } + ] + }, + { + "id": "Ray Heard", + "qualifications": ["dairy"], + "rules": "standard", + "preferences": { + "dairy_monday-early": 1, + "normal_monday-early": 1 + }, + "availability": [ + { + "start_time": "2023-11-20T00:00:00+02:00", + "end_time": "2023-11-21T00:00:00+02:00" + }, + { + "start_time": "2023-11-21T00:00:00+02:00", + "end_time": "2023-11-22T00:00:00+02:00" + } + ] + }, + { + "id": "Mark Leath", + "qualifications": [], + "rules": "standard", + "preferences": { + "normal_monday-late": 1 + }, + "availability": [ + { + "start_time": "2023-11-20T00:00:00+02:00", + "end_time": "2023-11-21T00:00:00+02:00" + }, + { + "start_time": "2023-11-21T00:00:00+02:00", + "end_time": "2023-11-22T00:00:00+02:00" + } + ] + }, + { + "id": "Dylan Mccormack", + "qualifications": [], + "rules": "standard", + "preferences": { + "normal_monday-late": 3 + }, + "availability": [ + { + "start_time": "2023-11-20T00:00:00+02:00", + "end_time": "2023-11-21T00:00:00+02:00" + }, + { + "start_time": "2023-11-21T00:00:00+02:00", + "end_time": "2023-11-22T00:00:00+02:00" + } + ] + } + ], + "rules": [ + { + "id": "standard", + "min_rest_hours_between_shifts": 11 + } + ], + "shifts": [ + { + "id": "dairy_monday-early", + "time_id": "monday-early", + "start_time": "2023-11-20T06:00:00+02:00", + "end_time": "2023-11-20T14:00:00+02:00", + "qualification": "dairy", + "count": 1 + }, + { + "id": "dairy_monday-late", + "time_id": "monday-late", + "start_time": "2023-11-20T14:00:00+02:00", + "end_time": "2023-11-20T22:00:00+02:00", + "qualification": "dairy", + "count": 1 + }, + { + "id": "normal_monday-early", + "time_id": "monday-early", + "start_time": "2023-11-20T06:00:00+02:00", + "end_time": "2023-11-20T14:00:00+02:00", + "qualification": "", + "count": 2 + }, + { + "id": "normal_monday-late", + "time_id": "monday-late", + "start_time": "2023-11-20T14:00:00+02:00", + "end_time": "2023-11-20T22:00:00+02:00", + "qualification": "", + "count": 1 + } + ] +} diff --git a/shift-assignment/input2.json b/shift-assignment/input2.json new file mode 100644 index 0000000..674fd0a --- /dev/null +++ b/shift-assignment/input2.json @@ -0,0 +1,137 @@ +{ + "workers": [ + { + "id": "Louis Hardy", + "qualifications": ["dairy"], + "rules": "standard", + "preferences": { + "dairy_monday-early": 1, + "dairy_monday-late": 1 + }, + "availability": [ + { + "start_time": "2024-11-20T00:00:00+02:00", + "end_time": "2024-11-21T00:00:00+02:00" + }, + { + "start_time": "2024-11-21T00:00:00+02:00", + "end_time": "2024-11-22T00:00:00+02:00" + } + ] + }, + { + "id": "Jacob Cunningham", + "qualifications": ["dairy"], + "rules": "standard", + "preferences": { + "dairy_monday-early": 1, + "normal_monday-early": 1 + }, + "availability": [ + { + "start_time": "2023-11-20T00:00:00+02:00", + "end_time": "2023-11-21T00:00:00+02:00" + }, + { + "start_time": "2023-11-21T00:00:00+02:00", + "end_time": "2023-11-22T00:00:00+02:00" + } + ] + }, + { + "id": "Ray Heard", + "qualifications": ["dairy"], + "rules": "standard", + "preferences": { + "dairy_monday-early": 1, + "normal_monday-early": 1 + }, + "availability": [ + { + "start_time": "2023-11-20T00:00:00+02:00", + "end_time": "2023-11-21T00:00:00+02:00" + }, + { + "start_time": "2023-11-21T00:00:00+02:00", + "end_time": "2023-11-22T00:00:00+02:00" + } + ] + }, + { + "id": "Mark Leath", + "qualifications": [], + "rules": "standard", + "preferences": { + "normal_monday-late": 1 + }, + "availability": [ + { + "start_time": "2023-11-20T00:00:00+02:00", + "end_time": "2023-11-21T00:00:00+02:00" + }, + { + "start_time": "2023-11-21T00:00:00+02:00", + "end_time": "2023-11-22T00:00:00+02:00" + } + ] + }, + { + "id": "Dylan Mccormack", + "qualifications": [], + "rules": "standard", + "preferences": { + "normal_monday-late": 3 + }, + "availability": [ + { + "start_time": "2023-11-20T00:00:00+02:00", + "end_time": "2023-11-21T00:00:00+02:00" + }, + { + "start_time": "2023-11-21T00:00:00+02:00", + "end_time": "2023-11-22T00:00:00+02:00" + } + ] + } + ], + "rules": [ + { + "id": "standard", + "min_rest_hours_between_shifts": 11 + } + ], + "shifts": [ + { + "id": "dairy_monday-early", + "time_id": "monday-early", + "start_time": "2023-11-20T06:00:00+02:00", + "end_time": "2023-11-20T14:00:00+02:00", + "qualification": "dairy", + "count": 1 + }, + { + "id": "dairy_monday-late", + "time_id": "monday-late", + "start_time": "2023-11-20T14:00:00+02:00", + "end_time": "2023-11-20T22:00:00+02:00", + "qualification": "dairy", + "count": 1 + }, + { + "id": "normal_monday-early", + "time_id": "monday-early", + "start_time": "2023-11-20T06:00:00+02:00", + "end_time": "2023-11-20T14:00:00+02:00", + "qualification": "", + "count": 2 + }, + { + "id": "normal_monday-late", + "time_id": "monday-late", + "start_time": "2023-11-20T14:00:00+02:00", + "end_time": "2023-11-20T22:00:00+02:00", + "qualification": "", + "count": 1 + } + ] +} diff --git a/shift-assignment/input3.json b/shift-assignment/input3.json new file mode 100644 index 0000000..61774bf --- /dev/null +++ b/shift-assignment/input3.json @@ -0,0 +1,137 @@ +{ + "workers": [ + { + "id": "Louis Hardy", + "qualifications": ["dairy"], + "rules": "standard", + "preferences": { + "dairy_monday-early": 1, + "dairy_monday-late": 1 + }, + "availability": [ + { + "start_time": "2023-11-20T00:00:00+02:00", + "end_time": "2023-11-21T00:00:00+02:00" + }, + { + "start_time": "2023-11-21T00:00:00+02:00", + "end_time": "2023-11-22T00:00:00+02:00" + } + ] + }, + { + "id": "Jacob Cunningham", + "qualifications": ["dairy"], + "rules": "standard", + "preferences": { + "dairy_monday-early": 1, + "normal_monday-early": 1 + }, + "availability": [ + { + "start_time": "2023-11-20T00:00:00+02:00", + "end_time": "2023-11-21T00:00:00+02:00" + }, + { + "start_time": "2023-11-21T00:00:00+02:00", + "end_time": "2023-11-22T00:00:00+02:00" + } + ] + }, + { + "id": "Ray Heard", + "qualifications": ["dairy"], + "rules": "standard", + "preferences": { + "dairy_monday-early": 1, + "normal_monday-early": 1 + }, + "availability": [ + { + "start_time": "2023-11-20T00:00:00+02:00", + "end_time": "2023-11-21T00:00:00+02:00" + }, + { + "start_time": "2023-11-21T00:00:00+02:00", + "end_time": "2023-11-22T00:00:00+02:00" + } + ] + }, + { + "id": "Mark Leath", + "qualifications": [], + "rules": "standard", + "preferences": { + "normal_monday-late": 1 + }, + "availability": [ + { + "start_time": "2023-11-20T00:00:00+02:00", + "end_time": "2023-11-21T00:00:00+02:00" + }, + { + "start_time": "2023-11-21T00:00:00+02:00", + "end_time": "2023-11-22T00:00:00+02:00" + } + ] + }, + { + "id": "Dylan Mccormack", + "qualifications": [], + "rules": "standard", + "preferences": { + "normal_monday-late": 3 + }, + "availability": [ + { + "start_time": "2023-11-20T00:00:00+02:00", + "end_time": "2023-11-21T00:00:00+02:00" + }, + { + "start_time": "2023-11-21T00:00:00+02:00", + "end_time": "2023-11-22T00:00:00+02:00" + } + ] + } + ], + "rules": [ + { + "id": "standard", + "min_rest_hours_between_shifts": 11 + } + ], + "shifts": [ + { + "id": "dairy_monday-early", + "time_id": "monday-early", + "start_time": "2023-11-20T06:00:00+02:00", + "end_time": "2023-11-20T14:00:00+02:00", + "qualification": "dairy", + "count": 1 + }, + { + "id": "dairy_monday-late", + "time_id": "monday-late", + "start_time": "2023-11-20T14:00:00+02:00", + "end_time": "2023-11-20T22:00:00+02:00", + "qualification": "dairy", + "count": 1 + }, + { + "id": "normal_monday-early", + "time_id": "monday-early", + "start_time": "2023-11-20T06:00:00+02:00", + "end_time": "2023-11-20T14:00:00+02:00", + "qualification": "", + "count": 2 + }, + { + "id": "normal_monday-late", + "time_id": "monday-late", + "start_time": "2023-11-20T14:00:00+02:00", + "end_time": "2023-11-20T22:00:00+02:00", + "qualification": "", + "count": 1 + } + ] +} diff --git a/shift-assignment/main.py b/shift-assignment/main.py index a0f8fdd..45053bd 100644 --- a/shift-assignment/main.py +++ b/shift-assignment/main.py @@ -1,11 +1,14 @@ import argparse import datetime import json +import logging import sys from typing import Any import pyomo.environ as pyo +logging.getLogger("pyomo.core").setLevel(logging.ERROR) + # Duration parameter for the solver. SUPPORTED_PROVIDER_DURATIONS = { "cbc": "sec", @@ -183,6 +186,7 @@ def solve( results = solver.solve(model) # Parse solution. + value = pyo.value(model.objective, exception=False) assigned_shifts = [] fixed_vars = 0 for worker in workers: @@ -203,11 +207,6 @@ def solve( active_workers = len(set(shift["worker_id"] for shift in assigned_shifts)) total_workers = len(workers) - try: - value = pyo.value(model.objective) - except ValueError: - value = "nan" - # Measure the time. end_time = datetime.datetime.now() @@ -225,7 +224,7 @@ def solve( "availability_usage": 100 * (active_workers / total_workers), }, "duration": results.solver.time, - "value": value, + "value": value if value is not None else "nan", }, "run": { "duration": (end_time - start_time).total_seconds(),