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









1780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807                  18081809181018111812181318141815181618171818181918201821182218231824182518261827








413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159 4160    416141624163416441654166416741684169417041714172417341744175417641774178417941804181








41964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227 4228422942304231423242334234423542364237423842394240       42414242424342444245424642474248424942504251425242534254425542564257425842594260








42724273427442754276427742784279428042814282428342844285428642874288428942904291                              429242934294429542964297429842994300430143024303430443054306 43074308430943104311431243134314431543164317431843194320432143224323   4324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351       43524353435443554356 4357435843594360 436143624363436443654366436743684369437043714372437343744375437643774378437943804381








44394440444144424443444444454446444744484449445044514452445344544455445644574458            445944604461446244634464 44654466446744684469447044714472447344744475447644774478447944804481448244834484








4638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689











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





  Returns:
    String identifying either the airline, or Unknown if not available.
  """
  airline = flight.get('airline_short_name', flight.get('airline_full_name'))

  # 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):
  """Returns length (in meters) of aircraft, or 0 if unknown."""
  aircraft = flight.get('aircraft_type_friendly')
  if not aircraft:
    return 0
  if aircraft not in AIRCRAFT_LENGTH:
    return 0
  return AIRCRAFT_LENGTH[aircraft]




















def DisplayAircraft(flight):
  """Provides a display-ready string about the aircraft used.

  Args:
    flight: dictionary with key-value attributes about the flight.

  Returns:
    Aircraft string if available; empty string otherwise.
  """
  aircraft = flight.get('aircraft_type_friendly')
  if aircraft:
    aircraft = aircraft.replace('(twin-jet)', '(twin)')
    aircraft = aircraft.replace('(quad-jet)', '(quad)')
    aircraft = aircraft.replace('Regional Jet ', '')
    aircraft = aircraft[:SPLITFLAP_CHARS_PER_LINE]
  else:
    aircraft = ''
  return aircraft




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




      keyfunction,
      sort_type,
      truncate=truncate,
      hours=hours,
      max_distance_feet=max_distance_feet,
      max_altitude_feet=max_altitude_feet,
      normalize_factor=normalize_factor,
      exhaustive=exhaustive)
  if position:
    matplotlib.pyplot.subplot(*position)
  matplotlib.pyplot.figure(figsize=figsize_inches)
  values_coordinates = numpy.arange(len(keys))
  matplotlib.pyplot.bar(values_coordinates, values)

  # The filtering may have removed any flight data, or there may be none to start
  if not filtered_data:
    return

  earliest_flight_time = int(filtered_data[0]['now'])
  last_flight_time = int(filtered_data[-1]['now'])
  date_range_string = ' (%d flights over last %s hours)' % (
      sum(values), SecondsToDdHh(last_flight_time - earliest_flight_time))

  matplotlib.pyplot.title(title + date_range_string)






  matplotlib.pyplot.subplots_adjust(bottom=0.15, left=0.09, right=0.99, top=0.92)

  matplotlib.pyplot.xticks(
      values_coordinates, keys, rotation='vertical', wrap=True,
      horizontalalignment='right',
      verticalalignment='center')


def HistogramSettingsHours(how_much_history):
  """Extracts the desired history (in hours) from the histogram configuration string.

  Args:
    how_much_history: string from the histogram config file.

  Returns:
    Number of hours of history to include in the histogram.
  """
  if how_much_history == 'today':
    hours = HoursSinceMidnight()
  elif how_much_history == '24h':
    hours = HOURS_IN_DAY




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




  Args:
    max_screens: string from the histogram config file.

  Returns:
    Number of maximum number of screens to display for a splitflap histogram.
  """
  if max_screens == '_1':
    screen_limit = 1
  elif max_screens == '_2':
    screen_limit = 2
  elif max_screens == '_5':
    screen_limit = 5
  elif max_screens == 'all':
    screen_limit = 0  # no limit on screens
  else:
    Log('Histogram form has invalid value for max_screens: %s' % max_screens)
    screen_limit = 1
  return screen_limit


def HistogramSettingsKeySortTitle(which, hours, max_altitude=45000):
  """Provides the arguments necessary to generate a histogram from the config string.

  The same parameters are used to generate either a splitflap text or web-rendered
  histogram in terms of the histogram title, the keyfunction, and how to sort the keys.
  For a given histogram name (based on the names defined in the histogram config file),
  this provides those parameters.

  Args:
    which: string from the histogram config file indicating the histogram to provide
      settings for.
    hours: how many hours of histogram data have been requested.

    max_altitude: indicates the maximum altitude that should be included on the
      altitude labels.

  Returns:
    A 4-tuple of the parameters used by either CreateSingleHistogramChart or
    MessageboardHistogram, of the keyfunction, sort, title, and hours.
  """
  def DivideAndFormat(dividend, divisor):
    if dividend is None:
      return KEY_NOT_PRESENT_STRING
    if isinstance(dividend, numbers.Number):
      return '%2d' % round(dividend / divisor)
    return dividend[:2]








  if which == 'destination':
    key = lambda k: k.get('destination_iata', KEY_NOT_PRESENT_STRING)
    sort = 'value'
    title = 'Destination'
  elif which == 'origin':
    key = lambda k: k.get('origin_iata', KEY_NOT_PRESENT_STRING)
    sort = 'value'
    title = 'Origin'
  elif which == 'hour':
    key = lambda k: DisplayTime(k, '%H')
    sort = 'key'
    title = 'Hour'
  elif which == 'airline':
    key = DisplayAirline
    sort = 'value'
    title = 'Airline'
  elif which == 'aircraft':
    key = lambda k: k.get('aircraft_type_code', KEY_NOT_PRESENT_STRING)
    sort = 'value'




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




    key = lambda k: DivideAndFormat(k.get('min_feet', KEY_NOT_PRESENT_STRING), 100)
    sort = ['%2d'%x for x in range(0, round((MIN_METERS*FEET_IN_METER)/100)+1)]
    title = 'Min Dist (100ft)'
  elif which == 'day_of_week':
    key = lambda k: DisplayTime(k, '%a')
    sort = DAYS_OF_WEEK
    title = 'Day of Week'
    # if less than one week, as requested; if more than one week, in full week multiples
    hours_in_week = 7 * HOURS_IN_DAY
    weeks = hours / hours_in_week
    if weeks > 1:
      hours = hours_in_week * int(hours / hours_in_week)
  elif which == 'day_of_month':
    key = lambda k: DisplayTime(k, '%-d').rjust(2)
    today_day = datetime.datetime.now(TZ).day
    days = list(range(today_day, 0, -1))  # today down to the first of the month
    days.extend(range(31, today_day, -1))  # 31st of the month down to day after today
    days = [str(d).rjust(2) for d in days]
    sort = days
    title = 'Day of Month'






























  else:
    Log(
        'Histogram form has invalid value for which_histograms: %s' % which)
    return HistogramSettingsKeySortTitle(
        'destination', hours, max_altitude=max_altitude)

  return (key, sort, title, hours)


def ImageHistograms(
    flights,
    which_histograms,
    how_much_history,
    filename_prefix=HISTOGRAM_IMAGE_PREFIX,
    filename_suffix=HISTOGRAM_IMAGE_SUFFIX):

  """Generates multiple split histogram images.

  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.
    which_histograms: string paramater indicating which histogram(s) to generate, which
      can be either the special string 'all', or a string linked to a specific
      histogram.
    how_much_history: string parameter taking a value among ['today', '24h', '7d', '30d].
    filename_prefix: this string indicates the file path and name prefix for the images
      that are created. File names are created in the form [prefix]name.[suffix], i.e.:
      if the prefix is histogram_ and the suffix is png, then the file name might be
      histogram_aircraft.png.
    filename_suffix: see above; also interpreted by savefig to generate the correct
      format.




  Returns:
    List of the names of the histograms generated.
  """
  hours = HistogramSettingsHours(how_much_history)

  histograms_to_generate = []
  if which_histograms in ['destination', 'all']:
    histograms_to_generate.append({'generate': 'destination'})
  if which_histograms in ['origin', 'all']:
    histograms_to_generate.append({'generate': 'origin'})
  if which_histograms in ['hour', 'all']:
    histograms_to_generate.append({'generate': 'hour'})
  if which_histograms in ['airline', 'all']:
    histograms_to_generate.append({'generate': 'airline', 'truncate': int(TRUNCATE/2)})
  if which_histograms in ['aircraft', 'all']:
    histograms_to_generate.append({'generate': 'aircraft'})
  if which_histograms in ['altitude', 'all']:
    histograms_to_generate.append({'generate': 'altitude', 'exhaustive': True})
  if which_histograms in ['bearing', 'all']:
    histograms_to_generate.append({'generate': 'bearing'})
  if which_histograms in ['distance', 'all']:
    histograms_to_generate.append({'generate': 'distance', 'exhaustive': True})
  if which_histograms in ['day_of_week', 'all']:
    histograms_to_generate.append({'generate': 'day_of_week'})
  if which_histograms in ['day_of_month', 'all']:
    histograms_to_generate.append({'generate': 'day_of_month'})








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


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

    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
    matplotlib.pyplot.savefig(filename)
    matplotlib.pyplot.close()

  histograms_generated = [h['generate'] for h in histograms_to_generate]
  return histograms_generated


def MessageboardHistograms(
    flights,
    which_histograms,




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




        'suppress_percent_sign': True,
        'columns': 3})
  if which_histograms in ['distance', 'all']:
    histograms_to_generate.append({
        'generate': 'distance',
        'columns': 3})
  if ((which_histograms == 'all' and how_much_history == '7d')
      or which_histograms == 'day_of_week'):
    histograms_to_generate.append({
        'generate': 'day_of_week',
        'columns': 3,
        'absolute': True})
  if ((which_histograms == 'all' and how_much_history == '30d')
      or which_histograms == 'day_of_month'):
    histograms_to_generate.append({
        'generate': 'day_of_month',
        'columns': 3,
        'suppress_percent_sign': True,
        'column_divider': '|',
        'absolute': True})













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


    # 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.
    Heartbeat()
    histogram = MessageboardHistogram(
        flights,
        key,
        sort,
        title,
        screen_limit=screen_limit,
        columns=histogram.get('columns', 2),
        suppress_percent_sign=histogram.get('suppress_percent_sign', False),
        column_divider=histogram.get('column_divider', ' '),
        data_summary=data_summary,
        hours=hours,
        absolute=histogram.get('absolute', False))
    messages.extend(histogram)

  messages = [(FLAG_MSG_HISTOGRAM, m) for m in messages]




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




  created so that broken image links are not displayed in the webpage.

  Args:
    flights: List of flight attribute dictionaries.
    histogram_settings: Dictionary of histogram parameters.

  Returns:
    List of histogram messages, if text-based histograms are selected; empty list
    otherwise.
  """
  histogram_messages = []

  if histogram_settings['type'] in ('messageboard', 'both'):
    histogram_messages = MessageboardHistograms(
        flights,
        histogram_settings['histogram'],
        histogram_settings['histogram_history'],
        histogram_settings['histogram_max_screens'],
        histogram_settings.get('histogram_data_summary', False))
  if histogram_settings['type'] in ('images', 'both'):
    histograms_generated = ImageHistograms(
        flights,
        histogram_settings['histogram'],
        histogram_settings['histogram_history'])
    all_available_histograms = [
        'destination', 'origin', 'hour', 'airline', 'aircraft', 'altitude',
        'bearing', 'distance', 'day_of_week', 'day_of_month']
    for histogram in all_available_histograms:
      if histogram not in histograms_generated:
        missing_filename = (
            HISTOGRAM_IMAGE_PREFIX + histogram + '.' + HISTOGRAM_IMAGE_SUFFIX)
        shutil.copyfile(HISTOGRAM_EMPTY_IMAGE_FILE, missing_filename)

  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,
  up to the max determined by the max altitude and max distance amongst all the flights.




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





01234567890123456789012345678901234567890123456789012345678901234567890123456789









178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845








415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204








4219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291








430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455








45134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571








472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748        47494750475147524753475447554756475747584759476047614762476347644765476647674768











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





  Returns:
    String identifying either the airline, or Unknown if not available.
  """
  airline = flight.get('airline_short_name', flight.get('airline_full_name'))

  # 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 not in AIRCRAFT_LENGTH:
    return default
  return AIRCRAFT_LENGTH[aircraft]


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."""
  return flight.get('vert_rate', KEY_NOT_PRESENT_STRING)


def DisplayAircraft(flight):
  """Provides a display-ready string about the aircraft used.

  Args:
    flight: dictionary with key-value attributes about the flight.

  Returns:
    Aircraft string if available; empty string otherwise.
  """
  aircraft = flight.get('aircraft_type_friendly')
  if aircraft:
    aircraft = aircraft.replace('(twin-jet)', '(twin)')
    aircraft = aircraft.replace('(quad-jet)', '(quad)')
    aircraft = aircraft.replace('Regional Jet ', '')
    aircraft = aircraft[:SPLITFLAP_CHARS_PER_LINE]
  else:
    aircraft = ''
  return aircraft




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




      keyfunction,
      sort_type,
      truncate=truncate,
      hours=hours,
      max_distance_feet=max_distance_feet,
      max_altitude_feet=max_altitude_feet,
      normalize_factor=normalize_factor,
      exhaustive=exhaustive)
  if position:
    matplotlib.pyplot.subplot(*position)
  matplotlib.pyplot.figure(figsize=figsize_inches)
  values_coordinates = numpy.arange(len(keys))
  matplotlib.pyplot.bar(values_coordinates, values)

  # The filtering may have removed any flight data, or there may be none to start
  if not filtered_data:
    return

  earliest_flight_time = int(filtered_data[0]['now'])
  last_flight_time = int(filtered_data[-1]['now'])
  date_range_string = ' %d flights over last %s hours' % (
      sum(values), SecondsToDdHh(last_flight_time - earliest_flight_time))

  timestamp_string = 'Last updated %s' % EpochDisplayTime(
      time.time(), format_string='%b %-d %H:%M')

  full_title = '\n'.join([title, date_range_string, timestamp_string])

  matplotlib.pyplot.title(full_title)

  matplotlib.pyplot.subplots_adjust(bottom=0.15, left=0.09, right=0.99, top=0.89)

  matplotlib.pyplot.xticks(
      values_coordinates, keys, rotation='vertical', wrap=True,
      horizontalalignment='right',
      verticalalignment='center')


def HistogramSettingsHours(how_much_history):
  """Extracts the desired history (in hours) from the histogram configuration string.

  Args:
    how_much_history: string from the histogram config file.

  Returns:
    Number of hours of history to include in the histogram.
  """
  if how_much_history == 'today':
    hours = HoursSinceMidnight()
  elif how_much_history == '24h':
    hours = HOURS_IN_DAY




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




  Args:
    max_screens: string from the histogram config file.

  Returns:
    Number of maximum number of screens to display for a splitflap histogram.
  """
  if max_screens == '_1':
    screen_limit = 1
  elif max_screens == '_2':
    screen_limit = 2
  elif max_screens == '_5':
    screen_limit = 5
  elif max_screens == 'all':
    screen_limit = 0  # no limit on screens
  else:
    Log('Histogram form has invalid value for max_screens: %s' % max_screens)
    screen_limit = 1
  return screen_limit


def HistogramSettingsKeySortTitle(which, hours, flights, max_altitude=45000):
  """Provides the arguments necessary to generate a histogram from the config string.

  The same parameters are used to generate either a splitflap text or web-rendered
  histogram in terms of the histogram title, the keyfunction, and how to sort the keys.
  For a given histogram name (based on the names defined in the histogram config file),
  this provides those parameters.

  Args:
    which: string from the histogram config file indicating the histogram to provide
      settings for.
    hours: how many hours of histogram data have been requested.
    flights: list of the flights in the data set.
    max_altitude: indicates the maximum altitude that should be included on the
      altitude labels.

  Returns:
    A 4-tuple of the parameters used by either CreateSingleHistogramChart or
    MessageboardHistogram, of the keyfunction, sort, title, and hours.
  """
  def DivideAndFormat(dividend, divisor):
    if dividend is None:
      return KEY_NOT_PRESENT_STRING
    if isinstance(dividend, numbers.Number):
      return '%2d' % round(dividend / divisor)
    return dividend[:2]

  def RoundAndFormat(dividend, divisor, digits):
    if dividend is None:
      return KEY_NOT_PRESENT_STRING
    if isinstance(dividend, numbers.Number):
      return divisor*round(dividend / divisor)
    return dividend[:digits]

  if which == 'destination':
    key = lambda k: k.get('destination_iata', KEY_NOT_PRESENT_STRING)
    sort = 'value'
    title = 'Destination'
  elif which == 'origin':
    key = lambda k: k.get('origin_iata', KEY_NOT_PRESENT_STRING)
    sort = 'value'
    title = 'Origin'
  elif which == 'hour':
    key = lambda k: DisplayTime(k, '%H')
    sort = 'key'
    title = 'Hour'
  elif which == 'airline':
    key = DisplayAirline
    sort = 'value'
    title = 'Airline'
  elif which == 'aircraft':
    key = lambda k: k.get('aircraft_type_code', KEY_NOT_PRESENT_STRING)
    sort = 'value'




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




    key = lambda k: DivideAndFormat(k.get('min_feet', KEY_NOT_PRESENT_STRING), 100)
    sort = ['%2d'%x for x in range(0, round((MIN_METERS*FEET_IN_METER)/100)+1)]
    title = 'Min Dist (100ft)'
  elif which == 'day_of_week':
    key = lambda k: DisplayTime(k, '%a')
    sort = DAYS_OF_WEEK
    title = 'Day of Week'
    # if less than one week, as requested; if more than one week, in full week multiples
    hours_in_week = 7 * HOURS_IN_DAY
    weeks = hours / hours_in_week
    if weeks > 1:
      hours = hours_in_week * int(hours / hours_in_week)
  elif which == 'day_of_month':
    key = lambda k: DisplayTime(k, '%-d').rjust(2)
    today_day = datetime.datetime.now(TZ).day
    days = list(range(today_day, 0, -1))  # today down to the first of the month
    days.extend(range(31, today_day, -1))  # 31st of the month down to day after today
    days = [str(d).rjust(2) for d in days]
    sort = days
    title = 'Day of Month'

  elif which == 'speed':
    rounding = 25
    field = 'speed'
    min_value = min([f[field] for f in flights if f.get(field)])
    max_value = max([f[field] for f in flights if f.get(field)])
    digits = int(math.log10(max_value)) + 1
    key = lambda k: RoundAndFormat(k.get(field, KEY_NOT_PRESENT_STRING), rounding, digits)
    values = range(int(min_value), int(max_value) + 1)
    sort = sorted(list({RoundAndFormat(v, rounding, digits) for v in values}))
    title = 'Speed (kn)'

  elif which == 'aircraft_length':
    key = DisplayLength
    min_value = min([AircraftLength(f, default=float('inf')) for f in flights])
    max_value = max([AircraftLength(f, default=float('-inf')) for f in flights])
    sort = list(range(round(min_value), round(max_value) + 1))
    title = 'Plane Length (m)'

  elif which == 'vert_rate':
    rounding = 200
    field = 'vert_rate'
    min_value = min([f[field] for f in flights if f.get(field)])
    max_value = max([f[field] for f in flights if f.get(field)])
    digits = max(int(math.log10(max_value)), 1 + int(math.log10(abs(min_value)))) + 1
    key = lambda k: RoundAndFormat(k.get(field, KEY_NOT_PRESENT_STRING), rounding, digits)
    values = range(int(min_value), int(max_value) + 1)
    sort = sorted(list({RoundAndFormat(v, rounding, digits) for v in values}))
    title = 'Ascent Rate (%s)' % CLIMB_RATE_UNITS

  else:
    Log(
        'Histogram form has invalid value for which_histograms: %s' % which)
    return HistogramSettingsKeySortTitle(
        'destination', hours, flights, max_altitude=max_altitude)

  return (key, sort, title, hours)


def ImageHistograms(
    flights,
    which_histograms,
    how_much_history,
    filename_prefix=HISTOGRAM_IMAGE_PREFIX,
    filename_suffix=HISTOGRAM_IMAGE_SUFFIX,
    heartbeat=True):
  """Generates multiple split histogram images.

  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.
    which_histograms: string paramater indicating which histogram(s) to generate, which
      can be either the special string 'all', or a string linked to a specific
      histogram.
    how_much_history: string parameter taking a value among ['today', '24h', '7d', '30d].
    filename_prefix: this string indicates the file path and name prefix for the images
      that are created. File names are created in the form [prefix]name.[suffix], i.e.:
      if the prefix is histogram_ and the suffix is png, then the file name might be
      histogram_aircraft.png.
    filename_suffix: see above; also interpreted by savefig to generate the correct
      format.
    heartbeat: boolean indicating whether we should log heartbeats between each histogram
      to make sure monitoring does not mistake this slow procedure for being hung; this
      should be set to false if this is called from outside of messageboard.main.

  Returns:
    List of the names of the histograms generated.
  """
  hours = HistogramSettingsHours(how_much_history)

  histograms_to_generate = []
  if which_histograms in ['destination', 'all']:
    histograms_to_generate.append({'generate': 'destination'})
  if which_histograms in ['origin', 'all']:
    histograms_to_generate.append({'generate': 'origin'})
  if which_histograms in ['hour', 'all']:
    histograms_to_generate.append({'generate': 'hour'})
  if which_histograms in ['airline', 'all']:
    histograms_to_generate.append({'generate': 'airline', 'truncate': int(TRUNCATE/2)})
  if which_histograms in ['aircraft', 'all']:
    histograms_to_generate.append({'generate': 'aircraft'})
  if which_histograms in ['altitude', 'all']:
    histograms_to_generate.append({'generate': 'altitude', 'exhaustive': True})
  if which_histograms in ['bearing', 'all']:
    histograms_to_generate.append({'generate': 'bearing'})
  if which_histograms in ['distance', 'all']:
    histograms_to_generate.append({'generate': 'distance', 'exhaustive': True})
  if which_histograms in ['day_of_week', 'all']:
    histograms_to_generate.append({'generate': 'day_of_week'})
  if which_histograms in ['day_of_month', 'all']:
    histograms_to_generate.append({'generate': 'day_of_month'})

  if which_histograms in ['speed', 'all']:
    histograms_to_generate.append({'generate': 'speed', 'exhaustive': True})
  if which_histograms in ['aircraft_length', 'all']:
    histograms_to_generate.append({'generate': 'aircraft_length', 'exhaustive': True})
  if which_histograms in ['vert_rate', 'all']:
    histograms_to_generate.append({'generate': 'vert_rate', 'exhaustive': True})

  for histogram in histograms_to_generate:
    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
    matplotlib.pyplot.savefig(filename)
    matplotlib.pyplot.close()

  histograms_generated = [h['generate'] for h in histograms_to_generate]
  return histograms_generated


def MessageboardHistograms(
    flights,
    which_histograms,




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




        'suppress_percent_sign': True,
        'columns': 3})
  if which_histograms in ['distance', 'all']:
    histograms_to_generate.append({
        'generate': 'distance',
        'columns': 3})
  if ((which_histograms == 'all' and how_much_history == '7d')
      or which_histograms == 'day_of_week'):
    histograms_to_generate.append({
        'generate': 'day_of_week',
        'columns': 3,
        'absolute': True})
  if ((which_histograms == 'all' and how_much_history == '30d')
      or which_histograms == 'day_of_month'):
    histograms_to_generate.append({
        'generate': 'day_of_month',
        'columns': 3,
        'suppress_percent_sign': True,
        'column_divider': '|',
        'absolute': True})
  if which_histograms in ['speed', 'all']:
    histograms_to_generate.append({
        'generate': 'speed',
        'columns': 2})
  if which_histograms in ['aircraft_length', 'all']:
    histograms_to_generate.append({
        'generate': 'aircraft_length',
        'columns': 3})
  if which_histograms in ['vert_rate', 'all']:
    histograms_to_generate.append({
        'generate': 'vert_rate',
        'columns': 2})

  for histogram in histograms_to_generate:
    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.
    Heartbeat()
    histogram = MessageboardHistogram(
        flights,
        key,
        sort,
        title,
        screen_limit=screen_limit,
        columns=histogram.get('columns', 2),
        suppress_percent_sign=histogram.get('suppress_percent_sign', False),
        column_divider=histogram.get('column_divider', ' '),
        data_summary=data_summary,
        hours=hours,
        absolute=histogram.get('absolute', False))
    messages.extend(histogram)

  messages = [(FLAG_MSG_HISTOGRAM, m) for m in messages]




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




  created so that broken image links are not displayed in the webpage.

  Args:
    flights: List of flight attribute dictionaries.
    histogram_settings: Dictionary of histogram parameters.

  Returns:
    List of histogram messages, if text-based histograms are selected; empty list
    otherwise.
  """
  histogram_messages = []

  if histogram_settings['type'] in ('messageboard', 'both'):
    histogram_messages = MessageboardHistograms(
        flights,
        histogram_settings['histogram'],
        histogram_settings['histogram_history'],
        histogram_settings['histogram_max_screens'],
        histogram_settings.get('histogram_data_summary', False))
  if histogram_settings['type'] in ('images', 'both'):
    ImageHistograms(
        flights,
        histogram_settings['histogram'],
        histogram_settings['histogram_history'])









  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,
  up to the max determined by the max altitude and max distance amongst all the flights.




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