def duty_day_classifier(duty_day): """Given a DutyDay, this classifier will insert the following tags: - regular : any continental flight under 10:00 duty time. It is the default value - transoceanic : MAD, CDG, MXP, FCO, LHR, AMS, SVO, BCN, MUC, FRA, NRT, ICN, PVG, PEK - special transoceanic : a) MUST be transoceanic b) scheduled block time > 13:00 hrs c) scheduled duty time > 15:00 hrs - long haul : Any flight with a) #of legs <= 2 b) AND at least one leg > 4:30 hrs c) AND - OR BLK > 10:00 - OR DUTY > 12:00 - OR DUTY > 09:30 AND DUTY.OVERLAPS(00:59, 04:59) inclusive """ # TODO : I don't like the way this looks if set([airport.iata_code for airport in duty_day._credits['routing'] ]).intersection(TRANSOCEANIC): duty_day._credits['duty_type'] = 'transoceanic' if (duty_day._credits['total'] ) > MINIMUM_BLOCK or duty_day._credits['daily'] > MINIMUM_DUTY: duty_day._credits['duty_type'] = 'special trans' elif len(duty_day.events) <= 2: for event in duty_day.events: if event.duration > Duration(4 * 60 + 30): if duty_day._credits['block'] > Duration(10 * 60) or \ duty_day._credits['daily'] > Duration(12 * 60) or \ (duty_day._credits['daily'] > Duration(9 * 60 + 30) and Creditator.has_early_morning_time(duty_day)): duty_day._credits['duty_type'] = 'long haul' break
def credits_from_line(self, line): """Returns a credits_list with all credits for given line""" # 1. Calculate pending credits for line DESC 7DAT HDAY line._credits.update({ 'day': '', 'routing': 'TOTALS', 'report': '', 'release': '', 'duty_type': '', 'event_names': '', 'daily': Duration(0), 'block': Duration(0), 'dh': Duration(0), 'night': Duration(0), 'xduty': Duration(0), 'xblock': Duration(0), 'maxirre': Duration(0), 'delay': Duration(0), 'pending_rest': Duration(0), 'xturn': Duration(0), 'sunday': 0, '7day': 0 }) # 2 Add all credits to find totals line_credits_list = [] consecutive_days = ConsecutiveDays(line.duties[0].report.date(), 7) for duty in line.duties: credit_list = duty.compute_credits(self) if credit_list: line_credits_list.extend(credit_list) line._credits['block'] += duty._credits['block'] line._credits['dh'] += duty._credits['dh'] line._credits['daily'] += duty._credits['daily'] line._credits['night'] += duty._credits['night'] line._credits['xblock'] += duty._credits['xblock'] line._credits['xduty'] += duty._credits['xduty'] line._credits['maxirre'] += duty._credits['maxirre'] line._credits['delay'] += duty._credits['delay'] line._credits['pending_rest'] += duty._credits['pending_rest'] line._credits['xturn'] += duty._credits['xturn'] line._credits['sunday'] += duty._credits['sunday'] consecutive_days.calculate(duty) # 3 How many 7day do we have? days7 = consecutive_days.dates line._credits['7day'] += len(days7) # 4 Assign the needed template to print credits line._credits['header'] = line_credits_header line._credits['template'] = line_credits_template return line_credits_list
def calculate_night_time(event): """ Returns the nighttime flown in a given event, value is returned in seconds """ BEGIN = event.begin.hour * 60 + event.begin.minute END = event.end.hour * 60 + event.end.minute NIGHTTIME_BEGIN = 22 * 60 NIGHTTIME_END = 5 * 60 MIDNIGHT = 24 * 60 morninginter = [0, NIGHTTIME_END] nightinter = [NIGHTTIME_BEGIN, MIDNIGHT] if BEGIN > END: # Event starts and ends in different days total = Creditator.overlapping( nightinter, [BEGIN, MIDNIGHT]) + Creditator.overlapping( morninginter, [0, END]) else: # Event starts and ends in same day total = Creditator.overlapping( morninginter, [BEGIN, END]) + Creditator.overlapping( nightinter, [BEGIN, END]) return Duration(total)
def credits_from_duty_day(duty_day): """ Returns """ # 1. Init our _credits duty_day._credits = CreditsDict() duty_day._credits.update({ 'day': duty_day.begin.day, 'routing': FormattedList([duty_day.origin]), 'report': duty_day.report, 'release': duty_day.release, 'duty_type': 'regular', 'event_names': FormattedList(['']), 'daily': duty_day.duration, 'block': Duration(0), 'dh': Duration(0), 'night': Duration(0), 'xduty': Duration(0), 'xblock': Duration(0), 'maxirre': Duration(0), 'delay': duty_day.delay, 'pending_rest': Duration(0), 'xturn': Duration(0), 'sunday': 0 }) # 2. Calculate night, event_names and routing for event in duty_day.events: event.compute_credits() #DH flights don't account for night time if isinstance(event, Flight) and not event.dh: duty_day._credits['night'] += Creditator.calculate_night_time( event) duty_day._credits['block'] += event._credits['block'] duty_day._credits['dh'] += event._credits['dh'] duty_day._credits['event_names'].append(event.name) duty_day._credits['routing'].append(event.route.destination) duty_day._credits[ 'total'] = duty_day._credits['block'] + duty_day._credits['dh'] # 3. Calculate xturn: for turn in duty_day.turns: duty_day._credits['xturn'] += (turn - MAX_TURN_TIME) # 4. Classify duty Creditator.duty_day_classifier(duty_day) if duty_day._credits['duty_type'] == 'regular': duty_day._credits['xblock'] = duty_day._credits[ 'block'] - JORNADA_ORDINARIA_VUELO_REGULAR duty_day._credits['xduty'] = duty_day._credits[ 'daily'] - JORNADA_ORDINARIA_SERVICIO_REGULAR duty_day._credits['maxirre'] = duty_day._credits[ 'daily'] - MAXIMA_IRREBASABLE_SERVICIO_REGULAR elif duty_day._credits['duty_type'] == 'transoceanic': duty_day._credits['xblock'] = duty_day._credits[ 'block'] - JORNADA_ORDINARIA_VUELO_TRAN duty_day._credits['xduty'] = duty_day._credits[ 'daily'] - JORNADA_ORDINARIA_SERVICIO_TRAN duty_day._credits['maxirre'] = duty_day._credits[ 'daily'] - MAXIMA_IRREBASABLE_SERVICIO_TRAN elif duty_day._credits['duty_type'] == 'special trans': duty_day._credits['xblock'] = duty_day._credits[ 'block'] - JORNADA_ORDINARIA_VUELO_TRANSP duty_day._credits['xduty'] = duty_day._credits[ 'daily'] - JORNADA_ORDINARIA_SERVICIO_TRANSP duty_day._credits['maxirre'] = duty_day._credits[ 'daily'] - MAXIMA_IRREBASABLE_SERVICIO_TRANSP # If there is a maxirre, normal xduty time ends where maxirre starts elif duty_day._credits['duty_type'] == 'long haul': duty_day._credits['xblock'] = duty_day._credits[ 'block'] - JORNADA_ORDINARIA_VUELO_LARGO_ALCANCE duty_day._credits['xduty'] = duty_day._credits[ 'daily'] - JORNADA_ORDINARIA_SERVICIO_LARGO_ALCANCE duty_day._credits['maxirre'] = duty_day._credits[ 'daily'] - MAXIMA_ASIGNABLE_SERVICIO_LARGO_ALCANCE # If there is a maxirre, normal xduty time ends where maxirre starts if duty_day._credits['maxirre'] > Duration(0): duty_day._credits['xduty'] = Duration(5 * 60) # 5. If there is a maxirre, normal xduty time ends where maxirre starts if duty_day._credits['maxirre'] > Duration(0): duty_day._credits['xduty'] = Duration(5 * 60) # 6. How many sundays? duty_day._credits['sunday'] = duty_day.how_many_sundays() duty_day_release_time = Duration(duty_day.release.hour * 60 + duty_day.release.minute) duty_day_ending_weekday = duty_day.release.isoweekday() is_a_worked_day = duty_day_release_time > WORKED_DAY if duty_day_ending_weekday == 7 and not is_a_worked_day: duty_day._credits['sunday'] -= 1 # 7. Assign the needed template to print credits duty_day._credits['header'] = duty_day_credits_header duty_day._credits['template'] = duty_day_credits_template
def credits_from_trip(self, trip): """Returns a list of all duty_day_credits_dict within the trip_match""" credits_list = [] trip._credits = CreditsDict() # 1 Add all credits to find totals trip._credits['block'] = Duration(0) trip._credits['dh'] = Duration(0) trip._credits['daily'] = Duration(0) trip._credits['night'] = Duration(0) trip._credits['xblock'] = Duration(0) trip._credits['xduty'] = Duration(0) trip._credits['maxirre'] = Duration(0) trip._credits['delay'] = Duration(0) trip._credits['pending_rest'] = Duration(0) trip._credits['xturn'] = Duration(0) trip._credits['sunday'] = 0 for duty_day in trip.duty_days: if duty_day.begin.month == self.month_scope: duty_day.compute_credits(self) trip._credits['block'] += duty_day._credits['block'] trip._credits['dh'] += duty_day._credits['dh'] trip._credits['daily'] += duty_day._credits['daily'] trip._credits['night'] += duty_day._credits['night'] trip._credits['xblock'] += duty_day._credits['xblock'] trip._credits['xduty'] += duty_day._credits['xduty'] trip._credits['maxirre'] += duty_day._credits['maxirre'] trip._credits['delay'] += duty_day._credits['delay'] trip._credits['xturn'] += duty_day._credits['xturn'] trip._credits['sunday'] += duty_day._credits['sunday'] credits_list.append(duty_day._credits) # 2. Calculate pending_rest between each duty day for rest, duty_day in zip(trip.rests, trip.duty_days): if (duty_day.begin.month == self.month_scope) and rest < Duration(12 * 60): tempo = Creditator.calculate_pending_rest(rest) duty_day._credits['pending_rest'] = tempo trip._credits['pending_rest'] += duty_day._credits[ 'pending_rest'] # 3. How many sundays? trip._credits['sunday'] = trip.how_many_sundays() trip_release_time = Duration(duty_day.release.hour * 60 + duty_day.release.minute) trip_ending_weekday = duty_day.release.isoweekday() is_a_worked_day = trip_release_time > WORKED_DAY if trip_ending_weekday == 7 and not is_a_worked_day: trip._credits['sunday'] -= 1 # 4 Assign the needed template to print credits trip._credits['header'] = trip_credits_header trip._credits['template'] = trip_credits_template return credits_list
def test_as_timedelta(): td = timedelta(minutes=30) d = Duration.from_timedelta(td) assert isinstance(d, Duration) assert td.total_seconds() == 30 * 60
""" Created on 23/06/2016 @author: Xico """ from datetime import timedelta # TODO : Move all this constants into a configuration file, associated to method set_rules from models.scheduleclasses import Flight from models.timeclasses import Duration TRANSOCEANIC = [ 'MAD', 'CDG', 'MXP', 'FCO', 'LHR', 'AMS', 'SVO', 'BCN', 'MUC', 'FRA', 'NRT', 'ICN', 'PVG', 'PEK' ] MINIMUM_BLOCK = Duration(13 * 60) MINIMUM_DUTY = Duration(15 * 60) WORKED_DAY = Duration(1 * 60 + 30) # TABLA 1 Jornada continental JORNADA_ORDINARIA_VUELO_REGULAR = Duration(7 * 60 + 30) MAXIMA_IRREBASABLE_VUELO_REGULAR = Duration(10 * 60) JORNADA_ORDINARIA_SERVICIO_REGULAR = Duration(9 * 60) MAXIMA_IRREBASABLE_SERVICIO_REGULAR = Duration(12 * 60) # TABLA 1 Jornada transoceanica JORNADA_ORDINARIA_VUELO_TRAN = Duration(8 * 60) MAXIMA_IRREBASABLE_VUELO_TRAN = Duration(13 * 60) JORNADA_ORDINARIA_SERVICIO_TRAN = Duration(10 * 60) MAXIMA_IRREBASABLE_SERVICIO_TRAN = Duration(15 * 60)
def test_duration_formatting_zero_hide(self): d = Duration(0) expected = 4 * ' ' obtained = "{0:<4H}".format(d) assert obtained == expected
def test_duration_rep_method(self): d = Duration(23 * 60 + 30) expected = "Duration({})".format(23 * 60 + 30) obtained = d.__repr__() assert obtained == expected
def test_duration_formatting_no_colon(self): d = Duration(23 * 60 + 30) expected = "2330" obtained = "{0:<4}".format(d) assert expected == obtained
def test_duration_formatting_zero_show(self): d = Duration(0) expected = '0000' obtained = "{0:0<4}".format(d) assert obtained == expected
def test_duration_formatting_default(self): d = Duration(23 * 60 + 30) expected = "23:30" obtained = "{0:<5:}".format(d) assert expected == obtained
def test_init(self, minutes): duration = Duration(minutes) assert isinstance(duration, Duration) assert duration.minutes == minutes
def setUp(self): self.duration = Duration(minutes=40) self.td = timedelta(minutes=40)
def test_from_string(self): duration = Duration.from_string('0040') self.assertEqual(duration, self.duration)
def test_from_timedelta(self): duration = Duration.from_timedelta(self.td) self.assertEqual(self.duration, duration)