messageboard-2020-06-09-1619.py
01234567890123456789012345678901234567890123456789012345678901234567890123456789









949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134








239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281








4426442744284429443044314432443344344435443644374438443944404441444244434444444544464447      44484449445044514452445344544455445644574458445944604461446244634464446544664467








475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776 4777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804











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




SPLIT_SIMULATION_FLIGHT_PICKLE = False

# Status data about messageboard - is it running, etc.  Specifically, has tuples
# of data (timestamp, system_id, status), where system_id is either the pin id of GPIO,
# or a 0 to indicate overall system, and status is boolean
PICKLE_DASHBOARD = 'pickle/dashboard.pk'

CACHED_ELEMENT_PREFIX = 'cached_'

# This web-exposed file is used for non-error messages that might highlight data or
# code logic to check into. It is only cleared out manually.
LOGFILE = 'log.txt'
# Identical to the LOGFILE, except it includes just the most recent n lines. Newest
# lines are at the end.
ROLLING_LOGFILE = 'rolling_log.txt' #file for error messages

ROLLING_LOG_SIZE = 1000  # default number of lines which may be overridden by settings file

# Users can trigger .png histograms analogous to the text ones from the web interface;
# this is the folder (within WEBSERVER_PATH) where those files are placed
WEBSERVER_IMAGE_FOLDER = 'images/'
# Multiple histograms can be generated, i.e. for airline, aircraft, day of week, etc.
# The output files are named by the prefix & suffix, i.e.: prefix + type + . + suffix,
# as in histogram_aircraft.png. These names match up to the names expected by the html
# page that displays the images. Also, note that the suffix is interpreted by matplotlib
# to identify the image format to create.
HISTOGRAM_IMAGE_PREFIX = 'histogram_'
HISTOGRAM_IMAGE_SUFFIX = 'png'
HISTOGRAM_IMAGE_HTML = 'histograms.html'

# This file indicates a pending request for histograms - either png, text-based, or
# both; once it is processed, this file is deleted. The contents are concatenated key-value
# pairs, histogram=all;histogram_history=24h; etc.
HISTOGRAM_CONFIG_FILE = 'secure/histogram.txt'
HISTOGRAM_BOOLEANS = ('histogram_data_summary')
# This contains concatenated key-value configuration attributes in a similar format
# to the HISTOGRAM_CONFIG_FILE that are exposed to the user via the web interface or,
# for a subset of them, through the Arduino interface. They are polled at every iteration
# so that the most current value is always leveraged by the running software.
CONFIG_FILE = 'secure/settings.txt'
CONFIG_BOOLEANS = ('setting_screen_enabled', 'next_flight', 'reset_logs', 'log_jsons')




                            <----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
  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

  WEBSERVER_IMAGE_FOLDER = WEBSERVER_PATH + WEBSERVER_IMAGE_FOLDER
  HISTOGRAM_IMAGE_HTML = WEBSERVER_PATH + HISTOGRAM_IMAGE_HTML
  HOURLY_IMAGE_FILE = WEBSERVER_IMAGE_FOLDER + HOURLY_IMAGE_FILE
  VERSION_REPOSITORY = WEBSERVER_PATH + VERSION_REPOSITORY



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

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

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']

HOURS = ['12a', ' 1a', ' 2a', ' 3a', ' 4a', ' 5a', ' 6a', ' 7a',
         ' 8a', ' 9a', '10a', '11a', '12p', ' 1p', ' 2p', ' 3p',
         ' 4p', ' 5p', ' 6p', ' 7p', ' 8p', ' 9p', '10p', '11p']




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




    this_histogram = which_histograms
    if this_histogram == 'all':
      this_histogram = histogram['generate']
    (key, sort, title, hours) = HistogramSettingsKeySortTitle(
        this_histogram, hours, flights)

    # if multiple histograms are getting generated, this might take a few seconds;
    # logging a heartbeat with each histogram ensures that monitoring.py does not
    # mistake this pause for a hang.
    if heartbeat:
      Heartbeat()

    CreateSingleHistogramChart(
        flights,
        key,
        sort,
        title,
        truncate=histogram.get('truncate', TRUNCATE),
        hours=hours,
        exhaustive=histogram.get('exhaustive', False))
    filename = filename_prefix + histogram['generate'] + '.' + filename_suffix
    filepath = WEBSERVER_IMAGE_FOLDER + filename






    matplotlib.pyplot.savefig(filepath)
    matplotlib.pyplot.close()
    histograms_generated.append((histogram['generate'], filename))

  return histograms_generated


def MessageboardHistograms(
    flights,
    which_histograms,
    how_much_history,
    max_screens,
    data_summary):
  """Generates multiple split flap screen histograms.

  Args:
    flights: the iterable of the raw data from which the histogram will be generated;
      each element of the iterable is a dictionary, that contains at least the key
      'now', and depending on other parameters, also potentially
      'min_feet' amongst others.




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





    generated_histograms = ImageHistograms(
        flights,
        histogram_settings['histogram'],
        histogram_settings['histogram_history'],
        filename_prefix=HISTOGRAM_IMAGE_PREFIX + epoch_string)
    html_lines = ReadFile(HISTOGRAM_IMAGE_HTML).split('\n')
    replaced_images = []
    for identifier, new_filename in generated_histograms:
      # for each histogram, find the html_line with the matching id
      # Example line: <img id="destination" src="images/histogram_destination.png"><p>
      identifier = '"%s"' % identifier  # add quotations to make sure its complete
      n, line = None, None  # addresses pylint complaint
      found = False
      for n, line in enumerate(html_lines):
        if identifier in line:
          found = True
          break
        found = False
      if found:
        start_char = line.find(WEBSERVER_IMAGE_FOLDER) + len(WEBSERVER_IMAGE_FOLDER)
        end_character = (
            line.find(HISTOGRAM_IMAGE_SUFFIX, start_char) + len(HISTOGRAM_IMAGE_SUFFIX))
        old_filename = line[start_char:end_character]

        line = line.replace(old_filename, new_filename)
        html_lines[n] = line
        replaced_images.append(line[start_char:end_character])
    new_html = '\n'.join(html_lines)
    WriteFile(HISTOGRAM_IMAGE_HTML, new_html)

    # Remove those obsoleted files
    for f in old_filename:
      RemoveFile(f)

  return histogram_messages


def SaveFlightsByAltitudeDistanceCSV(
    flights,
    max_days=0,
    filename='flights_by_alt_dist.csv',
    precision=100):
  """Extracts hourly histogram into text file for a variety of altitudes and distances.

  Generates a csv with 26 columns:
  - col#1: altitude (in feet)
  - col#2: distance (in feet)
  - cols#3-26: hour of the day

  The first row is a header row; subsequent rows list the number of flights that have
  occurred in the last max_days with an altitude and min distance less than that identified
  in the first two columns. Each row increments elevation or altitude by precision feet,




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





01234567890123456789012345678901234567890123456789012345678901234567890123456789









949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134








239240241242243244245246247248249250251252253254255256257258 259260261262263264265266267268269270271272273274275276277278279280








442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472








47584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810











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




SPLIT_SIMULATION_FLIGHT_PICKLE = False

# Status data about messageboard - is it running, etc.  Specifically, has tuples
# of data (timestamp, system_id, status), where system_id is either the pin id of GPIO,
# or a 0 to indicate overall system, and status is boolean
PICKLE_DASHBOARD = 'pickle/dashboard.pk'

CACHED_ELEMENT_PREFIX = 'cached_'

# This web-exposed file is used for non-error messages that might highlight data or
# code logic to check into. It is only cleared out manually.
LOGFILE = 'log.txt'
# Identical to the LOGFILE, except it includes just the most recent n lines. Newest
# lines are at the end.
ROLLING_LOGFILE = 'rolling_log.txt' #file for error messages

ROLLING_LOG_SIZE = 1000  # default number of lines which may be overridden by settings file

# Users can trigger .png histograms analogous to the text ones from the web interface;
# this is the folder (within WEBSERVER_PATH) where those files are placed
WEBSERVER_IMAGE_RELATIVE_FOLDER = 'images/'
# Multiple histograms can be generated, i.e. for airline, aircraft, day of week, etc.
# The output files are named by the prefix & suffix, i.e.: prefix + type + . + suffix,
# as in histogram_aircraft.png. These names match up to the names expected by the html
# page that displays the images. Also, note that the suffix is interpreted by matplotlib
# to identify the image format to create.
HISTOGRAM_IMAGE_PREFIX = 'histogram_'
HISTOGRAM_IMAGE_SUFFIX = 'png'
HISTOGRAM_IMAGE_HTML = 'histograms.html'

# This file indicates a pending request for histograms - either png, text-based, or
# both; once it is processed, this file is deleted. The contents are concatenated key-value
# pairs, histogram=all;histogram_history=24h; etc.
HISTOGRAM_CONFIG_FILE = 'secure/histogram.txt'
HISTOGRAM_BOOLEANS = ('histogram_data_summary')
# This contains concatenated key-value configuration attributes in a similar format
# to the HISTOGRAM_CONFIG_FILE that are exposed to the user via the web interface or,
# for a subset of them, through the Arduino interface. They are polled at every iteration
# so that the most current value is always leveraged by the running software.
CONFIG_FILE = 'secure/settings.txt'
CONFIG_BOOLEANS = ('setting_screen_enabled', 'next_flight', 'reset_logs', 'log_jsons')




                            <----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
  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


  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)

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

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']

HOURS = ['12a', ' 1a', ' 2a', ' 3a', ' 4a', ' 5a', ' 6a', ' 7a',
         ' 8a', ' 9a', '10a', '11a', '12p', ' 1p', ' 2p', ' 3p',
         ' 4p', ' 5p', ' 6p', ' 7p', ' 8p', ' 9p', '10p', '11p']




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




    this_histogram = which_histograms
    if this_histogram == 'all':
      this_histogram = histogram['generate']
    (key, sort, title, hours) = HistogramSettingsKeySortTitle(
        this_histogram, hours, flights)

    # if multiple histograms are getting generated, this might take a few seconds;
    # logging a heartbeat with each histogram ensures that monitoring.py does not
    # mistake this pause for a hang.
    if heartbeat:
      Heartbeat()

    CreateSingleHistogramChart(
        flights,
        key,
        sort,
        title,
        truncate=histogram.get('truncate', TRUNCATE),
        hours=hours,
        exhaustive=histogram.get('exhaustive', False))

    filename = (
        WEBSERVER_IMAGE_RELATIVE_FOLDER +  # i.e.: images/
        filename_prefix +                  # i.e.: histogram_
        histogram['generate'] +            # i.e.: destination
        '.' + filename_suffix)             # i.e.: .png
    filepath = WEBSERVER_PATH + filename   # i.e.: /var/www/html/ + filename

    matplotlib.pyplot.savefig(filepath)
    matplotlib.pyplot.close()
    histograms_generated.append((histogram['generate'], filename))

  return histograms_generated


def MessageboardHistograms(
    flights,
    which_histograms,
    how_much_history,
    max_screens,
    data_summary):
  """Generates multiple split flap screen histograms.

  Args:
    flights: the iterable of the raw data from which the histogram will be generated;
      each element of the iterable is a dictionary, that contains at least the key
      'now', and depending on other parameters, also potentially
      'min_feet' amongst others.




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





    generated_histograms = ImageHistograms(
        flights,
        histogram_settings['histogram'],
        histogram_settings['histogram_history'],
        filename_prefix=HISTOGRAM_IMAGE_PREFIX + epoch_string)
    html_lines = ReadFile(HISTOGRAM_IMAGE_HTML).split('\n')
    replaced_images = []
    for identifier, new_filename in generated_histograms:
      # for each histogram, find the html_line with the matching id
      # Example line: <img id="destination" src="images/histogram_destination.png"><p>
      identifier = '"%s"' % identifier  # add quotations to make sure its complete
      n, line = None, None  # addresses pylint complaint
      found = False
      for n, line in enumerate(html_lines):
        if identifier in line:
          found = True
          break
        found = False
      if found:
        start_char = line.find(WEBSERVER_IMAGE_RELATIVE_FOLDER)
        end_character = (
            line.find(HISTOGRAM_IMAGE_SUFFIX, start_char) + len(HISTOGRAM_IMAGE_SUFFIX))
        old_filename = line[start_char:end_character]
        print(old_filename, new_filename)
        line = line.replace(old_filename, new_filename)
        html_lines[n] = line
        replaced_images.append(line[start_char:end_character])
    new_html = '\n'.join(html_lines)
    WriteFile(HISTOGRAM_IMAGE_HTML, new_html)

    # Remove those obsoleted files
    for f in replaced_images:
      RemoveFile(f)

  return histogram_messages


def SaveFlightsByAltitudeDistanceCSV(
    flights,
    max_days=0,
    filename='flights_by_alt_dist.csv',
    precision=100):
  """Extracts hourly histogram into text file for a variety of altitudes and distances.

  Generates a csv with 26 columns:
  - col#1: altitude (in feet)
  - col#2: distance (in feet)
  - cols#3-26: hour of the day

  The first row is a header row; subsequent rows list the number of flights that have
  occurred in the last max_days with an altitude and min distance less than that identified
  in the first two columns. Each row increments elevation or altitude by precision feet,




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