messageboard-2022-12-25-2330.py
01234567890123456789012345678901234567890123456789012345678901234567890123456789









389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429








514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554








212021212122212321242125212621272128212921302131213221332134213521362137213821392140 21412142214321442145214621472148214921502151215221532154215521562157215821592160








22042205220622072208220922102211221222132214221522162217221822192220222122222223 22242225222622272228222922302231223222332234223522362237223822392240224122422243








59845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006 600760086009601060116012       60136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053








64666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506











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




AIRCRAFT_LENGTH['Airbus A350-900 (twin-jet)'] = 66.8
AIRCRAFT_LENGTH['Airbus A380-800 (quad-jet)'] = 72.72
AIRCRAFT_LENGTH['Antonov An-124 Ruslan (quad-jet)'] = 69.1
AIRCRAFT_LENGTH['Beechcraft Beechjet (twin-jet)'] = 14.76
AIRCRAFT_LENGTH['Beechcraft Bonanza (33) (piston-single)'] = 7.65
AIRCRAFT_LENGTH['Beechcraft Bonanza (36) (piston-single)'] = 8.38
AIRCRAFT_LENGTH['Beechcraft King Air 90 (twin-turboprop)'] = 10.82
AIRCRAFT_LENGTH['Beechcraft King Air F90 (twin-turboprop)'] = 12.14
AIRCRAFT_LENGTH['Beechcraft Premier 1 (twin-jet)'] = 14.02
AIRCRAFT_LENGTH['Beechcraft Super King Air 200 (twin-turboprop)'] = 13.31
AIRCRAFT_LENGTH['Beechcraft Super King Air 350 (twin-turboprop)'] = 14.22
AIRCRAFT_LENGTH['Bell 429 GlobalRanger (twin-turboshaft)'] = 12.70
AIRCRAFT_LENGTH['Boeing 737-400 (twin-jet)'] = 36.4
AIRCRAFT_LENGTH['Boeing 737-500 (twin-jet)'] = 31.0
AIRCRAFT_LENGTH['Boeing 737-700 (twin-jet)'] = 33.63
AIRCRAFT_LENGTH['Boeing 737-800 (twin-jet)'] = 39.47
AIRCRAFT_LENGTH['Boeing 737-900 (twin-jet)'] = 42.11
AIRCRAFT_LENGTH['Boeing 737 MAX 8 (twin-jet)'] = 39.47
AIRCRAFT_LENGTH['Boeing 737 MAX 9 (twin-jet)'] = 42.1
AIRCRAFT_LENGTH['Boeing 737 MAX 9 (twin-jet)'] = 42.1
AIRCRAFT_LENGTH['Boeing 737 MAX 9 (biréacteur)'] = 42.1
AIRCRAFT_LENGTH['Boeing 747-100 (quad-jet)'] = 70.66
AIRCRAFT_LENGTH['Boeing 747-400 (quad-jet)'] = 70.66
AIRCRAFT_LENGTH['Boeing 747-8 (quad-jet)'] = 76.25
AIRCRAFT_LENGTH['Boeing 757-200 (twin-jet)'] = 47.3
AIRCRAFT_LENGTH['Boeing 757-300 (twin-jet)'] = 54.4
AIRCRAFT_LENGTH['Boeing 767-200 (twin-jet)'] = 48.51
AIRCRAFT_LENGTH['BOEING 767-300 (twin-jet)'] = 54.94
AIRCRAFT_LENGTH['Boeing 767-300 (twin-jet)'] = 54.94
AIRCRAFT_LENGTH['BOEING 767-400 (twin-jet)'] = 61.37
AIRCRAFT_LENGTH['Boeing 777 (twin-jet)'] = (63.73 + 73.86) / 2
AIRCRAFT_LENGTH['Boeing 777-200 (twin-jet)'] = 63.73
AIRCRAFT_LENGTH['BOEING 777-200ER (twin-jet)'] = 63.73
AIRCRAFT_LENGTH['BOEING 777-200LR (twin-jet)'] = 63.73
AIRCRAFT_LENGTH['Boeing 777-200LR/F (twin-jet)'] = 63.73
AIRCRAFT_LENGTH['Boeing 777-300 (twin-jet)'] = 33.4
AIRCRAFT_LENGTH['BOEING 777-300ER (twin-jet)'] = 73.86
AIRCRAFT_LENGTH['Boeing 777-300ER (twin-jet)'] = 73.86
AIRCRAFT_LENGTH['Boeing 787-10 (twin-jet)'] = 68.28
AIRCRAFT_LENGTH['Boeing 787-8 (twin-jet)'] = 56.72
AIRCRAFT_LENGTH['Boeing 787-9 (twin-jet)'] = 62.81




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




AIRCRAFT_LENGTH['McDonnell Douglas MD-83 (twin-jet)'] = 45.06
AIRCRAFT_LENGTH['Mooney M-20 (piston-single)'] = 8.13
AIRCRAFT_LENGTH['North American Navion (piston-single)'] = 8.38
AIRCRAFT_LENGTH['North American Sabreliner (twin-jet)'] = 13.41
AIRCRAFT_LENGTH['Pilatus PC-12 (single-turboprop)'] = 14.4
AIRCRAFT_LENGTH['Pilatus PC-24 (twin-jet)'] = 16.85
AIRCRAFT_LENGTH['Piper Cherokee (piston-single)'] = 7.10
AIRCRAFT_LENGTH['Piper Navajo (twin-piston)'] = 9.94
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 900XP (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 737 MAX 9 (biréacteur)'] = 'Boeing 737 MAX 9 (twin-jet)'
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['Canadair Regional Jet CRJ-200 (twin-jet)'] = 'Canadair CRJ-200 (twin-jet)'
SHORTER_AIRCRAFT_NAME['Canadair Regional Jet CRJ-700 (twin-jet)'] = 'Canadair CRJ-700 (twin-jet)'
SHORTER_AIRCRAFT_NAME['Canadair Regional Jet CRJ-900 (twin-jet)'] = 'Canadair CRJ-900 (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)'
SHORTER_AIRCRAFT_NAME['GULFSTREAM AEROSPACE G-7 Gulfstream G600 (twin-jet)'] = 'Gulfstream G600 (twin-jet)'
# pylint: enable=line-too-long

SHORTER_AIRLINE_NAME = {}
SHORTER_AIRLINE_NAME['WHEELS UP - GAMA AVIATION'] = 'GAMA AVIATION'


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





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




  flight = {}
  parsed_json = json.loads(flight_json)

  fa_flight_number = list(parsed_json['flights'].keys())[0]
  parsed_flight_details = parsed_json['flights'][fa_flight_number]
  flight['fa_flight_number'] = fa_flight_number

  origin = parsed_flight_details.get('origin')
  if origin:
    flight['origin_friendly'] = origin.get('friendlyLocation')
    flight['origin_iata'] = origin.get('iata')

  destination = parsed_flight_details.get('destination')
  if destination:
    flight['destination_friendly'] = destination.get('friendlyLocation')
    flight['destination_iata'] = destination.get('iata')

  aircraft_type = parsed_flight_details.get('aircraft')
  if aircraft_type:
    flight['aircraft_type_code'] = aircraft_type.get('type')
    flight['aircraft_type_friendly'] = aircraft_type.get('friendlyType')

    flight['owner_location'] = Unidecode(aircraft_type.get('ownerLocation'))
    flight['owner'] = Unidecode(aircraft_type.get('owner'))
    flight['tail'] = Unidecode(aircraft_type.get('tail'))

  takeoff_time = parsed_flight_details.get('takeoffTimes')
  if takeoff_time:
    flight['scheduled_takeofftime'] = takeoff_time.get('scheduled')
    flight['actual_takeoff_time'] = takeoff_time.get('actual')

  gate_departure_time = parsed_flight_details.get('gateDepartureTimes')
  if gate_departure_time:
    flight['scheduled_departure_time'] = gate_departure_time.get('scheduled')
    flight['actual_departure_time'] = gate_departure_time.get('actual')

  gate_arrival_time = parsed_flight_details.get('gateArrivalTimes')
  if gate_arrival_time:
    flight['scheduled_arrival_time'] = gate_arrival_time.get('scheduled')
    flight['estimated_arrival_time'] = gate_arrival_time.get('estimated')

  landing_time = parsed_flight_details.get('landingTimes')




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





  # Some names are very similar to others and so appear identical on splitflap
  replacement_names = (
      ('Delta Private Jets', 'DPJ'),
      ('United Parcel Service', 'UPS'))
  for (old, new) in replacement_names:
    if airline and old.upper() == airline.upper():
      airline = new
      break

  if not airline:
    airline = KEY_NOT_PRESENT_STRING
  return airline


def AircraftLength(flight, default=0):
  """Returns length (in meters) of aircraft, or default if unknown."""
  aircraft = flight.get('aircraft_type_friendly')
  if not aircraft:
    return default

  if aircraft.upper() not in AIRCRAFT_LENGTH:
    return default
  return AIRCRAFT_LENGTH[aircraft.upper()]


def DisplayLength(flight):
  """Returns rounded length (in meters) of aircraft, or UNKNOWN if unknown."""
  length = round(AircraftLength(flight, default=0))
  if length:
    return length
  return KEY_NOT_PRESENT_STRING


def DisplaySpeed(flight):
  """Returns speed in knots or UNKNOWN if not known."""
  return flight.get('speed', KEY_NOT_PRESENT_STRING)


def DisplayVertRate(flight):
  """Returns vertical rate in fpm or UNKNOWN if not known."""




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





  Because the .pk is updated infrequently (i.e.: only once every 10 min),
  we can cache it and re-read it only when it changes.

  If there are not enough intervals to provide the number_of_intervals
  requested, as many as are present will be provided.

  Args:
    number_of_intervals: integer of time periods desired.

  Returns:
    A 5-tuple:
    - List of the most recent number_of_intervals from the network status
      pickle file.
    - String representing the most recent day in the list
    - Index of the interval from that most recent day
    - String representing the earliest day in the list
    - Index of the interval from that earliest day
    If the list is empty, then the strings in positions 2 & 4 of the return
    tuple will be the empty string, and indices in positions 3 & 5 of the
    return tuple will be -1.
  """
  global CACHED_FILES

  filepath = NETWORK_PICKLE_FILE
  (last_read_time, network_status) = CACHED_FILES.get(filepath, (0, {}))
  if os.path.exists(filepath):
    last_modified = os.path.getmtime(filepath)
    if last_modified > last_read_time:
      network_status = UnpickleObjectFromFile(filepath, False)[0]







      CACHED_FILES[filepath] = (last_modified, network_status)
    # Now we've read in the network status, or picked it up from cache

    day_names = sorted(network_status.keys())
    last_day = day_names[-1]
    last_interval = len(network_status[last_day])
    relevant_status = []
    remaining_to_add = number_of_intervals
    while len(relevant_status) < number_of_intervals and day_names:
      day_pointer = day_names.pop()
      relevant_status.extend(list(reversed(network_status[day_pointer])))
      first_interval = len(network_status[day_pointer]) - remaining_to_add
      remaining_to_add -= len(network_status[day_pointer])
      first_day = day_pointer

    first_interval = max(first_interval, 0)
    return (
        relevant_status[:min(len(relevant_status), number_of_intervals)],
        last_day, last_interval,
        first_day, first_interval)
  return [], '', -1, '', -1


def InterruptRebootFromButton():
  """Sets flag so main loop will terminate when it completes the iteration.

  This function is only triggered by an physical button press.
  """
  msg = ('Soft reboot requested by button push')
  global SHUTDOWN_SIGNAL
  SHUTDOWN_SIGNAL = msg

  global REBOOT_SIGNAL
  REBOOT_SIGNAL = True

  RPi.GPIO.output(GPIO_SOFT_RESET[1], False)  # signal that reset received
  Log(msg)


def InterruptShutdownFromSignal(signalNumber, unused_frame):
  """Sets flag so main loop will terminate when it completes the iteration.




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




      [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]
    where each 0 is replaced by an integer 0,...,69.
  '''
  # For each element in the string,
  #   if it is an '{', find the closing '}' and collapse into one element
  #   if it is not an '{' and we are not in an escaped string, translate
  translation = {
  ' ': 0, 'A': 1, 'B': 2, 'C': 3, 'D': 4,
  'E': 5, 'F': 6, 'G': 7, 'H': 8, 'I': 9,
  'J': 10, 'K': 11, 'L': 12, 'M': 13, 'N': 14,
  'O': 15, 'P': 16, 'Q': 17, 'R': 18, 'S': 19,
  'T': 20, 'U': 21, 'V': 22, 'W': 23, 'X': 24,
  'Y': 25, 'Z': 26, '1': 27, '2': 28, '3': 29,
  '4': 30, '5': 31, '6': 32, '7': 33, '8': 34,
  '9': 35, '0': 36, '!': 37, '@': 38, '#': 39,
  '$': 40, '(': 41, ')': 42, '-': 44, '+': 46,
  '&': 47, '=': 48, ';': 49, ':': 50, "'": 52,
  '"': 53, '%': 54, ',': 55, '.': 56, '/': 59,
  '?': 60, '°': 62, 'É': 5}

  # valid codes are those enumerated above plus the seven colors in 63, ..., 69
  valid_codes = list(translation.values())
  valid_codes.extend([63, 64, 65, 66, 67, 68, 69])

  pointer = 0
  message_array = []

  while pointer < len(s):
    if s[pointer] == '{':
      end_escape = s.find('}', pointer)

      if end_escape == -1:
        Log('Escaped sequence missing closing curly brace: "%s"' % s)

      escaped_sequence = s[pointer+1 : end_escape]
      try:
        escaped_value = int(escaped_sequence)
      except ValueError:
        Log('Escaped sequence "%s" is not a number in message "%s"'




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





01234567890123456789012345678901234567890123456789012345678901234567890123456789









389390391392393394395396397398399400401402403404405406407408 409410411412413414415416417418419420421422423424425426427428








513514515516517518519520521522523524525526527528529530531532 533534535536537538539540541542543544545546547548549550551552








211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159








22032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243








598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061








64746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514











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




AIRCRAFT_LENGTH['Airbus A350-900 (twin-jet)'] = 66.8
AIRCRAFT_LENGTH['Airbus A380-800 (quad-jet)'] = 72.72
AIRCRAFT_LENGTH['Antonov An-124 Ruslan (quad-jet)'] = 69.1
AIRCRAFT_LENGTH['Beechcraft Beechjet (twin-jet)'] = 14.76
AIRCRAFT_LENGTH['Beechcraft Bonanza (33) (piston-single)'] = 7.65
AIRCRAFT_LENGTH['Beechcraft Bonanza (36) (piston-single)'] = 8.38
AIRCRAFT_LENGTH['Beechcraft King Air 90 (twin-turboprop)'] = 10.82
AIRCRAFT_LENGTH['Beechcraft King Air F90 (twin-turboprop)'] = 12.14
AIRCRAFT_LENGTH['Beechcraft Premier 1 (twin-jet)'] = 14.02
AIRCRAFT_LENGTH['Beechcraft Super King Air 200 (twin-turboprop)'] = 13.31
AIRCRAFT_LENGTH['Beechcraft Super King Air 350 (twin-turboprop)'] = 14.22
AIRCRAFT_LENGTH['Bell 429 GlobalRanger (twin-turboshaft)'] = 12.70
AIRCRAFT_LENGTH['Boeing 737-400 (twin-jet)'] = 36.4
AIRCRAFT_LENGTH['Boeing 737-500 (twin-jet)'] = 31.0
AIRCRAFT_LENGTH['Boeing 737-700 (twin-jet)'] = 33.63
AIRCRAFT_LENGTH['Boeing 737-800 (twin-jet)'] = 39.47
AIRCRAFT_LENGTH['Boeing 737-900 (twin-jet)'] = 42.11
AIRCRAFT_LENGTH['Boeing 737 MAX 8 (twin-jet)'] = 39.47
AIRCRAFT_LENGTH['Boeing 737 MAX 9 (twin-jet)'] = 42.1
AIRCRAFT_LENGTH['Boeing 737 MAX 9 (twin-jet)'] = 42.1

AIRCRAFT_LENGTH['Boeing 747-100 (quad-jet)'] = 70.66
AIRCRAFT_LENGTH['Boeing 747-400 (quad-jet)'] = 70.66
AIRCRAFT_LENGTH['Boeing 747-8 (quad-jet)'] = 76.25
AIRCRAFT_LENGTH['Boeing 757-200 (twin-jet)'] = 47.3
AIRCRAFT_LENGTH['Boeing 757-300 (twin-jet)'] = 54.4
AIRCRAFT_LENGTH['Boeing 767-200 (twin-jet)'] = 48.51
AIRCRAFT_LENGTH['BOEING 767-300 (twin-jet)'] = 54.94
AIRCRAFT_LENGTH['Boeing 767-300 (twin-jet)'] = 54.94
AIRCRAFT_LENGTH['BOEING 767-400 (twin-jet)'] = 61.37
AIRCRAFT_LENGTH['Boeing 777 (twin-jet)'] = (63.73 + 73.86) / 2
AIRCRAFT_LENGTH['Boeing 777-200 (twin-jet)'] = 63.73
AIRCRAFT_LENGTH['BOEING 777-200ER (twin-jet)'] = 63.73
AIRCRAFT_LENGTH['BOEING 777-200LR (twin-jet)'] = 63.73
AIRCRAFT_LENGTH['Boeing 777-200LR/F (twin-jet)'] = 63.73
AIRCRAFT_LENGTH['Boeing 777-300 (twin-jet)'] = 33.4
AIRCRAFT_LENGTH['BOEING 777-300ER (twin-jet)'] = 73.86
AIRCRAFT_LENGTH['Boeing 777-300ER (twin-jet)'] = 73.86
AIRCRAFT_LENGTH['Boeing 787-10 (twin-jet)'] = 68.28
AIRCRAFT_LENGTH['Boeing 787-8 (twin-jet)'] = 56.72
AIRCRAFT_LENGTH['Boeing 787-9 (twin-jet)'] = 62.81




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




AIRCRAFT_LENGTH['McDonnell Douglas MD-83 (twin-jet)'] = 45.06
AIRCRAFT_LENGTH['Mooney M-20 (piston-single)'] = 8.13
AIRCRAFT_LENGTH['North American Navion (piston-single)'] = 8.38
AIRCRAFT_LENGTH['North American Sabreliner (twin-jet)'] = 13.41
AIRCRAFT_LENGTH['Pilatus PC-12 (single-turboprop)'] = 14.4
AIRCRAFT_LENGTH['Pilatus PC-24 (twin-jet)'] = 16.85
AIRCRAFT_LENGTH['Piper Cherokee (piston-single)'] = 7.10
AIRCRAFT_LENGTH['Piper Navajo (twin-piston)'] = 9.94
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 900XP (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['Canadair Regional Jet CRJ-200 (twin-jet)'] = 'Canadair CRJ-200 (twin-jet)'
SHORTER_AIRCRAFT_NAME['Canadair Regional Jet CRJ-700 (twin-jet)'] = 'Canadair CRJ-700 (twin-jet)'
SHORTER_AIRCRAFT_NAME['Canadair Regional Jet CRJ-900 (twin-jet)'] = 'Canadair CRJ-900 (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)'
SHORTER_AIRCRAFT_NAME['GULFSTREAM AEROSPACE G-7 Gulfstream G600 (twin-jet)'] = 'Gulfstream G600 (twin-jet)'
# pylint: enable=line-too-long

SHORTER_AIRLINE_NAME = {}
SHORTER_AIRLINE_NAME['WHEELS UP - GAMA AVIATION'] = 'GAMA AVIATION'


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





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




  flight = {}
  parsed_json = json.loads(flight_json)

  fa_flight_number = list(parsed_json['flights'].keys())[0]
  parsed_flight_details = parsed_json['flights'][fa_flight_number]
  flight['fa_flight_number'] = fa_flight_number

  origin = parsed_flight_details.get('origin')
  if origin:
    flight['origin_friendly'] = origin.get('friendlyLocation')
    flight['origin_iata'] = origin.get('iata')

  destination = parsed_flight_details.get('destination')
  if destination:
    flight['destination_friendly'] = destination.get('friendlyLocation')
    flight['destination_iata'] = destination.get('iata')

  aircraft_type = parsed_flight_details.get('aircraft')
  if aircraft_type:
    flight['aircraft_type_code'] = aircraft_type.get('type')
    flight['aircraft_type_friendly'] = aircraft_type.get(
        'friendlyType').replace('biréacteur', 'twin-jet')
    flight['owner_location'] = Unidecode(aircraft_type.get('ownerLocation'))
    flight['owner'] = Unidecode(aircraft_type.get('owner'))
    flight['tail'] = Unidecode(aircraft_type.get('tail'))

  takeoff_time = parsed_flight_details.get('takeoffTimes')
  if takeoff_time:
    flight['scheduled_takeofftime'] = takeoff_time.get('scheduled')
    flight['actual_takeoff_time'] = takeoff_time.get('actual')

  gate_departure_time = parsed_flight_details.get('gateDepartureTimes')
  if gate_departure_time:
    flight['scheduled_departure_time'] = gate_departure_time.get('scheduled')
    flight['actual_departure_time'] = gate_departure_time.get('actual')

  gate_arrival_time = parsed_flight_details.get('gateArrivalTimes')
  if gate_arrival_time:
    flight['scheduled_arrival_time'] = gate_arrival_time.get('scheduled')
    flight['estimated_arrival_time'] = gate_arrival_time.get('estimated')

  landing_time = parsed_flight_details.get('landingTimes')




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





  # Some names are very similar to others and so appear identical on splitflap
  replacement_names = (
      ('Delta Private Jets', 'DPJ'),
      ('United Parcel Service', 'UPS'))
  for (old, new) in replacement_names:
    if airline and old.upper() == airline.upper():
      airline = new
      break

  if not airline:
    airline = KEY_NOT_PRESENT_STRING
  return airline


def AircraftLength(flight, default=0):
  """Returns length (in meters) of aircraft, or default if unknown."""
  aircraft = flight.get('aircraft_type_friendly')
  if not aircraft:
    return default

  if aircraft.upper() not in AIRCRAFT_LENGTH:
    return default
  return AIRCRAFT_LENGTH[aircraft.upper()]


def DisplayLength(flight):
  """Returns rounded length (in meters) of aircraft, or UNKNOWN if unknown."""
  length = round(AircraftLength(flight, default=0))
  if length:
    return length
  return KEY_NOT_PRESENT_STRING


def DisplaySpeed(flight):
  """Returns speed in knots or UNKNOWN if not known."""
  return flight.get('speed', KEY_NOT_PRESENT_STRING)


def DisplayVertRate(flight):
  """Returns vertical rate in fpm or UNKNOWN if not known."""




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





  Because the .pk is updated infrequently (i.e.: only once every 10 min),
  we can cache it and re-read it only when it changes.

  If there are not enough intervals to provide the number_of_intervals
  requested, as many as are present will be provided.

  Args:
    number_of_intervals: integer of time periods desired.

  Returns:
    A 5-tuple:
    - List of the most recent number_of_intervals from the network status
      pickle file.
    - String representing the most recent day in the list
    - Index of the interval from that most recent day
    - String representing the earliest day in the list
    - Index of the interval from that earliest day
    If the list is empty, then the strings in positions 2 & 4 of the return
    tuple will be the empty string, and indices in positions 3 & 5 of the
    return tuple will be -1 - that is, [], '', -1, '', -1
  """
  global CACHED_FILES
  no_file_return_tuple = ([], '', -1, '', -1)
  filepath = NETWORK_PICKLE_FILE
  (last_read_time, network_status) = CACHED_FILES.get(filepath, (0, {}))
  if os.path.exists(filepath):
    last_modified = os.path.getmtime(filepath)
    if last_modified > last_read_time:
      unpickled_list = UnpickleObjectFromFile(filepath, False)
      if not unpickled_list:
        # This might happen in a race condition: we are re-writing the
        # NETWORK_PICKLE_FILE at exactly the same time we are trying to
        # read it in.  In this case, we should just exit here, with
        # a tuple indicative of inability to access network file
        return no_file_return_tuple
      network_status = unpickled_list[0]
      CACHED_FILES[filepath] = (last_modified, network_status)
    # Now we've read in the network status, or picked it up from cache

    day_names = sorted(network_status.keys())
    last_day = day_names[-1]
    last_interval = len(network_status[last_day])
    relevant_status = []
    remaining_to_add = number_of_intervals
    while len(relevant_status) < number_of_intervals and day_names:
      day_pointer = day_names.pop()
      relevant_status.extend(list(reversed(network_status[day_pointer])))
      first_interval = len(network_status[day_pointer]) - remaining_to_add
      remaining_to_add -= len(network_status[day_pointer])
      first_day = day_pointer

    first_interval = max(first_interval, 0)
    return (
        relevant_status[:min(len(relevant_status), number_of_intervals)],
        last_day, last_interval,
        first_day, first_interval)
  return no_file_return_tuple


def InterruptRebootFromButton():
  """Sets flag so main loop will terminate when it completes the iteration.

  This function is only triggered by an physical button press.
  """
  msg = ('Soft reboot requested by button push')
  global SHUTDOWN_SIGNAL
  SHUTDOWN_SIGNAL = msg

  global REBOOT_SIGNAL
  REBOOT_SIGNAL = True

  RPi.GPIO.output(GPIO_SOFT_RESET[1], False)  # signal that reset received
  Log(msg)


def InterruptShutdownFromSignal(signalNumber, unused_frame):
  """Sets flag so main loop will terminate when it completes the iteration.




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




      [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]
    where each 0 is replaced by an integer 0,...,69.
  '''
  # For each element in the string,
  #   if it is an '{', find the closing '}' and collapse into one element
  #   if it is not an '{' and we are not in an escaped string, translate
  translation = {
  ' ': 0, 'A': 1, 'B': 2, 'C': 3, 'D': 4,
  'E': 5, 'F': 6, 'G': 7, 'H': 8, 'I': 9,
  'J': 10, 'K': 11, 'L': 12, 'M': 13, 'N': 14,
  'O': 15, 'P': 16, 'Q': 17, 'R': 18, 'S': 19,
  'T': 20, 'U': 21, 'V': 22, 'W': 23, 'X': 24,
  'Y': 25, 'Z': 26, '1': 27, '2': 28, '3': 29,
  '4': 30, '5': 31, '6': 32, '7': 33, '8': 34,
  '9': 35, '0': 36, '!': 37, '@': 38, '#': 39,
  '$': 40, '(': 41, ')': 42, '-': 44, '+': 46,
  '&': 47, '=': 48, ';': 49, ':': 50, "'": 52,
  '"': 53, '%': 54, ',': 55, '.': 56, '/': 59,
  '?': 60, '°': 62, 'É': 5, 'Д': 1}

  # valid codes are those enumerated above plus the seven colors in 63, ..., 69
  valid_codes = list(translation.values())
  valid_codes.extend([63, 64, 65, 66, 67, 68, 69])

  pointer = 0
  message_array = []

  while pointer < len(s):
    if s[pointer] == '{':
      end_escape = s.find('}', pointer)

      if end_escape == -1:
        Log('Escaped sequence missing closing curly brace: "%s"' % s)

      escaped_sequence = s[pointer+1 : end_escape]
      try:
        escaped_value = int(escaped_sequence)
      except ValueError:
        Log('Escaped sequence "%s" is not a number in message "%s"'




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