def has_good_lunch(self, lunch_duration, hours_wo_lunch): lunch_stops = [x for x in self.stops if x.stop_name in ("LUNCH", "Lunch")] if len(lunch_stops) > 1: print("Multiple lunches found!!") return False if len(lunch_stops) < 1: return False lunch_stop = lunch_stops[0] if lunch_stop.layover() < lunch_duration: # print("lunch found, but too short") return False start_time = self.stops[0].arrive_time stop_time = self.stops[-1].depart_time if dur(start_time, lunch_stop.arrive_time) > hours_wo_lunch * 60: # print("lunch found, is too late") return False if dur(lunch_stop.depart_time, stop_time) > hours_wo_lunch * 60: # print("lunch found, is too early") return False return True
def duration_postalized(self): # note this method adds individual travel times and layovers rather than comparing the first # and last times of the schedule to account for schedules over 24 hours duration = 0 for x, stop in enumerate(self.stops_postalized[:-1]): next_stop = self.stops[x + 1] duration += stop.layover() duration += dur(stop.depart_time, next_stop.arrive_time) duration += self.stops[-1].layover() return duration
def layover(self): return dur(self.arrive_time, self.depart_time)
def add_a_lunch(self, input_passer): hours_wo_lunch = input_passer.hours_wo_lunch lunch_duration = input_passer.lunch_duration allow_extension = input_passer.allow_extension allow_non_postal = input_passer.allow_non_postal lunch_travel_time = input_passer.lunch_travel_time lunch_buffer_time = input_passer.lunch_buffer_time total_buffer = lunch_travel_time + lunch_buffer_time duration = self.duration_current() if duration < hours_wo_lunch * 60: return True if self.has_good_lunch(lunch_duration, hours_wo_lunch): return True existing_lunch = [x for x in self.stops if x.stop_name in ("LUNCH", "Lunch")] if len(existing_lunch) > 0: # print("already tried best we could") return False start_time = self.stops[0].arrive_time stop_time = self.stops[-1].depart_time max_td = timedelta(hours=hours_wo_lunch) one_day = timedelta(hours=24) start_dt = time_to_datetime(start_time) stop_dt = time_to_datetime(stop_time) if stop_dt < start_dt: stop_dt += one_day earliest_possible_lunch_dt = stop_dt - max_td latest_possible_lunch_dt = start_dt + max_td if earliest_possible_lunch_dt > latest_possible_lunch_dt: print("Schedule " + self.schedule_name + " is too long for one lunch.") return False earliest_possible_time = earliest_possible_lunch_dt.time() latest_possible_time = latest_possible_lunch_dt.time() earliest_possible_minute = max(26, dur(start_time, earliest_possible_time)+total_buffer) latest_possible_minute = dur(start_time, latest_possible_time)-total_buffer first_start_time = self.stops[0].arrive_time postal_stops = [] extend_stops = [] non_postal_stops = [] extend_non_postal_stops = [] potential_stops = 0 for x, stop in enumerate(self.stops): start_minute = dur(first_start_time, stop.arrive_time) stop_minute = dur(first_start_time, stop.depart_time) layover_minutes = stop_minute - start_minute if stop_minute > earliest_possible_minute: if start_minute < latest_possible_minute: if stop.post_office_location: if layover_minutes >= (lunch_duration + 2*(lunch_travel_time + lunch_buffer_time)): postal_stops.append(x) potential_stops += 1 elif allow_extension: extend_stops.append(x) potential_stops += 1 elif allow_non_postal: if layover_minutes >= (lunch_duration + 2*(lunch_travel_time + lunch_buffer_time)): non_postal_stops.append(x) potential_stops += 1 elif allow_extension: extend_non_postal_stops.append(x) potential_stops += 1 if potential_stops == 0: # print("No eligible lunch stops found") return False # else: # print("Found " + str(potential_stops) + " eligible stops for lunch") if len(postal_stops) > 0: index = postal_stops[0] stop = self.stops[index] must_start_by = latest_possible_minute - dur(start_time, stop.arrive_time) + total_buffer # cant_finish_before = earliest_possible_minute - dur(start_time, stop.arrive_time) self.insert_lunch(index, lunch_duration, must_start_by, lunch_travel_time, lunch_buffer_time) elif len(non_postal_stops) > 0: index = non_postal_stops[0] stop = self.stops[index] must_start_by = latest_possible_minute - dur(start_time, stop.arrive_time) + total_buffer # cant_finish_before = earliest_possible_minute - dur(start_time, stop.arrive_time) self.insert_lunch(index, lunch_duration, must_start_by, lunch_travel_time, lunch_buffer_time) elif len(extend_stops) > 0: index = extend_stops[0] for x in extend_stops: if self.stops[x].layover() > self.stops[index].layover(): index = x orig_layover = self.stops[index].layover() required_layover = lunch_duration + 2*(lunch_travel_time + lunch_buffer_time) pushback_minutes = required_layover - orig_layover self.stops[index].change_depart_time(pushback_minutes) stop = self.stops[index] must_start_by = latest_possible_minute - dur(start_time, stop.arrive_time) + total_buffer # cant_finish_before = earliest_possible_minute - dur(start_time, stop.arrive_time) self.insert_lunch(index, lunch_duration, must_start_by, lunch_travel_time, lunch_buffer_time) for stop in self.stops[index+3:]: stop.shift_stop(pushback_minutes) for stop in self.stops: stop.shift_stop(-round(pushback_minutes/2, 0)) elif len(extend_non_postal_stops) > 0: index = extend_non_postal_stops[0] for x in extend_non_postal_stops: if self.stops[x].layover() > self.stops[index].layover(): index = x orig_layover = self.stops[index].layover() required_layover = lunch_duration + 2*(lunch_travel_time + lunch_buffer_time) pushback_minutes = required_layover - orig_layover self.stops[index].change_depart_time(pushback_minutes) stop = self.stops[index] must_start_by = latest_possible_minute - dur(start_time, stop.arrive_time) + total_buffer # cant_finish_before = earliest_possible_minute - dur(start_time, stop.arrive_time) self.insert_lunch(index, lunch_duration, must_start_by, lunch_travel_time, lunch_buffer_time) for stop in self.stops[index+3:]: stop.shift_stop(pushback_minutes) for stop in self.stops: stop.shift_stop(-round(pushback_minutes/2, 0)) else: print("wtf? eligible lunch stops both found and not found??") return True
def postal_compliance_check(self, input_passer): pvs_time = input_passer.pvs_time pdc_time = input_passer.pdc_time pvs_to_pdc = input_passer.pvs_to_pdc layover_time = pvs_time + pdc_time + pvs_to_pdc pvs_name = self.pvs_name pdc_name = self.pdc_name max_working_time = input_passer.max_duration lunch_duration = input_passer.lunch_duration hours_wo_lunch = input_passer.hours_wo_lunch # check all layovers longer than one minute for stop in self.stops: if not stop.is_good_stop(): return False # check all travel times at least one minute for x, stop in enumerate(self.stops[:-1]): next_stop = self.stops[x+1] travel_time = dur(stop.depart_time, next_stop.arrive_time) if travel_time < 1: return False # check the PVS and P&DC start situation start_check = False if self.same_name: if self.stops[0].stop_name == pvs_name: if self.stops[0].layover() == layover_time: start_check = True else: if self.stops[0].stop_name == pvs_name: if self.stops[1].stop_name == pdc_name: if self.stops[0].layover() >= pvs_time: if self.stops[1].layover() >= pdc_time: start_check = True if not start_check: return False # check stop PVS and P&DC stop_check = False if self.same_name: if self.stops[-1].stop_name == pvs_name: if self.stops[-1].layover() == layover_time: stop_check = True else: if self.stops[-1].stop_name == pvs_name: if self.stops[-2].stop_name == pdc_name: if self.stops[-1].layover() >= pvs_time: if self.stops[-2].layover() >= pdc_time: stop_check = True if not stop_check: return False # check lunch situation break_time = 0 lunch_check = False duration = self.duration_postalized() if duration < hours_wo_lunch * 60: lunch_check = True elif duration <= lunch_duration + hours_wo_lunch * 120: if self.has_good_lunch(lunch_duration, hours_wo_lunch): lunch_check = True lunch_stop = next(x for x in self.stops if x.stop_name in ("LUNCH", "Lunch")) break_time += lunch_stop.layover() else: print("too long for lunch") if not lunch_check: return False work_time = duration - break_time if work_time > max_working_time * 60: return False return True