messageboard-2021-06-18-1451.py
01234567890123456789012345678901234567890123456789012345678901234567890123456789









451452453454455456457458459460461462463464465466467468469470           471472473474475476477478479480481482483484485486487488489490








26672668266926702671267226732674267526762677267826792680268126822683268426852686     26872688268926902691269226932694269526962697269826992700270127022703270427052706








288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929








3926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980








64336434643564366437643864396440644164426443644464456446644764486449645064516452   64536454645564566457645864596460646164626463646464656466646764686469647064716472











                            <----SKIPPED LINES---->




AIRCRAFT_LENGTH['Gulfstream Aerospace Gulfstream V (twin-jet)'] = 29.4
AIRCRAFT_LENGTH['Hawker Beechcraft 4000 (twin-jet)'] = 21.08
AIRCRAFT_LENGTH['Honda HondaJet (twin-jet)'] = 12.99
AIRCRAFT_LENGTH['IAI Gulfstream G100 (twin-jet)'] = 16.94
AIRCRAFT_LENGTH['IAI Gulfstream G200 (twin-jet)'] = 18.97
AIRCRAFT_LENGTH['IAI Gulfstream G280 (twin-jet)'] = 20.3
AIRCRAFT_LENGTH['Learjet 35 (twin-jet)'] = 14.83
AIRCRAFT_LENGTH['Learjet 45 (twin-jet)'] = 17.68
AIRCRAFT_LENGTH['Learjet 60 (twin-jet)'] = 17.88
AIRCRAFT_LENGTH['McDonnell Douglas MD-11 (tri-jet)'] = 61.6
AIRCRAFT_LENGTH['Pilatus PC-12 (single-turboprop)'] = 14.4
AIRCRAFT_LENGTH['Raytheon Hawker 800 (twin-jet)'] = 15.60
AIRCRAFT_LENGTH['Raytheon Hawker 800XP (twin-jet)'] = 15.60
AIRCRAFT_LENGTH['Raytheon Hawker 850XP (twin-jet)'] = 15.60
AIRCRAFT_LENGTH['Raytheon Hawker 1000 (twin-jet)'] = 16.08
AIRCRAFT_LENGTH['Rockwell Turbo Commander 690 (twin-turboprop)'] = 11.22
for mixed_case_plane in list(AIRCRAFT_LENGTH.keys()):  # pylint: disable=C0201
  AIRCRAFT_LENGTH[mixed_case_plane.upper()] = AIRCRAFT_LENGTH[mixed_case_plane]
  AIRCRAFT_LENGTH.pop(mixed_case_plane)













def Log(message, file=None, rolling=None):
  """Write a message to a logfile along with a timestamp.

  Args:
    message: string message to write
    file: string representing file name and, if needed, path to the
      file to write to
    rolling: name of file that will keep only the last n files of file
  """
  # can't define as a default parameter because LOGFILE name is potentially
  # modified based on SIMULATION flag
  if not file:
    file = LOGFILE

  # special case: for the main logfile, we always keep a rolling log
  if not rolling and file == LOGFILE:
    rolling = ROLLING_LOGFILE

  try:




                            <----SKIPPED LINES---->




  extra_space = SPLITFLAP_CHARS_PER_LINE - sum([len(str(s)) for s in l])
  last_gap = round(extra_space / (len(l) - 1))
  return EvenlySpace([*l[:-2], str(l[-2]) + ' '*last_gap + str(l[-1])])


def RemoveParentheticals(s):
  """Removes all instances of () and the text contained within - from string."""
  if not s:
    return s
  if '(' in s and ')' in s:
    open_paren = s.find('(')
    close_paren = s.find(')')
  else:
    return s
  if close_paren < open_paren:
    return s
  s = s.replace(s[open_paren:close_paren+1], '').strip().replace('  ', ' ')
  return RemoveParentheticals(s)







def Ordinal(n):
  """Converts integer n to an ordinal string - i.e.: 2 -> 2nd; 5 -> 5th."""
  return '%d%s' % (n, 'tsnrhtdd'[(math.floor(n/10)%10 != 1)*(n%10 < 4)*n%10::4])


def Screenify(lines, splitflap):
  """Transforms a list of lines to a single text string for display / print.

  Given a list of lines that is a fully-formed message to send to the splitflap
  display, this function transforms the list of strings to a single string that
  is an easier-to-read and more faithful representation of how the message will
  be displayed. The transformations are to add blank lines to the message to
  make it consistent number of lines, and to add border to the sides & top /
  bottom of the message.

  Args:
    lines: list of strings that comprise the message
    splitflap: boolean, True if directed for splitflap display; false if
    directed to screen





                            <----SKIPPED LINES---->




              1 + percent_size_difference)):
      last_aircraft_bigger = True
      comparative_text = 'smaller'

    last_flight_time_string = DisplayTime(last_flight, '%b %-d')
    if this_aircraft and last_aircraft:
      if this_aircraft_bigger or last_aircraft_bigger:
        message = ('%s used a %s plane today compared with last, on %s '
                   '(%s @ %dft vs. %s @ %dft)' % (
                       this_flight_number, comparative_text,
                       last_flight_time_string,
                       RemoveParentheticals(this_aircraft),
                       this_aircraft_length*FEET_IN_METER,
                       RemoveParentheticals(last_aircraft),
                       last_aircraft_length*FEET_IN_METER))
      elif last_aircraft and this_aircraft and last_aircraft != this_aircraft:
        message = (
            '%s used a different aircraft today compared'
            ' with last, on %s (%s vs. %s)' % (
                this_flight_number, last_flight_time_string,
                RemoveParentheticals(this_aircraft),
                RemoveParentheticals(last_aircraft)))

  return message


def FlightInsightNthFlight(flights, hours=1, min_multiple_flights=2):
  """Generates string about seeing frequent flights to the same dest.

  Generates text of the following form for the "focus" flight in the data.
  - ASA1337 was the 4th flight to PHX in the last 53 minutes, served by Alaska
    Airlines, American Airlines, Southwest and United
  - SWA3102 was the 2nd flight to SAN in the last 25 minutes, both with
    Southwest

  Args:
    flights: the list of the raw data from which the insights will be generated,
      where the flights are listed in order of observation - i.e.: flights[0]
      was the earliest seen, and flights[-1] is the most recent flight for
      which we are attempting to generate an insight.
    hours: the time horizon over which to look for flights with the same
      destination.




                            <----SKIPPED LINES---->




      overall_stats_elements = []
      if delay_early_count:
        overall_stats_elements.append('%d ER' % delay_early_count)
      if delay_ontime_count:
        overall_stats_elements.append('%d OT' % delay_ontime_count)
      if delay_late_count:
        overall_stats_elements.append('%d LT' % delay_late_count)
      if delay_unknown_count:
        overall_stats_elements.append('%d UNK' % delay_unknown_count)
      overall_stats_text = '; '.join(overall_stats_elements)

      days_history = (int(
          round(last_timestamp - relevant_flights[0]['now']) / SECONDS_IN_DAY)
                      + 1)

      late_percentage = delay_late_count / len(relevant_flights)

      if (superlative and
          delay_late_avg_sec >= min_average_delay_minutes * SECONDS_IN_MINUTE):
        message = (
            'This %s delay is the %s %s has seen in the '
            'last %d days (avg delay is %s); overall stats: %s' % (
                SecondsToHhMm(this_delay_seconds),
                delay_keyword,
                this_flight_number,
                days_history,
                SecondsToHhMm(delay_late_avg_sec),
                overall_stats_text))
      elif (late_percentage > min_late_percentage and
            delay_late_avg_sec >=
            min_average_delay_minutes * SECONDS_IN_MINUTE):
        # it's just been delayed frequently!
        message = (
            'With today''s delay of %s, %s is delayed %d%% of the time in'
            ' the last %d days for avg delay of %s; overall stats: %s' % (
                SecondsToHhMm(this_delay_seconds),
                this_flight_number,
                int(100 * late_percentage),
                days_history,
                SecondsToHhMm(delay_late_avg_sec),
                overall_stats_text))
  return message


def FlightInsights(flights):
  """Identifies all the insight messages about the most recently seen flight.

  Generates a possibly-empty list of messages about the flight.

  Args:
    flights: List of all flights where the last flight in the list is the focus
    flight for which we are trying to identify something interesting.

  Returns:
    List of 2-tuples, where the first element in the tuple is a flag indicating




                            <----SKIPPED LINES---->




    message_queue: FIFO list of message tuples of (message type,
      message string).
    next_message_time: epoch at which next message should be displayed
    configuration: dictionary of configuration attributes.
    screens: List of past screens displayed to splitflap screen.

  Returns:
    Next_message_time, potentially updated if a message has been displayed,
    or unchanged if no message was displayed.
  """
  if message_queue and (time.time() >= next_message_time or SIMULATION):

    if SIMULATION:  # drain the queue because the messages come so fast
      messages_to_display = list(message_queue)
      # passed by reference, so clear it out since we drained it to the display
      del message_queue[:]
    else:  # display only one message, being mindful of the display timing
      messages_to_display = [message_queue.pop(0)]

    for message in messages_to_display:



      message_type = message[0]
      message_text = message[1]

      # There may be one or several insight messages that were added to the
      # message queue along with the flight at a time when the screen was
      # enabled, but by the time it comes to display them, the screen is now
      # disabled.  These should not be displayed.  Note that this check only
      # needs to be done for insight messages because other message types
      # are user initiated and so presumably should be displayed irrespective
      # of when the user triggered it to be displayed.
      if message_type == FLAG_MSG_INSIGHT and not MessageMeetsDisplayCriteria(
          configuration):
        Log('Message %s purged')

      else:
        if isinstance(message_text, str):
          message_text = textwrap.wrap(
              message_text,
              width=SPLITFLAP_CHARS_PER_LINE)
        display_message = Screenify(message_text, False)




                            <----SKIPPED LINES---->





01234567890123456789012345678901234567890123456789012345678901234567890123456789









451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501








267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722








290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945








3942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996








6449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491











                            <----SKIPPED LINES---->




AIRCRAFT_LENGTH['Gulfstream Aerospace Gulfstream V (twin-jet)'] = 29.4
AIRCRAFT_LENGTH['Hawker Beechcraft 4000 (twin-jet)'] = 21.08
AIRCRAFT_LENGTH['Honda HondaJet (twin-jet)'] = 12.99
AIRCRAFT_LENGTH['IAI Gulfstream G100 (twin-jet)'] = 16.94
AIRCRAFT_LENGTH['IAI Gulfstream G200 (twin-jet)'] = 18.97
AIRCRAFT_LENGTH['IAI Gulfstream G280 (twin-jet)'] = 20.3
AIRCRAFT_LENGTH['Learjet 35 (twin-jet)'] = 14.83
AIRCRAFT_LENGTH['Learjet 45 (twin-jet)'] = 17.68
AIRCRAFT_LENGTH['Learjet 60 (twin-jet)'] = 17.88
AIRCRAFT_LENGTH['McDonnell Douglas MD-11 (tri-jet)'] = 61.6
AIRCRAFT_LENGTH['Pilatus PC-12 (single-turboprop)'] = 14.4
AIRCRAFT_LENGTH['Raytheon Hawker 800 (twin-jet)'] = 15.60
AIRCRAFT_LENGTH['Raytheon Hawker 800XP (twin-jet)'] = 15.60
AIRCRAFT_LENGTH['Raytheon Hawker 850XP (twin-jet)'] = 15.60
AIRCRAFT_LENGTH['Raytheon Hawker 1000 (twin-jet)'] = 16.08
AIRCRAFT_LENGTH['Rockwell Turbo Commander 690 (twin-turboprop)'] = 11.22
for mixed_case_plane in list(AIRCRAFT_LENGTH.keys()):  # pylint: disable=C0201
  AIRCRAFT_LENGTH[mixed_case_plane.upper()] = AIRCRAFT_LENGTH[mixed_case_plane]
  AIRCRAFT_LENGTH.pop(mixed_case_plane)

# pylint: disable=line-too-long
SHORTER_AIRCRAFT_NAME = {}
SHORTER_AIRCRAFT_NAME['Boeing 787-9 Dreamliner (twin-jet)'] = 'Boeing 787-9 (twin-jet)'
SHORTER_AIRCRAFT_NAME['BOEING 787-10 Dreamliner (twin-jet)'] = 'Boeing 787-10 (twin-jet)'
SHORTER_AIRCRAFT_NAME['Gulfstream Aerospace Gulfstream 3 (twin-jet)'] = 'Gulfstream 3 (twin-jet)'
SHORTER_AIRCRAFT_NAME['Gulfstream Aerospace Gulfstream G450 (twin-jet)'] = 'Gulfstream G450 (twin-jet)'
SHORTER_AIRCRAFT_NAME['Gulfstream Aerospace Gulfstream G550 (twin-jet)'] = 'Gulfstream G550 (twin-jet)'
SHORTER_AIRCRAFT_NAME['Gulfstream Aerospace Gulfstream IV (twin-jet)'] = 'Gulfstream IV (twin-jet)'
SHORTER_AIRCRAFT_NAME['Gulfstream Aerospace Gulfstream V (twin-jet)'] = 'Gulfstream V (twin-jet)'
# pylint: enable=line-too-long


def Log(message, file=None, rolling=None):
  """Write a message to a logfile along with a timestamp.

  Args:
    message: string message to write
    file: string representing file name and, if needed, path to the
      file to write to
    rolling: name of file that will keep only the last n files of file
  """
  # can't define as a default parameter because LOGFILE name is potentially
  # modified based on SIMULATION flag
  if not file:
    file = LOGFILE

  # special case: for the main logfile, we always keep a rolling log
  if not rolling and file == LOGFILE:
    rolling = ROLLING_LOGFILE

  try:




                            <----SKIPPED LINES---->




  extra_space = SPLITFLAP_CHARS_PER_LINE - sum([len(str(s)) for s in l])
  last_gap = round(extra_space / (len(l) - 1))
  return EvenlySpace([*l[:-2], str(l[-2]) + ' '*last_gap + str(l[-1])])


def RemoveParentheticals(s):
  """Removes all instances of () and the text contained within - from string."""
  if not s:
    return s
  if '(' in s and ')' in s:
    open_paren = s.find('(')
    close_paren = s.find(')')
  else:
    return s
  if close_paren < open_paren:
    return s
  s = s.replace(s[open_paren:close_paren+1], '').strip().replace('  ', ' ')
  return RemoveParentheticals(s)


def ShorterPlaneName(s):
  """Replaces full plane name with a shorter name, if it exists."""
  return SHORTER_AIRCRAFT_NAME.get(s, s)


def Ordinal(n):
  """Converts integer n to an ordinal string - i.e.: 2 -> 2nd; 5 -> 5th."""
  return '%d%s' % (n, 'tsnrhtdd'[(math.floor(n/10)%10 != 1)*(n%10 < 4)*n%10::4])


def Screenify(lines, splitflap):
  """Transforms a list of lines to a single text string for display / print.

  Given a list of lines that is a fully-formed message to send to the splitflap
  display, this function transforms the list of strings to a single string that
  is an easier-to-read and more faithful representation of how the message will
  be displayed. The transformations are to add blank lines to the message to
  make it consistent number of lines, and to add border to the sides & top /
  bottom of the message.

  Args:
    lines: list of strings that comprise the message
    splitflap: boolean, True if directed for splitflap display; false if
    directed to screen





                            <----SKIPPED LINES---->




              1 + percent_size_difference)):
      last_aircraft_bigger = True
      comparative_text = 'smaller'

    last_flight_time_string = DisplayTime(last_flight, '%b %-d')
    if this_aircraft and last_aircraft:
      if this_aircraft_bigger or last_aircraft_bigger:
        message = ('%s used a %s plane today compared with last, on %s '
                   '(%s @ %dft vs. %s @ %dft)' % (
                       this_flight_number, comparative_text,
                       last_flight_time_string,
                       RemoveParentheticals(this_aircraft),
                       this_aircraft_length*FEET_IN_METER,
                       RemoveParentheticals(last_aircraft),
                       last_aircraft_length*FEET_IN_METER))
      elif last_aircraft and this_aircraft and last_aircraft != this_aircraft:
        message = (
            '%s used a different aircraft today compared'
            ' with last, on %s (%s vs. %s)' % (
                this_flight_number, last_flight_time_string,
                RemoveParentheticals(ShorterPlaneName(this_aircraft)),
                RemoveParentheticals(ShorterPlaneName(last_aircraft))))

  return message


def FlightInsightNthFlight(flights, hours=1, min_multiple_flights=2):
  """Generates string about seeing frequent flights to the same dest.

  Generates text of the following form for the "focus" flight in the data.
  - ASA1337 was the 4th flight to PHX in the last 53 minutes, served by Alaska
    Airlines, American Airlines, Southwest and United
  - SWA3102 was the 2nd flight to SAN in the last 25 minutes, both with
    Southwest

  Args:
    flights: the list of the raw data from which the insights will be generated,
      where the flights are listed in order of observation - i.e.: flights[0]
      was the earliest seen, and flights[-1] is the most recent flight for
      which we are attempting to generate an insight.
    hours: the time horizon over which to look for flights with the same
      destination.




                            <----SKIPPED LINES---->




      overall_stats_elements = []
      if delay_early_count:
        overall_stats_elements.append('%d ER' % delay_early_count)
      if delay_ontime_count:
        overall_stats_elements.append('%d OT' % delay_ontime_count)
      if delay_late_count:
        overall_stats_elements.append('%d LT' % delay_late_count)
      if delay_unknown_count:
        overall_stats_elements.append('%d UNK' % delay_unknown_count)
      overall_stats_text = '; '.join(overall_stats_elements)

      days_history = (int(
          round(last_timestamp - relevant_flights[0]['now']) / SECONDS_IN_DAY)
                      + 1)

      late_percentage = delay_late_count / len(relevant_flights)

      if (superlative and
          delay_late_avg_sec >= min_average_delay_minutes * SECONDS_IN_MINUTE):
        message = (
            'This %s delay is the %s for %s of the '
            'last %d days (%s avg delay); overall: %s' % (
                SecondsToHhMm(this_delay_seconds),
                delay_keyword,
                this_flight_number,
                days_history,
                SecondsToHhMm(delay_late_avg_sec),
                overall_stats_text))
      elif (late_percentage > min_late_percentage and
            delay_late_avg_sec >=
            min_average_delay_minutes * SECONDS_IN_MINUTE):
        # it's just been delayed frequently!
        message = (
            'With today''s delay of %s, %s is delayed %d%% of the time in'
            ' the last %d days for avg %s delay; overall: %s' % (
                SecondsToHhMm(this_delay_seconds),
                this_flight_number,
                int(100 * late_percentage),
                days_history,
                SecondsToHhMm(delay_late_avg_sec),
                overall_stats_text))
  return message


def FlightInsights(flights):
  """Identifies all the insight messages about the most recently seen flight.

  Generates a possibly-empty list of messages about the flight.

  Args:
    flights: List of all flights where the last flight in the list is the focus
    flight for which we are trying to identify something interesting.

  Returns:
    List of 2-tuples, where the first element in the tuple is a flag indicating




                            <----SKIPPED LINES---->




    message_queue: FIFO list of message tuples of (message type,
      message string).
    next_message_time: epoch at which next message should be displayed
    configuration: dictionary of configuration attributes.
    screens: List of past screens displayed to splitflap screen.

  Returns:
    Next_message_time, potentially updated if a message has been displayed,
    or unchanged if no message was displayed.
  """
  if message_queue and (time.time() >= next_message_time or SIMULATION):

    if SIMULATION:  # drain the queue because the messages come so fast
      messages_to_display = list(message_queue)
      # passed by reference, so clear it out since we drained it to the display
      del message_queue[:]
    else:  # display only one message, being mindful of the display timing
      messages_to_display = [message_queue.pop(0)]

    for message in messages_to_display:
      # we cannot just unpack the tuple because messages of type
      # FLAG_MSG_FLIGHT are 3-tuples (with the third element being the flight
      # dictionary) whereas other message types are 2-tuples
      message_type = message[0]
      message_text = message[1]

      # There may be one or several insight messages that were added to the
      # message queue along with the flight at a time when the screen was
      # enabled, but by the time it comes to display them, the screen is now
      # disabled.  These should not be displayed.  Note that this check only
      # needs to be done for insight messages because other message types
      # are user initiated and so presumably should be displayed irrespective
      # of when the user triggered it to be displayed.
      if message_type == FLAG_MSG_INSIGHT and not MessageMeetsDisplayCriteria(
          configuration):
        Log('Message %s purged')

      else:
        if isinstance(message_text, str):
          message_text = textwrap.wrap(
              message_text,
              width=SPLITFLAP_CHARS_PER_LINE)
        display_message = Screenify(message_text, False)




                            <----SKIPPED LINES---->