01234567890123456789012345678901234567890123456789012345678901234567890123456789
102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 243244245246247248249250251252253254255256257258259260261262263264265266 267268269270271272273274275276277278279280281282283284285286 437143724373437443754376437743784379438043814382438343844385438643874388438943904391 43924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423 442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447 4448444944504451445244534454445544564457445844594460446144624463446444654466446744684469 472547264727472847294730473147324733473447354736473747384739474047414742474347444745 474647474748 47494750475147524753475447554756475747584759476047614762476347644765476647674768 | <----SKIPPED LINES----> # 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' # For those of the approximately ten different types of histograms _not_ generated, # an empty image is copied into the location expected by the webpage instead; this is # the location of that "empty" image file. HISTOGRAM_EMPTY_IMAGE_FILE = 'empty.png' # 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') # A few key settings for the messageboard are its sensitivity to displaying flights - # though it logs all flights within range, it may not be desirable to display all # flights to the user. Two key parameters are the maximum altitude, and the furthest # away we anticipate the flight being at its closest point to HOME. As those two # parameters are manipulated in the settings, a histogram is displayed with one or # potentially two series, showing the present and potentially prior-set distribution # of flights, 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. <----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_PREFIX = WEBSERVER_PATH + WEBSERVER_IMAGE_FOLDER + HISTOGRAM_IMAGE_PREFIX HISTOGRAM_EMPTY_IMAGE_FILE = ( WEBSERVER_PATH + WEBSERVER_IMAGE_FOLDER + HISTOGRAM_EMPTY_IMAGE_FILE) HOURLY_IMAGE_FILE = WEBSERVER_PATH + 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'] SECONDS_IN_MINUTE = 60 MINUTES_IN_HOUR = 60 <----SKIPPED LINES----> 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, 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. 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]. <----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----> |
01234567890123456789012345678901234567890123456789012345678901234567890123456789
102103104105106107108109110111112113114115116117118119120121122 123124125126127128129130131132133134135136137138139140141142 240241242243244245246247248249250251252253254255256257258259260 261262263264265266267268269270271272273274275276277278279280281282283284 4369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450 44514452445344544455445644574458445944604461446244634464446544664467446844694470 47264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802 | <----SKIPPED LINES----> # 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') # A few key settings for the messageboard are its sensitivity to displaying flights - # though it logs all flights within range, it may not be desirable to display all # flights to the user. Two key parameters are the maximum altitude, and the furthest # away we anticipate the flight being at its closest point to HOME. As those two # parameters are manipulated in the settings, a histogram is displayed with one or # potentially two series, showing the present and potentially prior-set distribution # of flights, 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. <----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_PREFIX = WEBSERVER_PATH + WEBSERVER_IMAGE_FOLDER + HISTOGRAM_IMAGE_PREFIX HISTOGRAM_IMAGE_HTML = WEBSERVER_PATH + HISTOGRAM_IMAGE_HTML HOURLY_IMAGE_FILE = WEBSERVER_PATH + 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'] SECONDS_IN_MINUTE = 60 MINUTES_IN_HOUR = 60 <----SKIPPED LINES----> 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 2-tuples of histograms generated, where the first element is the histogram identifier (i.e.: destination), and the second element is the filename (i.e.: histogram_destination.png). """ 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}) histograms_generated = [] 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.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. 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]. <----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'): # Since Google Chrome seems to ignore all instructions to not cache, we need # to make sure we do not reuse file names - hence the epoch_string - and then # we need to 1) update the histograms.html file with the correct file links, and # 2) delete the images that are now obsolete. epoch_string = '%d_' % round(time.time()) 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> n, line = None, None # addresses pylint complaint for n, line in enumerate(html_lines): if identifier in line: break 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: if os.path.exists(f): try: os.remove(f) except PermissionError: pass 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----> |