#!/awips2/python/bin/python # Update plotResourceData lowerLimit from -9999.0 to -9998.0 # This script should be run as user AWIPS from dx3 with no command line arguments to update # any D2D procedures stored in localization. # # The script may also be run by the field to update saved displays/perspectives outside localization # # When run by the field 1 or more files or directories containing save displays/perspectives # should be provided as command line arguments import glob import getpass import socket import logging import shutil import sys import re import os import xml.dom.minidom as minidom import xml.etree.ElementTree as ET from xml.etree.ElementTree import ParseError from xml.parsers.expat import ErrorString from xml.parsers.expat import errors SITE_PATHS = ["/awips2/edex/data/utility/cave_static/*/*/procedures", "/awips2/edex/data/utility/common_static/*/*/perspectives", ] LOWER_LIMIT_KEY = "lowerLimit" LOWER_LIMIT = -9998.0 logging.basicConfig(format='%(asctime)-15s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S', level=logging.DEBUG) log = logging.getLogger("updateLowerLimit.py") class CommentedTreeBuilder(ET.TreeBuilder): def __init__(self, *args, **kwargs): super(CommentedTreeBuilder, self).__init__(*args, **kwargs) def comment(self, data): self.start(ET.Comment, {}) self.data(data) self.end(ET.Comment) def processDir(directory): log.info("Processing directory: %s", directory) for root, directories, files in os.walk(directory, topdown=False): for filePath in files: if str(filePath).endswith(".xml"): fullPath = os.path.join(root, filePath) updateFile(fullPath) for directory in directories: fullPath = os.path.join(root, directory) processDir(fullPath) def updateFile(path): changed = False try: parser = ET.XMLParser(target=CommentedTreeBuilder()) tree = ET.parse(path, parser) root = tree.getroot() except ParseError as e: if ErrorString(e.code) == errors.XML_ERROR_JUNK_AFTER_DOC_ELEMENT: log.info("Skipping xml fragment: %s", path) elif ErrorString(e.code) == errors.XML_ERROR_NO_ELEMENTS: log.info("Skipping empty xml document: %s", path) else: log.error("Error parsing file: %s \"%s\"", path, ErrorString(e.code)) return 1 except Exception: log.exception("Unable to parse XML file: %s", path) return 1 for resourceData in root.iter("resourceData"): # ensure we have a plotResourceData isPlotResourceData = False for key, value in resourceData.attrib.items(): if key.endswith("}type") and value == "plotResourceData": isPlotResourceData = True break # if we have plotResourceData if isPlotResourceData: # if lowerLimt less than LOWER_LIMIT replace it with LOWER_LIMIT if LOWER_LIMIT_KEY in resourceData.attrib: value = resourceData.attrib[LOWER_LIMIT_KEY] if float(value) < LOWER_LIMIT: resourceData.attrib[LOWER_LIMIT_KEY] = str(LOWER_LIMIT) changed = True if changed: log.info("Updating path: %s", path) shutil.copy(path, path+".bak") pretty_xml = minidom.parseString(ET.tostring(root, 'utf-8')).toprettyxml(indent=' '*4, encoding='UTF-8') pretty_xml = '\n'.join([line for line in pretty_xml.split('\n') if line.strip()]) text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+\g<1> 1: searchPaths = sys.argv[1:] else: # No paths on command line # verify we are running on dx3 as user awips userName = getpass.getuser() hostName = socket.gethostname() log.info("Running %s as user: %s on host: %s", sys.argv[0], userName, hostName) if userName != "awips" or not hostName.startswith("dx3"): log.error("To update files under the localization tree you must run as user awips on dx3") return -1 for sp in searchPaths: paths = glob.glob(sp) for path in paths: if os.path.isdir(path): processDir(path) elif os.path.isfile(path): updateFile(path) return status def main(): log.info("RODO DR #6894 update plotResourceData lowerLimit to -9998.0") status = processArgs() if status == 0: log.info("RODO DR #6894: Success.") else: log.warning("RODO DR #6894: Errors encountered.") return status if __name__ == '__main__': sys.exit(main())