diff --git a/config/acl.yaml b/config/acl.yaml index d39b6be3..71a4918a 100644 --- a/config/acl.yaml +++ b/config/acl.yaml @@ -430,6 +430,14 @@ access_keys: label: Menu - id: analytics.forecast label: Forecasting + - id: shift_schedule.list + label: List + - id: shift_schedule.add + label: Add + - id: shift_schedule.update + label: Update + - id: shift_schedule.delete + label: Delete - id: sap_battery label: SAP Battery Access diff --git a/config/menu.yaml b/config/menu.yaml index d90ba3da..67b41bbc 100644 --- a/config/menu.yaml +++ b/config/menu.yaml @@ -216,3 +216,7 @@ main_menu: acl: analytics.forecast label: Forecasting parent: analytics + - id: shift_schedule_list + acl: shift_schedule.list + label: Shift Schedule + parent: analytics diff --git a/config/routes/shift_schedule.yaml b/config/routes/shift_schedule.yaml new file mode 100644 index 00000000..4b1a2d95 --- /dev/null +++ b/config/routes/shift_schedule.yaml @@ -0,0 +1,33 @@ +shift_schedule_list: + path: /shift_schedules + controller: App\Controller\ShiftScheduleController::index + +shift_schedule_rows: + path: /shift_schedules/rows + controller: App\Controller\ShiftScheduleController::rows + methods: [POST] + +shift_schedule_create: + path: /shift_schedules/create + controller: App\Controller\ShiftScheduleController::addForm + methods: [GET] + +shift_schedule_create_submit: + path: /shift_schedules/create + controller: App\Controller\ShiftScheduleController::addSubmit + methods: [POST] + +shift_schedule_update: + path: /shift_schedules/{id} + controller: App\Controller\ShiftScheduleController::updateForm + methods: [GET] + +shift_schedule_update_submit: + path: /shift_schedules/{id} + controller: App\Controller\ShiftScheduleController::updateSubmit + methods: [POST] + +shift_schedule_delete: + path: /shift_schedules/{id} + controller: App\Controller\ShiftScheduleController::destroy + methods: [DELETE] diff --git a/src/Controller/AnalyticsController.php b/src/Controller/AnalyticsController.php index d18b10bb..68e703ed 100644 --- a/src/Controller/AnalyticsController.php +++ b/src/Controller/AnalyticsController.php @@ -21,8 +21,7 @@ use DateInterval; use App\Entity\JobOrder; use App\Entity\Hub; - -use App\Ramcar\ShiftSchedule; +use App\Entity\ShiftSchedule; class AnalyticsController extends Controller { @@ -93,10 +92,19 @@ class AnalyticsController extends Controller $hub_list[$hub->getID()] = $hub->getName() . ' - ' . $hub->getBranch(); } + // get saved shift schedules + $all_shift_schedules = $em->getRepository(ShiftSchedule::class)->findAll(); + + $shift_sched_list = []; + foreach ($all_shift_schedules as $sched) + { + $shift_sched_list[$sched->getID()] = $sched->getName(); + } + $params = [ 'hub_list' => $hub_list, 'default_hubs' => $hub_ids, - 'shift_schedules' => ShiftSchedule::getCollection(), + 'shift_schedules' => $shift_sched_list, ]; return $this->render('analytics/forecast_form.html.twig', $params); @@ -123,8 +131,9 @@ class AnalyticsController extends Controller $shift = $req->request->get('shift_schedule'); // TODO: populate the hour_shift array, depending on the shift selected - $hour_shifts = $this->populateHourShift($shift); + $hour_shifts = $this->populateHourShift($em, $shift); + error_log('reference'); error_log(print_r($hour_shifts, true)); // error_log(print_r($hub_list, true)); @@ -177,7 +186,7 @@ class AnalyticsController extends Controller // error_log(print_r($scheduler_data, true)); // run scheduler - $sched_res = $this->runScheduler($scheduler_data, $hour_shifts, $shift); + $sched_res = $this->runScheduler($scheduler_data, $hour_shifts); // tally total JOs for the month foreach ($scheduler_data as $sday_data) @@ -252,7 +261,7 @@ class AnalyticsController extends Controller return $day_data; } - protected function runScheduler($scheduler_data, $hour_shifts, $shift) + protected function runScheduler($scheduler_data, $hour_shifts) { // run python script to solve scheduling for riders @@ -267,8 +276,37 @@ class AnalyticsController extends Controller foreach ($scheduler_data as $weekday_data) $args[] = implode('-', $weekday_data); - // add shift - $args[] = $shift; + // add length of hour_shifts + $args[] = count($hour_shifts); + + // form hour_shifts argument for the python script + foreach ($hour_shifts as $hour_shift) + { + $shift = ''; + foreach ($hour_shift as $entry) + { + // check if entry has format '00:00 - 00:00' + if (strpos($entry, ':')) + { + // need to modify to 00 - 00 + $hours = explode('-', $entry); + $start = trim($hours[0]); + $end = trim($hours[1]); + + // get the hour only + $s_parts = explode(':', $start); + $e_parts = explode(':', $end); + $start_hour = trim($s_parts[0]); + $end_hour = trim($s_parts[1]); + + $shift = '\'' . $start_hour . ' - ' . $end_hour . '\''; + } + else + $shift = $shift . ',' . $entry; + } + + $args[] = $shift; + } //error_log(print_r($args, true)); @@ -676,55 +714,51 @@ class AnalyticsController extends Controller { } - protected function populateHourShift($shift) + protected function populateHourShift(EntityManagerInterface $em, $id) { $hour_shift = []; - if ($shift == '24_7') { - $hour_shift = [ - ['00:00 - 09:00', 0, 1, 2, 3, 4, 5, 6, 7, 8], - ['01:00 - 10:00', 1, 2, 3, 4, 5, 6, 7, 8, 9], - ['02:00 - 11:00', 2, 3, 4, 5, 6, 7, 8, 9, 10], - ['03:00 - 12:00', 3, 4, 5, 6, 7, 8, 9, 10, 11], - ['04:00 - 13:00', 4, 5, 6, 7, 8, 9, 10, 11, 12], - ['05:00 - 14:00', 5, 6, 7, 8, 9, 10, 11, 12, 13], - ['06:00 - 15:00', 6, 7, 8, 9, 10, 11, 12, 13, 14], - ['07:00 - 16:00', 7, 8, 9, 10, 11, 12, 13, 14, 15], - ['08:00 - 17:00', 8, 9, 10, 11, 12, 13, 14, 15, 16], - ['09:00 - 18:00', 9, 10, 11, 12, 13, 14, 15, 16, 17], - ['10:00 - 19:00', 10, 11, 12, 13, 14, 15, 16, 17, 18], - ['11:00 - 20:00', 11, 12, 13, 14, 15, 16, 17, 18, 19], - ['12:00 - 21:00', 12, 13, 14, 15, 16, 17, 18, 19, 20], - ['13:00 - 22:00', 13, 14, 15, 16, 17, 18, 19, 20, 21], - ['14:00 - 23:00', 14, 15, 16, 17, 18, 19, 20, 21, 22], - ['15:00 - 00:00', 15, 16, 17, 18, 19, 20, 21, 22, 23], - ['16:00 - 01:00', 16, 17, 18, 19, 20, 21, 22, 23, 0], - ['17:00 - 02:00', 17, 18, 19, 20, 21, 22, 23, 0, 1], - ['18:00 - 03:00', 18, 19, 20, 21, 22, 23, 0, 1, 2], - ['19:00 - 04:00', 19, 20, 21, 22, 23, 0, 1, 2, 3], - ['20:00 - 05:00', 20, 21, 22, 23, 0, 1, 2, 3, 4], - ['21:00 - 06:00', 21, 22, 23, 0, 1, 2, 3, 4, 5], - ['22:00 - 07:00', 22, 23, 0, 1, 2, 3, 4, 5, 6], - ['23:00 - 08:00', 23, 0, 1, 2, 3, 4, 5, 6, 7], - ]; - } - if ($shift == '8AM_5PM') { - $hour_shift = [ - ['07:00 - 16:00', 7, 8, 9, 10, 11, 12, 13, 14, 15], - ['08:00 - 17:00', 8, 9, 10, 11, 12, 13, 14, 15, 16], - ]; - } + // get the hour shifts for the selected shift scheduled + $shift = $em->getRepository(ShiftSchedule::class)->find($id); - if ($shift == '7AM_10PM') { - $hour_shift = [ - ['07:00 - 16:00', 7, 8, 9, 10, 11, 12, 13, 14, 15], - ['08:00 - 17:00', 8, 9, 10, 11, 12, 13, 14, 15, 16], - ['09:00 - 18:00', 9, 10, 11, 12, 13, 14, 15, 16, 17], - ['10:00 - 19:00', 10, 11, 12, 13, 14, 15, 16, 17, 18], - ['11:00 - 20:00', 11, 12, 13, 14, 15, 16, 17, 18, 19], - ['12:00 - 21:00', 12, 13, 14, 15, 16, 17, 18, 19, 20], - ['13:00 - 22:00', 13, 14, 15, 16, 17, 18, 19, 20, 21] - ]; + if (empty($shift)) + throw $this->createNotFoundException('The item does not exist'); + + $shifts = $shift->getHourShifts(); + + foreach ($shifts as $shift) + { + $shift_entry = []; + // format of hour_shift: [['start time - end time', start time, ... , end time - 1]] + // first hour of hour shift is the start time + // form the first entry of the hour shift + $shift_time = $shift['start'] . ' - ' . $shift['end']; + + $shift_entry[] = $shift_time; + + $s_times = explode(':', $shift['start']); + $e_times = explode(':', $shift['end']); + + $shift_start = intval($s_times[0]); + $shift_end = intval($e_times[0]); + + if ($shift_start == 23) + { + $shift_entry[] = 23; + for ($hour = 0; $hour < $shift_end; $hour++) + { + $shift_entry[] = $hour; + } + } + for($hour = $shift_start; $hour < $shift_end; $hour++) + { + $shift_entry[] = $hour; + + if ($hour == 23) + $hour = 0; + } + + $hour_shift[] = $shift_entry; } return $hour_shift; diff --git a/src/Controller/ShiftScheduleController.php b/src/Controller/ShiftScheduleController.php new file mode 100644 index 00000000..0c5c2e8d --- /dev/null +++ b/src/Controller/ShiftScheduleController.php @@ -0,0 +1,329 @@ +denyAccessUnlessGranted('shift_schedule.list', null, 'No access.'); + + return $this->render('shift-schedule/list.html.twig'); + } + + public function rows(Request $req) + { + $this->denyAccessUnlessGranted('shift_schedule.list', null, 'No access.'); + + // get query builder + $qb = $this->getDoctrine() + ->getRepository(ShiftSchedule::class) + ->createQueryBuilder('q'); + + // get datatable params + $datatable = $req->request->get('datatable'); + + // count total records + $tquery = $qb->select('COUNT(q)'); + $this->setQueryFilters($datatable, $tquery); + $total = $tquery->getQuery() + ->getSingleScalarResult(); + + // get current page number + $page = $datatable['pagination']['page'] ?? 1; + + $perpage = $datatable['pagination']['perpage']; + $offset = ($page - 1) * $perpage; + + // add metadata + $meta = [ + 'page' => $page, + 'perpage' => $perpage, + 'pages' => ceil($total / $perpage), + 'total' => $total, + 'sort' => 'asc', + 'field' => 'id' + ]; + + // build query + $query = $qb->select('q'); + $this->setQueryFilters($datatable, $query); + + // check if sorting is present, otherwise use default + if (isset($datatable['sort']['field']) && !empty($datatable['sort']['field'])) { + $order = $datatable['sort']['sort'] ?? 'asc'; + $query->orderBy('q.' . $datatable['sort']['field'], $order); + } else { + $query->orderBy('q.id', 'asc'); + } + + // get rows for this page + $obj_rows = $query->setFirstResult($offset) + ->setMaxResults($perpage) + ->getQuery() + ->getResult(); + + // process rows + $rows = []; + foreach ($obj_rows as $orow) { + // add row data + $shifts = $orow->getHourShifts(); + $hour_shifts = []; + foreach ($shifts as $shift) + { + // convert to DateTime then format + $shift_start = DateTime::createFromFormat('H:i', $shift['start']); + $shift_end = DateTime::createFromFormat('H:i', $shift['end']); + + $hour_shifts[] = [ + $shift_start->format('g:i A') . ' - ' . $shift_end->format('g:i A') + ]; + } + + $row['id'] = $orow->getID(); + $row['name'] = $orow->getName(); + $row['start_time'] = $orow->getStartTime()->format('g:i A'); + $row['end_time'] = $orow->getEndtime()->format('g:i A'); + $row['hour_shifts'] = $hour_shifts; + + // add row metadata + $row['meta'] = [ + 'update_url' => '', + 'delete_url' => '' + ]; + + // add crud urls + if ($this->isGranted('shift_schedule.update')) + $row['meta']['update_url'] = $this->generateUrl('shift_schedule_update', ['id' => $row['id']]); + if ($this->isGranted('shift_schedule.delete')) + $row['meta']['delete_url'] = $this->generateUrl('shift_schedule_delete', ['id' => $row['id']]); + + $rows[] = $row; + } + + // response + return $this->json([ + 'meta' => $meta, + 'data' => $rows + ]); + } + + /** + * @Menu(selected="shift_schedule_list") + */ + public function addForm() + { + $this->denyAccessUnlessGranted('shift_schedule.add', null, 'No access.'); + + $params = []; + $params['obj'] = new ShiftSchedule(); + $params['mode'] = 'create'; + $params['shift_entries'] = []; + + // response + return $this->render('shift-schedule/form.html.twig', $params); + } + + public function addSubmit(Request $req, EntityManagerInterface $em, ValidatorInterface $validator) + { + $this->denyAccessUnlessGranted('shift_schedule.add', null, 'No access.'); + + $obj = new ShiftSchedule(); + + // set and save values + $this->setObject($obj, $em, $req); + + // validate + $errors = $validator->validate($obj); + + // initialize error list + $error_array = []; + + // add errors to list + foreach ($errors as $error) { + $error_array[$error->getPropertyPath()] = $error->getMessage(); + } + + // check if any errors were found + if (!empty($error_array)) { + // return validation failure response + return $this->json([ + 'success' => false, + 'errors' => $error_array + ], 422); + } + + // validated! save the entity + $em->persist($obj); + $em->flush(); + + // return successful response + return $this->json([ + 'success' => 'Changes have been saved!' + ]); + } + + /** + * @Menu(selected="shift_schedule_list") + */ + public function updateForm($id, EntityManagerInterface $em) + { + $this->denyAccessUnlessGranted('shift_schedule.update', null, 'No access.'); + + $params = []; + + // find shift schedule + $obj = $em->getRepository(ShiftSchedule::class)->find($id); + + // make sure this row exists + if (empty($obj)) + throw $this->createNotFoundException('The item does not exist'); + + // get the shift entries + $shifts = $obj->getHourShifts(); + $hour_shifts = []; + foreach ($shifts as $shift) + { + // convert to DateTime then format + $shift_start = DateTime::createFromFormat('H:i', $shift['start']); + $shift_end = DateTime::createFromFormat('H:i', $shift['end']); + + $hour_shifts[] = [ + 'start' => $shift_start, + 'end' => $shift_end + ]; + } + + $params['obj'] = $obj; + $params['mode'] = 'update'; + $params['shift_entries'] = $hour_shifts; + + // response + return $this->render('shift-schedule/form.html.twig', $params); + } + + public function updateSubmit(Request $req, EntityManagerInterface $em, ValidatorInterface $validator, $id) + { + $this->denyAccessUnlessGranted('shift_schedule.update', null, 'No access.'); + + // get object + $obj = $em->getRepository(ShiftSchedule::class)->find($id); + + // make sure this object exists + if (empty($obj)) + throw $this->createNotFoundException('The item does not exist'); + + $this->setObject($obj, $em, $req); + + // validate + $errors = $validator->validate($obj); + + // initialize error list + $error_array = []; + + // add errors to list + foreach ($errors as $error) { + $error_array[$error->getPropertyPath()] = $error->getMessage(); + } + + // check if any errors were found + if (!empty($error_array)) { + // return validation failure response + return $this->json([ + 'success' => false, + 'errors' => $error_array + ], 422); + } + + // validated! save the entity + $em->flush(); + + // return successful response + return $this->json([ + 'success' => 'Changes have been saved!' + ]); + } + + public function destroy($id, EntityManagerInterface $em) + { + $this->denyAccessUnlessGranted('shift_schedule.delete', null, 'No access.'); + + // get object data + $obj = $em->getRepository(ShiftSchedule::class)->find($id); + + if (empty($obj)) + throw $this->createNotFoundException('The item does not exist'); + + // delete this object + $em->remove($obj); + $em->flush(); + + // response + $response = new Response(); + $response->setStatusCode(Response::HTTP_OK); + $response->send(); + } + + protected function setQueryFilters($datatable, QueryBuilder $query) + { + if (isset($datatable['query']['data-rows-search']) && !empty($datatable['query']['data-rows-search'])) { + $query->where('q.name LIKE :filter') + ->setParameter('filter', '%' . $datatable['query']['data-rows-search'] . '%'); + } + } + + protected function setObject(ShiftSchedule $obj, EntityManagerInterface $em, Request $req) + { + $name = $req->request->get('name'); + + // times + $format = 'h:i A'; + $start_time = DateTime::createFromFormat($format, $req->request->get('start_time')); + $end_time = DateTime::createFromFormat($format, $req->request->get('end_time')); + + // get the shift entries + $shift_start = $req->request->get('shift_start_time'); + $shift_end = $req->request->get('shift_end_time'); + + $shifts = []; + for ($i = 0; $i < count($shift_start); $i++) + { + // convert to DateTime so that we can reformat to 00-23 format + $s_start = DateTime::createFromFormat($format, $shift_start[$i]); + $e_start = DateTime::createFromFormat($format, $shift_end[$i]); + + $start = $s_start->format('H:00'); + $end = $e_start->format('H:00'); + + $shifts[] = [ + 'start' => $start, + 'end' => $end + ]; + } + + $obj->setName($name) + ->setStartTime($start_time) + ->setEndTime($end_time) + ->setHourShifts($shifts); + } + +} diff --git a/src/Entity/ShiftSchedule.php b/src/Entity/ShiftSchedule.php new file mode 100644 index 00000000..1cdf7896 --- /dev/null +++ b/src/Entity/ShiftSchedule.php @@ -0,0 +1,121 @@ +start_time = new DateTime(); + $this->end_time = new DateTime(); + $this->hour_shifts = []; + } + + public function getID() + { + return $this->id; + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + return $this; + } + + public function getStartTime() + { + return $this->start_time; + } + + public function setStartTime(DateTime $start_time) + { + $this->start_time = $start_time; + return $this; + } + + public function getEndTime() + { + return $this->end_time; + } + + public function setEndTime(DateTime $end_time) + { + $this->end_time = $end_time; + return $this; + } + + public function addHourShift($id, $value) + { + $this->hour_shifts[$id] = $value; + return $this; + } + + public function getHourShiftsById($id) + { + // return null if we don't have it + if (!isset($this->hour_shifts[$id])) + return null; + + return $this->hour_shifts[$id]; + } + + public function getHourShifts() + { + return $this->hour_shifts; + } + + public function setHourShifts(array $hour_shifts) + { + $this->hour_shifts = $hour_shifts; + return $this; + } +} diff --git a/templates/shift-schedule/form.html.twig b/templates/shift-schedule/form.html.twig new file mode 100644 index 00000000..34629fd4 --- /dev/null +++ b/templates/shift-schedule/form.html.twig @@ -0,0 +1,254 @@ +{% extends 'base.html.twig' %} + +{% block body %} + +
+
+
+

Shift Schedules

+
+
+
+ +
+ +
+
+
+
+
+
+ + + +

+ {% if mode == 'update' %} + Edit Shift Schedule + {{ obj.getName() }} + {% else %} + New Shift Schedule + {% endif %} +

+
+
+
+
+
+
+
+ + + +
+
+
+
+ +
+ + + + +
+ +
+
+ +
+ + + + +
+ +
+
+
+
+
+

+ Breakdown of Shift Schedule +

+
+
+ +
+ + {% for shift_entry in shift_entries %} +
+
+
+ + + + +
+
+
+
+ + + + +
+
+
+ +
+
+ {% endfor %} +
+
+
+
+
+
+
+
+
+ + Back +
+
+
+
+ +
+ +{% endblock %} + + +{% block scripts %} + +{% endblock %} diff --git a/templates/shift-schedule/list.html.twig b/templates/shift-schedule/list.html.twig new file mode 100644 index 00000000..ecb8a05f --- /dev/null +++ b/templates/shift-schedule/list.html.twig @@ -0,0 +1,155 @@ +{% extends 'base.html.twig' %} + +{% block body %} + +
+
+
+

+ Shift Schedules +

+
+
+
+ +
+ +
+
+
+
+
+
+
+
+
+
+ + + + +
+
+
+
+ +
+
+ +
+ +
+
+
+
+
+{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/utils/schedule_solver/solver.py b/utils/schedule_solver/solver.py index abbdf49d..aa49d70c 100644 --- a/utils/schedule_solver/solver.py +++ b/utils/schedule_solver/solver.py @@ -47,48 +47,23 @@ def main(): for hour_index in range(0, len(hours)): req_hours[day_index][hour_index] = int(hours_data[hour_index]) - # index of shift selected is 8 - shift = sys.argv[8] + argv_index = day_index + 2 + # number of days is fixed at 7 so the number of shifts will be in index 8 + num_shifts = sys.argv[argv_index] + hour_shifts = [] - if shift == "24_7": - hour_shifts = [ - ['00 - 09', 0, 1, 2, 3, 4, 5, 6, 7, 8], - ['01 - 10', 1, 2, 3, 4, 5, 6, 7, 8, 9], - ['02 - 11', 2, 3, 4, 5, 6, 7, 8, 9, 10], - ['03 - 12', 3, 4, 5, 6, 7, 8, 9, 10, 11], - ['04 - 13', 4, 5, 6, 7, 8, 9, 10, 11, 12], - ['05 - 14', 5, 6, 7, 8, 9, 10, 11, 12, 13], - ['06 - 15', 6, 7, 8, 9, 10, 11, 12, 13, 14], - ['07 - 16', 7, 8, 9, 10, 11, 12, 13, 14, 15], - ['08 - 17', 8, 9, 10, 11, 12, 13, 14, 15, 16], - ['09 - 18', 9, 10, 11, 12, 13, 14, 15, 16, 17], - ['10 - 19', 10, 11, 12, 13, 14, 15, 16, 17, 18], - ['11 - 20', 11, 12, 13, 14, 15, 16, 17, 18, 19], - ['12 - 21', 12, 13, 14, 15, 16, 17, 18, 19, 20], - ['13 - 22', 13, 14, 15, 16, 17, 18, 19, 20, 21], - ['14 - 23', 14, 15, 16, 17, 18, 19, 20, 21, 22], - ['15 - 00', 15, 16, 17, 18, 19, 20, 21, 22, 23], - ['16 - 01', 16, 17, 18, 19, 20, 21, 22, 23, 0], - ['17 - 02', 17, 18, 19, 20, 21, 22, 23, 0, 1], - ['18 - 03', 18, 19, 20, 21, 22, 23, 0, 1, 2], - ['19 - 04', 19, 20, 21, 22, 23, 0, 1, 2, 3], - ['20 - 05', 20, 21, 22, 23, 0, 1, 2, 3, 4], - ['21 - 06', 21, 22, 23, 0, 1, 2, 3, 4, 5], - ['22 - 07', 22, 23, 0, 1, 2, 3, 4, 5, 6], - ['23 - 08', 23, 0, 1, 2, 3, 4, 5, 6, 7]] - if shift == "8AM_5PM": - hour_shifts = [ - ['07 - 16', 7, 8, 9, 10, 11, 12, 13, 14, 15], - ['08 - 17', 8, 9, 10, 11, 12, 13, 14, 15, 16]] - if shift == "7AM_10PM": - hour_shifts = [ - ['07 - 16', 7, 8, 9, 10, 11, 12, 13, 14, 15], - ['08 - 17', 8, 9, 10, 11, 12, 13, 14, 15, 16], - ['09 - 18', 9, 10, 11, 12, 13, 14, 15, 16, 17], - ['10 - 19', 10, 11, 12, 13, 14, 15, 16, 17, 18], - ['11 - 20', 11, 12, 13, 14, 15, 16, 17, 18, 19], - ['12 - 21', 12, 13, 14, 15, 16, 17, 18, 19, 20], - ['13 - 22', 13, 14, 15, 16, 17, 18, 19, 20, 21]] + for shift_ctr in range(0, int(num_shifts)): + # form list within the list + hour_shift_item = [] + + shift = sys.argv[argv_index+1] + + # append to list + hour_shift_item.append(shift) + + # append the list to the list + hour_shifts.append(hour_shift_item) + argv_index+=1 # all possible days riders come in day_shifts = [