awips2/deltaScripts/18.1.1/DR6346/createIncrementalAreaDictionaryAndCityLocation.py
2022-05-05 12:34:50 -05:00

290 lines
10 KiB
Python
Executable file

#!/awips2/python/bin/python2
##
# DR 6346 - This script will compare the site overrides for AreaDictionary.py
# and CityLocation.py to the CONFIGURED level versions and create incremental
# overrides of these files.
##
import copy
import logging
import glob
import imp
import os
import os.path
import pprint
import shutil
logging.basicConfig(format='%(asctime)-15s %(levelname)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
level=logging.DEBUG)
log = logging.getLogger("createIncrementalAreaDictionaryAndCityLocation.py")
TEXT_UTILS_BASE_PATH = "/awips2/edex/data/utility/cave_static/site/*/gfe/userPython/textUtilities/regular/"
AREA_DICT_GLOB_PATH = os.path.join(TEXT_UTILS_BASE_PATH, "AreaDictionary.py")
CITY_LOC_GLOB_PATH = os.path.join(TEXT_UTILS_BASE_PATH, "CityLocation.py")
AREA_DICT_HEADER = """
# ----------------------------------------------------------------------------
# This software is in the public domain, furnished "as is", without technical
# support, and with no warranty, express or implied, as to its usefulness for
# any purpose.
#
# AreaDictionary
# AreaDictionary file
#
# Author: GFE Installation Script
# ----------------------------------------------------------------------------
##
# This is an absolute override file, indicating that a higher priority version
# of the file will completely replace a lower priority version of the file.
##
# Format:
# AreaDictionary = {
# "editArea" : {
# "ugcCode": "STZxxx",
# "ugcName": "EditAreaName",
# "ugcCities": ['City1', 'City2'],
# "ugcTimeZone": "MST7MDT",
# "fullStateName": "COLORADO",
# "partOfState": "NORTHEAST",
# "stateAbbr": "CO",
# "independentCity": 0,
# "locationName": "GeneralAreaName",
# }
# ...
# }
# ugcTimeZone: This field should be replace with the correct time zone
# for that zone. If it is different from the time zone of
# the local WFO, it's time zone will appear in the header of
# some products in parentheses.
# Using any other strings to define
# the time zone may produce undesirable results.
# The time zone may also be a list of time zones in case
# a forecast zone happens to cover an area that contains
# two time zones.
# e.g. "ugcTimeZone" : ["MST7MDT", "PST8PDT"]
#
# ugcCode: This field contains the ugc coding for this area, such as COZ023
#
# ugcName: This field contains the descriptive name for this area. It
# is used in various products, including Hazard products. This is
# the official county or zone name.
#
# locationName: This field is optional, but provides an alternate name that
# is used in the text of some products to describe the area. The
# FFA product uses this value if available.
#
# ugcCities: This field contains the list of cities for hazard and routine
# products.
#
# fullStateName: This field is used in hazard products to fully describe
# the state in which this edit area resides.
#
# partOfState: This field describes the location within a state (such as
# NORTHEAST) for this area. It is used in hazard products.
#
# stateAbbr: State Abbreviation for the fullStateName.
#
# independentCity: Set to 0 or 1. Some counties (FIPS coding) are actually
# cities. Setting the flag to 1 will instruct those formatters
# to mention independent cities, rather than include this "county"
# in the county list of the product.
#
# wfo: The wfo(s) with responsibility for the area
#
#
from DefaultAreaDictionary import AreaDictionary
"""
CITY_LOCATION_HEADER = """
# ----------------------------------------------------------------------------
# This software is in the public domain, furnished "as is", without technical
# support, and with no warranty, express or implied, as to its usefulness for
# any purpose.
#
# CityLocation
# CityLocation file
#
# Author: GFE Installation Script
# ----------------------------------------------------------------------------
##
# This is an absolute override file, indicating that a higher priority version
# of the file will completely replace a lower priority version of the file.
##
# Format:
# CityLocation = {
# "editArea": {'cityName1' : (latitude, longitude),
# 'cityName2' : (latitude, longitude),
# ...
# }
# ...
# }
#
# editArea: name of edit area as in AreaDictionary
#
# cityName: name of the city - should be the same as in AreaDictionary.
#
# latitude/longitude: city's lat/lon location.
#
from DefaultCityLocation import CityLocation
"""
def create_incremental_area_dictionary():
for site_file in glob.iglob(AREA_DICT_GLOB_PATH):
log.info("Generating incremental override file [%s]...", site_file)
base_file = site_file.replace("site", "configured", 1)
if not os.path.isfile(base_file):
log.error("Could not find CONFIGURED level file [%s].", base_file)
log.error("Skipping to next file.")
continue
with open(site_file, 'r') as f:
contents = f.read()
if "from DefaultAreaDictionary import AreaDictionary" in contents:
log.info("Site AreaDictionary file [%s] has already been upgraded.", site_file)
continue
log.info("Using configured file [%s]...", base_file)
log.info("Using site file [%s]...", site_file)
configured_module = imp.load_source('base', base_file)
site_module = imp.load_source('override', site_file)
configured_dict = configured_module.AreaDictionary
site_dict = site_module.AreaDictionary
diffs = diff_dicts(configured_dict, site_dict)
log.debug("AreaDictionary Differences: %r", diffs)
write_override_file(site_file, 'AreaDictionary', diffs, AREA_DICT_HEADER)
delete_files(base_file + '*')
def create_incremental_city_location():
for site_file in glob.iglob(CITY_LOC_GLOB_PATH):
log.info("Generating incremental override file [%s]...", site_file)
base_file = site_file.replace("site", "configured", 1)
if not os.path.isfile(base_file):
log.error("Could not find CONFIGURED level file [%s].", base_file)
log.error("Skipping to next file.")
continue
with open(site_file, 'r') as f:
contents = f.read()
if "from DefaultCityLocation import CityLocation" in contents:
log.info("Site CityLocation file [%s] has already been upgraded.", site_file)
continue
log.info("Using configured file [%s]...", base_file)
log.info("Using site file [%s]...", site_file)
configured_module = imp.load_source('base', base_file)
site_module = imp.load_source('override', site_file)
configured_dict = configured_module.CityLocation
site_dict = site_module.CityLocation
diffs = diff_dicts(configured_dict, site_dict)
log.debug("CityLocation Differences: %r", diffs)
write_override_file(site_file, 'CityLocation', diffs, CITY_LOCATION_HEADER)
delete_files(base_file + '*')
def diff_dicts(base, override):
differences = []
keys = set().union(base.keys(), override.keys())
# log.debug("Combined keys: %s", keys)
for key in sorted(keys):
if key not in base:
log.debug("Key [%s] in override, but not base.", key)
differences.append((key, copy.copy(override[key]), True))
elif key not in override:
log.debug("Key [%s] in base, but not override.", key)
else:
sub_diffs = sub_diff_dicts(base[key], override[key])
if sub_diffs:
log.debug("Differences for key [%s]: %r", key, sub_diffs)
differences.append((key, sub_diffs, False))
return differences
def sub_diff_dicts(base, override, level=0):
differences = {}
keys = set().union(base.keys(), override.keys())
# log.debug("Combined keys: %s", keys)
for key in sorted(keys):
if key not in base:
log.debug("Key [%s] in override, but not base.", key)
differences[key] = copy.copy(override[key])
elif key not in override:
log.debug("Key [%s] in base, but not override.", key)
else:
if base[key] != override[key]:
differences[key] = override[key]
return differences
def write_override_file(file_name, object_name, object_value, header):
backup_file = file_name + ".bak.dr_6346"
log.info("Writing backup file [%s]", backup_file)
try:
shutil.copy(file_name, backup_file)
except:
log.exception("Unable to write backup file [%s]", backup_file)
log.error("Skipping file [%s]", file_name)
return
log.info("Writing override file [%s]", file_name)
try:
with open(file_name, 'w') as out_file:
printer = pprint.PrettyPrinter()
out_file.write(header)
for (key, value, added) in sorted(object_value, key=lambda i: i[0]):
if added:
out_file.write("{}[{!r}] = {}".format(object_name, key, printer.pformat(value)))
out_file.write('\n')
else:
for sub_key in sorted(value.keys()):
out_file.write("{}[{!r}][{!r}] = {}".format(object_name, key, sub_key, printer.pformat(value[sub_key])))
out_file.write('\n')
out_file.write('\n')
except:
log.exception("Unable to write incremental override file [%s]", file_name)
log.critical("Restore backup file [%s] to [%s] before restarting EDEX.", backup_file, file_name)
def delete_files(file_pattern):
for f in glob.iglob(file_pattern):
try:
os.remove(f)
except:
log.exception("Unable to delete file [%s].", f)
def main():
log.info("Starting delta script for DR #6346: creating incremental overrides for AreaDictionary.py and CityLocation.py...")
create_incremental_area_dictionary()
create_incremental_city_location()
log.info("Delta script complete.")
if __name__ == '__main__':
main()