messageboard-2020-10-24-2253.py
01234567890123456789012345678901234567890123456789012345678901234567890123456789









14081409141014111412141314141415141614171418141914201421142214231424142514261427 14281429143014311432143314341435143614371438143914401441144214431444   14451446144714481449145014511452145314541455145614571458145914601461146214631464








14921493149414951496149714981499150015011502150315041505150615071508150915101511   15121513151415151516151715181519152015211522152315241525152615271528152915301531








18651866186718681869187018711872187318741875187618771878187918801881188218831884                                                                      18851886188718881889189018911892189318941895189618971898189919001901190219031904








668366846685668666876688668966906691669266936694669566966697669866996700670167026703 67046705670667076708670967106711671267136714671567166717671867196720672167226723











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




  """
  newly_nearby_flight_identifiers = []
  for flight_identifier in current_nearby_aircraft:
    flight_number = flight_identifier[0]
    # Only add it to the list once we've received a flight number
    if flight_identifier not in persistent_nearby_aircraft and flight_number:
      newly_nearby_flight_identifiers.append(flight_identifier)
    persistent_nearby_aircraft[flight_identifier] = now

  flights_to_delete = []
  for flight_identifier in persistent_nearby_aircraft:
    if (flight_identifier not in current_nearby_aircraft
        and (now - persistent_nearby_aircraft[flight_identifier]) >
        PERSISTENCE_SECONDS):
      flights_to_delete.append(flight_identifier)
  for flight_identifier in flights_to_delete:
    del persistent_nearby_aircraft[flight_identifier]
  return newly_nearby_flight_identifiers



def ScanForNewFlights(persistent_nearby_aircraft, persistent_path, log_jsons):
  """Determines if there are any new aircraft in the radio message.

  The radio is continuously dumping new json messages to the Raspberry pi with
  all the flights currently observed. This function picks up the latest radio
  json, and for  any new nearby flights - there should generally be at most one
  new flight on each pass through - gets additional flight data from
  FlightAware and augments the flight definition with the relevant fields to
  keep.

  Args:
    persistent_nearby_aircraft: dictionary where keys are flight numbers, and
      the values are the time the flight was last seen.
    persistent_path: dictionary where keys are flight numbers, and the values
      are a sequential list of the location-attributes in the json file; allows
      for tracking the flight path over time.
    log_jsons: boolean indicating whether we should pickle the JSONs.




  Returns:
    A tuple:
    - updated persistent_nearby_aircraft
    - (possibly empty) dictionary of flight attributes of the new flight upon
      its first observation.
    - the time of the radio observation if present; None if no radio dump
    - a dictionary of attributes about the dump itself (i.e.: # of flights;
      furthest observed flight, etc.)
    - persistent_path, a data structure containing past details of a flight's
      location as described in ParseDumpJson
  """
  flight_details = {}
  now = time.time()
  if SIMULATION:
    (dump_json, json_time) = DUMP_JSONS[SIMULATION_COUNTER]
  else:
    dump_json = ReadFile(DUMP_JSON_FILE, log_exception=True)

  json_desc_dict = {}




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




      flight_aware_json = {}
      if SIMULATION:
        json_times = [j[1] for j in FA_JSONS]
        if json_time in json_times:
          flight_aware_json = FA_JSONS[json_times.index(json_time)][0]
      elif flight_identifier[0]:
        flight_number = flight_identifier[0]
        flight_aware_json, error_message = GetFlightAwareJson(flight_number)
        if flight_aware_json:
          UpdateStatusLight(GPIO_ERROR_FLIGHT_AWARE_CONNECTION, False)
        else:
          failure_message = 'No json from Flightaware for flight %s: %s' % (
              flight_number, error_message[:500])
          Log(failure_message)
          UpdateStatusLight(
              GPIO_ERROR_FLIGHT_AWARE_CONNECTION, True, failure_message)

      flight_details = {}
      if flight_aware_json:
        flight_details = ParseFlightAwareJson(flight_aware_json)




      if not SIMULATION and log_jsons:
        PickleObjectToFile((flight_aware_json, now), PICKLE_FA_JSON_FILE, True)

      # Augment FlightAware details with radio / radio-derived details
      flight_details.update(current_nearby_aircraft[flight_identifier])

      # Augment with the past location data; the [1] is because recall that
      # persistent_path[key] is actually a 2-tuple, the first element being
      # the most recent time seen, and the second element being the actual
      # path. But we do not need to keep around the most recent time seen any
      # more.
      flight_details['persistent_path'] = persistent_path[flight_identifier][1]

  return (
      persistent_nearby_aircraft,
      flight_details,
      now,
      json_desc_dict,
      persistent_path)




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




  for script in l:
    if "trackpollBootstrap" in str(script):
      flight_script = str(script)
      break
  if not flight_script:
    error_msg = (
        'Unable to find trackpollBootstrap script in page: ' + response.text)
    Log(error_msg)
    return '', error_msg
  first_open_curly_brace = flight_script.find('{')
  last_close_curly_brace = flight_script.rfind('}')
  flight_json = flight_script[first_open_curly_brace:last_close_curly_brace+1]
  return flight_json, ''


def Unidecode(s):
  """Convert a special unicode characters to closest ASCII representation."""
  if s is not None:
    s = unidecode.unidecode(s)
  return s








































































def ParseFlightAwareJson(flight_json):
  """Strips relevant data about the flight from FlightAware feed.

  The FlightAware json has hundreds of fields about a flight, only a fraction
  of which are relevant to extract. Note that some of the fields are
  inconsistently populated (i.e.: scheduled and actual times for departure and
  take-off).

  Args:
    flight_json: Text representation of the FlightAware json about a single
      flight.

  Returns:
    Dictionary of flight attributes extracted from the FlightAware json.
  """
  flight = {}
  parsed_json = json.loads(flight_json)





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




    # isn't a simulation, then only read & do related processing for the
    # next dump if the last-modified timestamp indicates the file has been
    # updated since it was last read.
    tmp_timestamp = 0
    if not SIMULATION:
      dump_json_exists = os.path.exists(DUMP_JSON_FILE)
      if dump_json_exists:
        tmp_timestamp = os.path.getmtime(DUMP_JSON_FILE)
    if (SIMULATION and DumpJsonChanges()) or (
        not SIMULATION and dump_json_exists and
        tmp_timestamp > last_dump_json_timestamp):

      last_dump_json_timestamp = tmp_timestamp

      (persistent_nearby_aircraft,
       flight, now,
       json_desc_dict,
       persistent_path) = ScanForNewFlights(
           persistent_nearby_aircraft,
           persistent_path,
           configuration.get('log_jsons', False))


      # because this might just be an updated instance of the previous
      # flight as more identifier information (squawk and or flight number)
      # comes in, we only want to process this if its a truly new flight
      new_flight_flag = ConfirmNewFlight(flight, flights)

      if new_flight_flag:
        flights.append(flight)
        remote, servo = RefreshArduinos(
            remote, servo,
            to_remote_q, to_servo_q, to_main_q, shutdown,
            flights, json_desc_dict, configuration, screen_history)

        if FlightMeetsDisplayCriteria(flight, configuration, log=True):
          personal_message = None  # Any personal message displayed now cleared
          flight_message = (
              FLAG_MSG_FLIGHT, CreateMessageAboutFlight(flight), flight)

          # display the next message about this flight now!
          next_message_time = time.time()




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





01234567890123456789012345678901234567890123456789012345678901234567890123456789









1408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468








1496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538








18721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981








676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801











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




  """
  newly_nearby_flight_identifiers = []
  for flight_identifier in current_nearby_aircraft:
    flight_number = flight_identifier[0]
    # Only add it to the list once we've received a flight number
    if flight_identifier not in persistent_nearby_aircraft and flight_number:
      newly_nearby_flight_identifiers.append(flight_identifier)
    persistent_nearby_aircraft[flight_identifier] = now

  flights_to_delete = []
  for flight_identifier in persistent_nearby_aircraft:
    if (flight_identifier not in current_nearby_aircraft
        and (now - persistent_nearby_aircraft[flight_identifier]) >
        PERSISTENCE_SECONDS):
      flights_to_delete.append(flight_identifier)
  for flight_identifier in flights_to_delete:
    del persistent_nearby_aircraft[flight_identifier]
  return newly_nearby_flight_identifiers


def ScanForNewFlights(
    persistent_nearby_aircraft, persistent_path, log_jsons, flights):
  """Determines if there are any new aircraft in the radio message.

  The radio is continuously dumping new json messages to the Raspberry pi with
  all the flights currently observed. This function picks up the latest radio
  json, and for  any new nearby flights - there should generally be at most one
  new flight on each pass through - gets additional flight data from
  FlightAware and augments the flight definition with the relevant fields to
  keep.

  Args:
    persistent_nearby_aircraft: dictionary where keys are flight numbers, and
      the values are the time the flight was last seen.
    persistent_path: dictionary where keys are flight numbers, and the values
      are a sequential list of the location-attributes in the json file; allows
      for tracking the flight path over time.
    log_jsons: boolean indicating whether we should pickle the JSONs.
    flights: list of flight dictionaries; if no json is returned, used to
      find a recent flight with same flight number to augment this flight with
      origin / destination / airline.

  Returns:
    A tuple:
    - updated persistent_nearby_aircraft
    - (possibly empty) dictionary of flight attributes of the new flight upon
      its first observation.
    - the time of the radio observation if present; None if no radio dump
    - a dictionary of attributes about the dump itself (i.e.: # of flights;
      furthest observed flight, etc.)
    - persistent_path, a data structure containing past details of a flight's
      location as described in ParseDumpJson
  """
  flight_details = {}
  now = time.time()
  if SIMULATION:
    (dump_json, json_time) = DUMP_JSONS[SIMULATION_COUNTER]
  else:
    dump_json = ReadFile(DUMP_JSON_FILE, log_exception=True)

  json_desc_dict = {}




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




      flight_aware_json = {}
      if SIMULATION:
        json_times = [j[1] for j in FA_JSONS]
        if json_time in json_times:
          flight_aware_json = FA_JSONS[json_times.index(json_time)][0]
      elif flight_identifier[0]:
        flight_number = flight_identifier[0]
        flight_aware_json, error_message = GetFlightAwareJson(flight_number)
        if flight_aware_json:
          UpdateStatusLight(GPIO_ERROR_FLIGHT_AWARE_CONNECTION, False)
        else:
          failure_message = 'No json from Flightaware for flight %s: %s' % (
              flight_number, error_message[:500])
          Log(failure_message)
          UpdateStatusLight(
              GPIO_ERROR_FLIGHT_AWARE_CONNECTION, True, failure_message)

      flight_details = {}
      if flight_aware_json:
        flight_details = ParseFlightAwareJson(flight_aware_json)
      elif flight_identifier[0]:  # if there's a flight number but no json
        flight_details = FindAttributesFromSimilarFlights(
            flight_identifier[0], flights)

      if not SIMULATION and log_jsons:
        PickleObjectToFile((flight_aware_json, now), PICKLE_FA_JSON_FILE, True)

      # Augment FlightAware details with radio / radio-derived details
      flight_details.update(current_nearby_aircraft[flight_identifier])

      # Augment with the past location data; the [1] is because recall that
      # persistent_path[key] is actually a 2-tuple, the first element being
      # the most recent time seen, and the second element being the actual
      # path. But we do not need to keep around the most recent time seen any
      # more.
      flight_details['persistent_path'] = persistent_path[flight_identifier][1]

  return (
      persistent_nearby_aircraft,
      flight_details,
      now,
      json_desc_dict,
      persistent_path)




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




  for script in l:
    if "trackpollBootstrap" in str(script):
      flight_script = str(script)
      break
  if not flight_script:
    error_msg = (
        'Unable to find trackpollBootstrap script in page: ' + response.text)
    Log(error_msg)
    return '', error_msg
  first_open_curly_brace = flight_script.find('{')
  last_close_curly_brace = flight_script.rfind('}')
  flight_json = flight_script[first_open_curly_brace:last_close_curly_brace+1]
  return flight_json, ''


def Unidecode(s):
  """Convert a special unicode characters to closest ASCII representation."""
  if s is not None:
    s = unidecode.unidecode(s)
  return s


def FindAttributesFromSimilarFlights(this_flight_number, flights):
  """Returns a dictionary with info about a flight based on other flights.

  We may not get a json from the internet about this flight for any number
  of reasons: internet down; website down; too frequent queries; etc.  However,
  there are still some basic attributes we can derive about this flight
  from past observations of the same flight number, or past observations
  about the flight number prefix.  Specifically, we can get the flight's
  airline, origin, and destination.

  Args:
    this_flight_number: String of this flight number.
    flights: List of past flights.

  Returns:
    Dictionary of flight attributes extracted from the FlightAware json.
  """
  derived_attributes = {}
  if not this_flight_number:
    return derived_attributes

  potential_values = {
      'origin_friendly': [],
      'origin_iata': [],
      'destination_friendly': [],
      'destination_iata': [],
      'airline_call_sign': [],
      'airline_short_name': [],
      'airline_full_name': []
      }

  def AppendIfPresent(k, flight):
    val = flight.get(k)
    if val and val != KEY_NOT_PRESENT_STRING:
      potential_values[k].append(val)

  for flight in flights:
    flight_number = flights.get('flight_number')
    if flight_number:
      if flight_number == this_flight_number:
        AppendIfPresent('origin_friendly', flight)
        AppendIfPresent('origin_iata', flight)
        AppendIfPresent('destination_friendly', flight)
        AppendIfPresent('destination_iata', flight)
      if flight_number[:3] == this_flight_number[:3]:
        AppendIfPresent('airline_call_sign', flight)
        AppendIfPresent('airline_short_name', flight)
        AppendIfPresent('airline_full_name', flight)

  def Mode(lst):
    freq = {}
    for val in lst:
      freq[val] = freq.get(val, 0) + 1
    max_freq = max(freq.values())
    for val in freq:
      if freq[val] == max_freq:
        return val
    return None

  for attribute, lst in potential_values.items():
    if lst:
      derived_attributes[attribute] = Mode(lst)

  if derived_attributes:
    Log(
        'Derived attributes for %s based on past flights: %s' %
        (this_flight_number, str(derived_attributes)))
  return derived_attributes


def ParseFlightAwareJson(flight_json):
  """Strips relevant data about the flight from FlightAware feed.

  The FlightAware json has hundreds of fields about a flight, only a fraction
  of which are relevant to extract. Note that some of the fields are
  inconsistently populated (i.e.: scheduled and actual times for departure and
  take-off).

  Args:
    flight_json: Text representation of the FlightAware json about a single
      flight.

  Returns:
    Dictionary of flight attributes extracted from the FlightAware json.
  """
  flight = {}
  parsed_json = json.loads(flight_json)





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




    # isn't a simulation, then only read & do related processing for the
    # next dump if the last-modified timestamp indicates the file has been
    # updated since it was last read.
    tmp_timestamp = 0
    if not SIMULATION:
      dump_json_exists = os.path.exists(DUMP_JSON_FILE)
      if dump_json_exists:
        tmp_timestamp = os.path.getmtime(DUMP_JSON_FILE)
    if (SIMULATION and DumpJsonChanges()) or (
        not SIMULATION and dump_json_exists and
        tmp_timestamp > last_dump_json_timestamp):

      last_dump_json_timestamp = tmp_timestamp

      (persistent_nearby_aircraft,
       flight, now,
       json_desc_dict,
       persistent_path) = ScanForNewFlights(
           persistent_nearby_aircraft,
           persistent_path,
           configuration.get('log_jsons', False),
           flights)

      # because this might just be an updated instance of the previous
      # flight as more identifier information (squawk and or flight number)
      # comes in, we only want to process this if its a truly new flight
      new_flight_flag = ConfirmNewFlight(flight, flights)

      if new_flight_flag:
        flights.append(flight)
        remote, servo = RefreshArduinos(
            remote, servo,
            to_remote_q, to_servo_q, to_main_q, shutdown,
            flights, json_desc_dict, configuration, screen_history)

        if FlightMeetsDisplayCriteria(flight, configuration, log=True):
          personal_message = None  # Any personal message displayed now cleared
          flight_message = (
              FLAG_MSG_FLIGHT, CreateMessageAboutFlight(flight), flight)

          # display the next message about this flight now!
          next_message_time = time.time()




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