messageboard-2020-06-30-2131.py
01234567890123456789012345678901234567890123456789012345678901234567890123456789









152153154155156157158159160161162163164165166167168169170171   172173174175176177178179180181182183184185186187188189190191








255256257258259260261262263264265266267268269270271272273274 275276277278279280281282283284285286287288289290291292293294








359360361362363364365366367368369370371372373374375376377378 379380381382383384385386387388389390391392393394395396397398








11301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158 1159116011611162    11631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188








25322533253425352536253725382539254025412542254325442545254625472548254925502551 25522553255425552556255725582559256025612562256325642565256625672568256925702571








60496050605160526053605460556056605760586059606060616062606360646065606660676068 606960706071607260736074607560766077607860796080608160826083608460856086608760886089











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




# by hour throughout the day, over the last seven days, normalized to
# flights per day. This allows those parameters to be fine-tuned in a
# useful way. This file is the location, on the webserver, of that image,
# which needs to be in alignment with the html page that displays it.
HOURLY_IMAGE_FILE = 'hours.png'

# This is all messages that have been sent to the board since the last time
# the file was manually cleared. Newest messages are at the bottom. It is
# visible at the webserver.
#enumeration of all messages sent to board
ALL_MESSAGE_FILE = 'secure/all_messages.txt'
# This shows the most recent n messages sent to the board. Newest messages
# are at the top for easier viewing of "what did I miss".
ROLLING_MESSAGE_FILE = 'rolling_messages.txt'

STDERR_FILE = 'secure/stderr.txt'
BACKUP_FILE = 'secure/backup.txt'
SERVICE_VERIFICATION_FILE = 'secure/service-verification.txt'
UPTIMES_FILE = 'uptimes.php'
CODE_HISTORY_FILE = 'code_history.php'




FLAG_MSG_FLIGHT = 1  # basic flight details
FLAG_MSG_INSIGHT = 2  # random tidbit about a flight
FLAG_MSG_HISTOGRAM = 3  # histogram message
FLAG_MSG_CLEAR = 4  # a blank message to clear the screen
# user-entered message to display for some duration of time
FLAG_MSG_PERSONAL = 5

FLAG_INSIGHT_LAST_SEEN = 0
FLAG_INSIGHT_DIFF_AIRCRAFT = 1
FLAG_INSIGHT_NTH_FLIGHT = 2
FLAG_INSIGHT_GROUNDSPEED = 3
FLAG_INSIGHT_ALTITUDE = 4
FLAG_INSIGHT_VERTRATE = 5
FLAG_INSIGHT_FIRST_DEST = 6
FLAG_INSIGHT_FIRST_ORIGIN = 7
FLAG_INSIGHT_FIRST_AIRLINE = 8
FLAG_INSIGHT_FIRST_AIRCRAFT = 9
FLAG_INSIGHT_LONGEST_DELAY = 10
FLAG_INSIGHT_FLIGHT_DELAY_FREQUENCY = 11




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




#if running on raspberry, then need to prepend path to file names
if RASPBERRY_PI:
  PICKLE_FLIGHTS = MESSAGEBOARD_PATH + PICKLE_FLIGHTS
  PICKLE_DASHBOARD = MESSAGEBOARD_PATH + PICKLE_DASHBOARD
  LOGFILE = MESSAGEBOARD_PATH + LOGFILE
  PICKLE_DUMP_JSON_FILE = MESSAGEBOARD_PATH + PICKLE_DUMP_JSON_FILE
  PICKLE_FA_JSON_FILE = MESSAGEBOARD_PATH + PICKLE_FA_JSON_FILE
  PICKLE_SCREENS = MESSAGEBOARD_PATH + PICKLE_SCREENS
  CODE_REPOSITORY = MESSAGEBOARD_PATH

  HISTOGRAM_CONFIG_FILE = WEBSERVER_PATH + HISTOGRAM_CONFIG_FILE
  CONFIG_FILE = WEBSERVER_PATH + CONFIG_FILE
  ROLLING_MESSAGE_FILE = WEBSERVER_PATH + ROLLING_MESSAGE_FILE
  ALL_MESSAGE_FILE = WEBSERVER_PATH + ALL_MESSAGE_FILE
  ROLLING_LOGFILE = WEBSERVER_PATH + ROLLING_LOGFILE
  STDERR_FILE = WEBSERVER_PATH + STDERR_FILE
  BACKUP_FILE = WEBSERVER_PATH + BACKUP_FILE
  SERVICE_VERIFICATION_FILE = WEBSERVER_PATH + SERVICE_VERIFICATION_FILE
  UPTIMES_FILE = WEBSERVER_PATH + UPTIMES_FILE
  CODE_HISTORY_FILE = WEBSERVER_PATH + CODE_HISTORY_FILE


  HISTOGRAM_IMAGE_HTML = WEBSERVER_PATH + HISTOGRAM_IMAGE_HTML
  HOURLY_IMAGE_FILE = (
      WEBSERVER_PATH + WEBSERVER_IMAGE_RELATIVE_FOLDER + HOURLY_IMAGE_FILE)
  VERSION_REPOSITORY = WEBSERVER_PATH + VERSION_REPOSITORY

TIMEZONE = 'US/Pacific' # timezone of display
TZ = pytz.timezone(TIMEZONE)

# iata codes that we don't need to expand
KNOWN_AIRPORTS = ('SJC', 'SFO', 'OAK')

SPLITFLAP_CHARS_PER_LINE = 22
SPLITFLAP_LINE_COUNT = 6

DIRECTIONS_4 = ['N', 'E', 'S', 'W']
DIRECTIONS_8 = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']
DIRECTIONS_16 = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE',
                 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW']





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




AIRCRAFT_LENGTH['Bombardier Challenger 300 (twin-jet)'] = 20.92
AIRCRAFT_LENGTH['Bombardier Global Express (twin-jet)'] = (29.5 + 30.3) / 2
AIRCRAFT_LENGTH['Canadair Regional Jet CRJ-200 (twin-jet)'] = 26.77
AIRCRAFT_LENGTH['Canadair Regional Jet CRJ-700 (twin-jet)'] = 32.3
AIRCRAFT_LENGTH['Canadair Regional Jet CRJ-900 (twin-jet)'] = 36.2
AIRCRAFT_LENGTH['Canadair Challenger (twin-jet)'] = 20.9
AIRCRAFT_LENGTH['Canadair Challenger 350 (twin-jet)'] = 20.9
AIRCRAFT_LENGTH['Cessna Caravan (single-turboprop)'] = 11.46
AIRCRAFT_LENGTH['Cessna Citation CJ2+ (twin-jet)'] = 14.53
AIRCRAFT_LENGTH['Cessna Citation CJ3 (twin-jet)'] = 15.59
AIRCRAFT_LENGTH['Cessna Citation Excel/XLS (twin-jet)'] = 16.0
AIRCRAFT_LENGTH['Cessna Citation II (twin-jet)'] = 14.54
AIRCRAFT_LENGTH['Cessna Citation Latitude (twin-jet)'] = 18.97
AIRCRAFT_LENGTH['Cessna Citation Sovereign (twin-jet)'] = 19.35
AIRCRAFT_LENGTH['Cessna Citation V (twin-jet)'] = 14.91
AIRCRAFT_LENGTH['Cessna Citation X (twin-jet)'] = 22.04
AIRCRAFT_LENGTH['Cessna Citation Mustang (twin-jet)'] = 12.37
AIRCRAFT_LENGTH['Cessna Skyhawk (piston-single)'] = 8.28
AIRCRAFT_LENGTH['Cessna Skylane (piston-single)'] = 8.84
AIRCRAFT_LENGTH['Cessna T206 Turbo Stationair (piston-single)'] = 8.61

AIRCRAFT_LENGTH['Dassault Falcon 900 (tri-jet)'] = 20.21
AIRCRAFT_LENGTH['Embraer 170/175 (twin-jet)'] = (29.90 + 31.68) / 2
AIRCRAFT_LENGTH['Embraer Phenom 300 (twin-jet)'] = 15.9
AIRCRAFT_LENGTH['EMBRAER 175 (long wing) (twin-jet)'] = 31.68
AIRCRAFT_LENGTH['Embraer ERJ-135 (twin-jet)'] = 26.33
AIRCRAFT_LENGTH['Gulfstream Aerospace Gulfstream G550 (twin-jet)'] = 29.39
AIRCRAFT_LENGTH['Gulfstream Aerospace Gulfstream IV (twin-jet)'] = 26.92
AIRCRAFT_LENGTH['Learjet 45 (twin-jet)'] = 17.68
AIRCRAFT_LENGTH['McDonnell Douglas MD-11 (tri-jet)'] = 61.6
AIRCRAFT_LENGTH['Pilatus PC-12 (single-turboprop)'] = 14.4


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




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




      if v.isdigit():
        v = int(v)
      else:
        try:
          v = float(v)
        except ValueError:
          pass
      settings_dict[k] = v

  return settings_dict


def RemoveSetting(configuration, setting):
  """Removes the named setting from the configuration file."""
  configuration.pop(setting)
  configuration = BuildSettings(configuration)
  WriteFile(CONFIG_FILE, configuration)
  return configuration


def WriteFile(filename, text, log_exception=False):
  """Writes the text to the file, returning boolean indicating success.

  Args:
    filename: string of the filename to open, potentially also including the
      full path.
    text: the text to write
    log_exception: boolean indicating whether to log an exception if file not
      found.


  Returns:
    Boolean indicating whether the write was successful.
  """




  try:
    with open(filename, 'w') as content_file:
      content_file.write(text)
  except IOError:
    if log_exception:
      Log('Unable to write to '+filename)
    return False
  return True


def PrependFileName(full_path, prefix):
  """Converts /dir/file.png to /dir/prefixfile.png."""
  directory, file_name = os.path.split(full_path)
  file_name = prefix+file_name
  return os.path.join(directory, file_name)


def UnpickleObjectFromFile(
    full_path,
    date_segmentation,
    max_days=None,
    filenames=False,
    heartbeat=False):
  """Load a repository of pickled data into memory.

  Args:




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




  last_seen = [
      f for f in flights[:-1]
      if DisplayFlightNumber(f) == this_flight_number]

  # Last time this same flight flew a materially different type of aircraft
  if last_seen and 'flight_number' in this_flight:
    last_flight = last_seen[-1]

    last_aircraft = last_flight.get('aircraft_type_friendly')
    last_aircraft_length = AIRCRAFT_LENGTH.get(last_aircraft, 0)

    this_aircraft = this_flight.get('aircraft_type_friendly')
    this_aircraft_length = AIRCRAFT_LENGTH.get(this_aircraft, 0)

    this_likely_commercial_flight = (
        this_flight.get('origin_iata') and this_flight.get('destination_iata'))
    if (this_likely_commercial_flight and this_aircraft
        and not this_aircraft_length):
      Log('%s used in a flight with defined origin & destination but yet is '
          'missing length details' % this_aircraft, file=LOGFILE)


    likely_same_commercial_flight = (
        last_flight.get('origin_iata') == this_flight.get('origin_iata') and
        last_flight.get(
            'destination_iata') == this_flight.get('destination_iata') and
        last_flight.get(
            'airline_call_sign') == this_flight.get('airline_call_sign'))

    this_aircraft_bigger = False
    last_aircraft_bigger = False
    if (likely_same_commercial_flight and
        this_aircraft_length > last_aircraft_length * (
            1 + percent_size_difference)):
      this_aircraft_bigger = True
      comparative_text = 'larger'
    elif (likely_same_commercial_flight and
          last_aircraft_length > this_aircraft_length * (
              1 + percent_size_difference)):
      last_aircraft_bigger = True
      comparative_text = 'smaller'




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




      flights = UnpickleObjectFromFile(f, False)
      for (n, flight) in enumerate(flights):
        if n/25 == int(n/25):
          print(' - %d' % n)
        CreateFlightInsights(
            flights[:n+1], configuration.get('insights', 'hide'), {})
        PickleObjectToFile(flight, tmp_f, False)

      if mtime == os.path.getmtime(f):
        shutil.move(tmp_f, f)
      else:
        print('Aborted: failed to bootstrap %s: file changed while in process'
              % full_path)
        return


def ResetLogs(config):
  """Clears the non-scrolling logs if reset_logs in config."""
  if 'reset_logs' in config:
    Log('Reset logs')

    for f in (STDERR_FILE, BACKUP_FILE, SERVICE_VERIFICATION_FILE):
      if RemoveFile(f):
        open(f, 'a').close()
    config.pop('reset_logs')
    config = BuildSettings(config)
    WriteFile(CONFIG_FILE, config)
  return config


def CheckTemperature():
  """Turn on fan if temperature exceeds threshold."""
  if RASPBERRY_PI:
    temperature = gpiozero.CPUTemperature().temperature
    if temperature > TEMP_FAN_TURN_ON_CELSIUS:
      UpdateStatusLight(GPIO_FAN, True, 'Temperature: %.1f' % temperature)
    elif temperature < TEMP_FAN_TURN_OFF_CELSIUS:
      UpdateStatusLight(GPIO_FAN, False)


pin_values = {}  # caches last set value
def SetPinMode():




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





01234567890123456789012345678901234567890123456789012345678901234567890123456789









152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194








258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298








363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403








1135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198








25422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582








606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101











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




# by hour throughout the day, over the last seven days, normalized to
# flights per day. This allows those parameters to be fine-tuned in a
# useful way. This file is the location, on the webserver, of that image,
# which needs to be in alignment with the html page that displays it.
HOURLY_IMAGE_FILE = 'hours.png'

# This is all messages that have been sent to the board since the last time
# the file was manually cleared. Newest messages are at the bottom. It is
# visible at the webserver.
#enumeration of all messages sent to board
ALL_MESSAGE_FILE = 'secure/all_messages.txt'
# This shows the most recent n messages sent to the board. Newest messages
# are at the top for easier viewing of "what did I miss".
ROLLING_MESSAGE_FILE = 'rolling_messages.txt'

STDERR_FILE = 'secure/stderr.txt'
BACKUP_FILE = 'secure/backup.txt'
SERVICE_VERIFICATION_FILE = 'secure/service-verification.txt'
UPTIMES_FILE = 'uptimes.php'
CODE_HISTORY_FILE = 'code_history.php'

# This keeps a log of all aircraft not yet cataloged in the AIRCRAFT_LENGTH dict
NEW_AIRCRAFT_FILE = 'secure/new_aircraft.txt'

FLAG_MSG_FLIGHT = 1  # basic flight details
FLAG_MSG_INSIGHT = 2  # random tidbit about a flight
FLAG_MSG_HISTOGRAM = 3  # histogram message
FLAG_MSG_CLEAR = 4  # a blank message to clear the screen
# user-entered message to display for some duration of time
FLAG_MSG_PERSONAL = 5

FLAG_INSIGHT_LAST_SEEN = 0
FLAG_INSIGHT_DIFF_AIRCRAFT = 1
FLAG_INSIGHT_NTH_FLIGHT = 2
FLAG_INSIGHT_GROUNDSPEED = 3
FLAG_INSIGHT_ALTITUDE = 4
FLAG_INSIGHT_VERTRATE = 5
FLAG_INSIGHT_FIRST_DEST = 6
FLAG_INSIGHT_FIRST_ORIGIN = 7
FLAG_INSIGHT_FIRST_AIRLINE = 8
FLAG_INSIGHT_FIRST_AIRCRAFT = 9
FLAG_INSIGHT_LONGEST_DELAY = 10
FLAG_INSIGHT_FLIGHT_DELAY_FREQUENCY = 11




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




#if running on raspberry, then need to prepend path to file names
if RASPBERRY_PI:
  PICKLE_FLIGHTS = MESSAGEBOARD_PATH + PICKLE_FLIGHTS
  PICKLE_DASHBOARD = MESSAGEBOARD_PATH + PICKLE_DASHBOARD
  LOGFILE = MESSAGEBOARD_PATH + LOGFILE
  PICKLE_DUMP_JSON_FILE = MESSAGEBOARD_PATH + PICKLE_DUMP_JSON_FILE
  PICKLE_FA_JSON_FILE = MESSAGEBOARD_PATH + PICKLE_FA_JSON_FILE
  PICKLE_SCREENS = MESSAGEBOARD_PATH + PICKLE_SCREENS
  CODE_REPOSITORY = MESSAGEBOARD_PATH

  HISTOGRAM_CONFIG_FILE = WEBSERVER_PATH + HISTOGRAM_CONFIG_FILE
  CONFIG_FILE = WEBSERVER_PATH + CONFIG_FILE
  ROLLING_MESSAGE_FILE = WEBSERVER_PATH + ROLLING_MESSAGE_FILE
  ALL_MESSAGE_FILE = WEBSERVER_PATH + ALL_MESSAGE_FILE
  ROLLING_LOGFILE = WEBSERVER_PATH + ROLLING_LOGFILE
  STDERR_FILE = WEBSERVER_PATH + STDERR_FILE
  BACKUP_FILE = WEBSERVER_PATH + BACKUP_FILE
  SERVICE_VERIFICATION_FILE = WEBSERVER_PATH + SERVICE_VERIFICATION_FILE
  UPTIMES_FILE = WEBSERVER_PATH + UPTIMES_FILE
  CODE_HISTORY_FILE = WEBSERVER_PATH + CODE_HISTORY_FILE
  NEW_AIRCRAFT_FILE = WEBSERVER_PATH + NEW_AIRCRAFT_FILE

  HISTOGRAM_IMAGE_HTML = WEBSERVER_PATH + HISTOGRAM_IMAGE_HTML
  HOURLY_IMAGE_FILE = (
      WEBSERVER_PATH + WEBSERVER_IMAGE_RELATIVE_FOLDER + HOURLY_IMAGE_FILE)
  VERSION_REPOSITORY = WEBSERVER_PATH + VERSION_REPOSITORY

TIMEZONE = 'US/Pacific' # timezone of display
TZ = pytz.timezone(TIMEZONE)

# iata codes that we don't need to expand
KNOWN_AIRPORTS = ('SJC', 'SFO', 'OAK')

SPLITFLAP_CHARS_PER_LINE = 22
SPLITFLAP_LINE_COUNT = 6

DIRECTIONS_4 = ['N', 'E', 'S', 'W']
DIRECTIONS_8 = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']
DIRECTIONS_16 = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE',
                 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW']





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




AIRCRAFT_LENGTH['Bombardier Challenger 300 (twin-jet)'] = 20.92
AIRCRAFT_LENGTH['Bombardier Global Express (twin-jet)'] = (29.5 + 30.3) / 2
AIRCRAFT_LENGTH['Canadair Regional Jet CRJ-200 (twin-jet)'] = 26.77
AIRCRAFT_LENGTH['Canadair Regional Jet CRJ-700 (twin-jet)'] = 32.3
AIRCRAFT_LENGTH['Canadair Regional Jet CRJ-900 (twin-jet)'] = 36.2
AIRCRAFT_LENGTH['Canadair Challenger (twin-jet)'] = 20.9
AIRCRAFT_LENGTH['Canadair Challenger 350 (twin-jet)'] = 20.9
AIRCRAFT_LENGTH['Cessna Caravan (single-turboprop)'] = 11.46
AIRCRAFT_LENGTH['Cessna Citation CJ2+ (twin-jet)'] = 14.53
AIRCRAFT_LENGTH['Cessna Citation CJ3 (twin-jet)'] = 15.59
AIRCRAFT_LENGTH['Cessna Citation Excel/XLS (twin-jet)'] = 16.0
AIRCRAFT_LENGTH['Cessna Citation II (twin-jet)'] = 14.54
AIRCRAFT_LENGTH['Cessna Citation Latitude (twin-jet)'] = 18.97
AIRCRAFT_LENGTH['Cessna Citation Sovereign (twin-jet)'] = 19.35
AIRCRAFT_LENGTH['Cessna Citation V (twin-jet)'] = 14.91
AIRCRAFT_LENGTH['Cessna Citation X (twin-jet)'] = 22.04
AIRCRAFT_LENGTH['Cessna Citation Mustang (twin-jet)'] = 12.37
AIRCRAFT_LENGTH['Cessna Skyhawk (piston-single)'] = 8.28
AIRCRAFT_LENGTH['Cessna Skylane (piston-single)'] = 8.84
AIRCRAFT_LENGTH['Cessna T206 Turbo Stationair (piston-single)'] = 8.61
AIRCRAFT_LENGTH['Cirrus SR-22 (piston-single)'] = 7.92
AIRCRAFT_LENGTH['Dassault Falcon 900 (tri-jet)'] = 20.21
AIRCRAFT_LENGTH['Embraer 170/175 (twin-jet)'] = (29.90 + 31.68) / 2
AIRCRAFT_LENGTH['Embraer Phenom 300 (twin-jet)'] = 15.9
AIRCRAFT_LENGTH['EMBRAER 175 (long wing) (twin-jet)'] = 31.68
AIRCRAFT_LENGTH['Embraer ERJ-135 (twin-jet)'] = 26.33
AIRCRAFT_LENGTH['Gulfstream Aerospace Gulfstream G550 (twin-jet)'] = 29.39
AIRCRAFT_LENGTH['Gulfstream Aerospace Gulfstream IV (twin-jet)'] = 26.92
AIRCRAFT_LENGTH['Learjet 45 (twin-jet)'] = 17.68
AIRCRAFT_LENGTH['McDonnell Douglas MD-11 (tri-jet)'] = 61.6
AIRCRAFT_LENGTH['Pilatus PC-12 (single-turboprop)'] = 14.4


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




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




      if v.isdigit():
        v = int(v)
      else:
        try:
          v = float(v)
        except ValueError:
          pass
      settings_dict[k] = v

  return settings_dict


def RemoveSetting(configuration, setting):
  """Removes the named setting from the configuration file."""
  configuration.pop(setting)
  configuration = BuildSettings(configuration)
  WriteFile(CONFIG_FILE, configuration)
  return configuration


def WriteFile(filename, text, log_exception=False, append=False):
  """Writes the text to the file, returning boolean indicating success.

  Args:
    filename: string of the filename to open, potentially also including the
      full path.
    text: the text to write
    log_exception: boolean indicating whether to log an exception on IOError.
    append: boolean indicating whether to append. True indicates we should
      append; False indicates we should write.

  Returns:
    Boolean indicating whether the write was successful.
  """
  file_mode = 'w'
  if append:
    file_mode = 'a'
    text += '\n'
  try:
    with open(filename, file_mode) as content_file:
      content_file.write(text)
  except IOError:
    if log_exception:
      Log('Unable to write or append to %s' % filename)
    return False
  return True


def PrependFileName(full_path, prefix):
  """Converts /dir/file.png to /dir/prefixfile.png."""
  directory, file_name = os.path.split(full_path)
  file_name = prefix+file_name
  return os.path.join(directory, file_name)


def UnpickleObjectFromFile(
    full_path,
    date_segmentation,
    max_days=None,
    filenames=False,
    heartbeat=False):
  """Load a repository of pickled data into memory.

  Args:




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




  last_seen = [
      f for f in flights[:-1]
      if DisplayFlightNumber(f) == this_flight_number]

  # Last time this same flight flew a materially different type of aircraft
  if last_seen and 'flight_number' in this_flight:
    last_flight = last_seen[-1]

    last_aircraft = last_flight.get('aircraft_type_friendly')
    last_aircraft_length = AIRCRAFT_LENGTH.get(last_aircraft, 0)

    this_aircraft = this_flight.get('aircraft_type_friendly')
    this_aircraft_length = AIRCRAFT_LENGTH.get(this_aircraft, 0)

    this_likely_commercial_flight = (
        this_flight.get('origin_iata') and this_flight.get('destination_iata'))
    if (this_likely_commercial_flight and this_aircraft
        and not this_aircraft_length):
      Log('%s used in a flight with defined origin & destination but yet is '
          'missing length details' % this_aircraft, file=LOGFILE)
      WriteFile(NEW_AIRCRAFT_FILE, this_aircraft, append=True)

    likely_same_commercial_flight = (
        last_flight.get('origin_iata') == this_flight.get('origin_iata') and
        last_flight.get(
            'destination_iata') == this_flight.get('destination_iata') and
        last_flight.get(
            'airline_call_sign') == this_flight.get('airline_call_sign'))

    this_aircraft_bigger = False
    last_aircraft_bigger = False
    if (likely_same_commercial_flight and
        this_aircraft_length > last_aircraft_length * (
            1 + percent_size_difference)):
      this_aircraft_bigger = True
      comparative_text = 'larger'
    elif (likely_same_commercial_flight and
          last_aircraft_length > this_aircraft_length * (
              1 + percent_size_difference)):
      last_aircraft_bigger = True
      comparative_text = 'smaller'




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




      flights = UnpickleObjectFromFile(f, False)
      for (n, flight) in enumerate(flights):
        if n/25 == int(n/25):
          print(' - %d' % n)
        CreateFlightInsights(
            flights[:n+1], configuration.get('insights', 'hide'), {})
        PickleObjectToFile(flight, tmp_f, False)

      if mtime == os.path.getmtime(f):
        shutil.move(tmp_f, f)
      else:
        print('Aborted: failed to bootstrap %s: file changed while in process'
              % full_path)
        return


def ResetLogs(config):
  """Clears the non-scrolling logs if reset_logs in config."""
  if 'reset_logs' in config:
    Log('Reset logs')
    for f in (
        STDERR_FILE, BACKUP_FILE, SERVICE_VERIFICATION_FILE, NEW_AIRCRAFT_FILE):
      if RemoveFile(f):
        open(f, 'a').close()
    config.pop('reset_logs')
    config = BuildSettings(config)
    WriteFile(CONFIG_FILE, config)
  return config


def CheckTemperature():
  """Turn on fan if temperature exceeds threshold."""
  if RASPBERRY_PI:
    temperature = gpiozero.CPUTemperature().temperature
    if temperature > TEMP_FAN_TURN_ON_CELSIUS:
      UpdateStatusLight(GPIO_FAN, True, 'Temperature: %.1f' % temperature)
    elif temperature < TEMP_FAN_TURN_OFF_CELSIUS:
      UpdateStatusLight(GPIO_FAN, False)


pin_values = {}  # caches last set value
def SetPinMode():




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