From 171a8021907f58f6e6a64200a8a578791efbd309 Mon Sep 17 00:00:00 2001 From: Sarah Pontius Date: Tue, 2 Dec 2014 17:18:06 -0700 Subject: [PATCH] VLab Issue #5492 - Dec 2nd HLS/TCV Code Check-in for 14.3.2 SwIT; fixes #5492 Change-Id: I2d0b6fbe44cb2931fc0cd74a9361cf886b0c9565 Former-commit-id: 24c01f47449607347812e0e676f1d14849d3b47e --- .../userPython/textProducts/HLSTCV_Common.py | 227 ++-- .../gfe/textproducts/AreaDictionaryMaker.java | 9 +- .../base/gfe/createAreaDictionary.py | 191 +++ .../textproducts/templates/product/HLS.py | 474 +++++-- .../templates/product/Hazard_TCV.py | 1187 ++++++++++------- 5 files changed, 1329 insertions(+), 759 deletions(-) diff --git a/cave/com.raytheon.viz.gfe/localization/gfe/userPython/textProducts/HLSTCV_Common.py b/cave/com.raytheon.viz.gfe/localization/gfe/userPython/textProducts/HLSTCV_Common.py index 685ecaaab4..495b5f317e 100644 --- a/cave/com.raytheon.viz.gfe/localization/gfe/userPython/textProducts/HLSTCV_Common.py +++ b/cave/com.raytheon.viz.gfe/localization/gfe/userPython/textProducts/HLSTCV_Common.py @@ -1,10 +1,10 @@ +# Version 2014.11.21-0 import GenericHazards import JsonSupport import string, time, os, errno, re, types, copy, collections import LogStream, ModuleAccessor, SampleAnalysis, EditAreaUtils import math - try: # See if this is the AWIPS I environment import AFPS from AFPS import AbsTime @@ -156,15 +156,15 @@ class TextProduct(GenericHazards.TextProduct): for area in hazard['id']: hazDict.setdefault((hdln, phen, sig), []).append(area) - #print "hazDict", hazDict + #self.debug_print("hazDict", hazDict hazardHdlns=[] huAreas = [] -# print "\nAdditional Hazard Headlines" +# self.debug_print("\nAdditional Hazard Headlines" for key in hazDict.keys(): hdln, phen, sig = key huAreas = huAreas + hazDict[key] hazardHdln = ((hdln, "NEW", phen,sig), hazDict[key], [],[],[]) - #print " ", hazardHdln, hazDict[key] + #self.debug_print(" ", hazardHdln, hazDict[key] hazardHdlns.append(hazardHdln) return hazardHdlns, huAreas @@ -187,32 +187,32 @@ class TextProduct(GenericHazards.TextProduct): # Otherwise, they are ignored. # # E.g. hdlnList = self._checkHazard(hazardHdlns, [("FA","W")], returnList=True) - print "_checkHazard hazardHdlns is ", hazardHdlns - print "_checkHazard phenSigList is ", phenSigList + self.debug_print("_checkHazard hazardHdlns is %s" % (hazardHdlns), 1) + self.debug_print("_checkHazard phenSigList is %s" % (phenSigList), 1) chosen = [] for key, landList, marineList, coastalList, inlandList in hazardHdlns: -# print "what is mode?", mode +# self.debug_print("what is mode?", mode # SARAH - we do not want to consider marine hazards in this product # hazAreas = landList+marineList hazAreas = landList hazValue = (key, hazAreas) - print "hazValue is ", hazValue + self.debug_print("hazValue is %s" % (repr(hazValue)), 1) hdln, act, phen, sig = key if not includeCAN and act == "CAN": continue for checkPhen, checkSig in phenSigList: - print "checkPhen is ", checkPhen - print "checkSig is ", checkSig + self.debug_print("checkPhen is %s" % (checkPhen), 1) + self.debug_print("checkSig is %s" % (checkSig), 1) if phen == checkPhen and sig == checkSig: if checkAreaTypes is not None: # Check for land, marine, etc. for checkAreaType in checkAreaTypes: exec "testList = " + checkAreaType + "List" -# print "testList is", testList +# self.debug_print("testList is", testList if testList != []: chosen.append(hazValue) -# print "chosen is ", chosen +# self.debug_print("chosen is ", chosen elif checkAreas is not None: acceptedAreas=[] for hazArea in hazAreas: @@ -224,7 +224,7 @@ class TextProduct(GenericHazards.TextProduct): chosen.append(hazValue) if not returnList and chosen!=[]: break - print "MATT _checkHazard chosen = %s" % (repr(chosen)) + self.debug_print("MATT _checkHazard chosen = %s" % (repr(chosen)), 1) if not returnList: return chosen!=[] return chosen @@ -270,7 +270,7 @@ class TextProduct(GenericHazards.TextProduct): def _initializeTimeVariables(self, argDict): argDict['creationTime'] = int(time.time()/60)*60 self._issueTime_secs = argDict['creationTime'] - self._issueTime = self._issueTime_secs * 1000 # in milliseconds + self._issueTime_ms = self._issueTime_secs * 1000 # in milliseconds self._ddhhmmTime = self.getCurrentTime( argDict, "%d%H%M", shiftToLocal=0, stripLeading=0) @@ -285,14 +285,43 @@ class TextProduct(GenericHazards.TextProduct): # Create a time range from the issuanceHour out 120 hours startTime = self._calculateStartTime(time.gmtime(self._issueTime_secs)) self._timeRange = self.makeTimeRange(startTime, startTime+120*3600) + + # Create a time range to look from the current time back 12 hours + # We will use this are to determine if we need to use "additional" + # wording with rainfall + self._extraSampleTimeRange = self.makeTimeRange(startTime-12*3600, + startTime) # Determine the time range list, making sure they are on hour boundaries # w.r.t. midnight today according to the resolution subRanges = self.divideRange(self._timeRange, self._resolution()) trList = [] - for tr in subRanges: - # print tr + self._periodList = [] + for index, tr in enumerate(subRanges): + # self.debug_print(tr) trList.append((tr, "Label")) + + if index == 0: + startTime = tr.startTime() + localtime = time.localtime(startTime.unixTime()) + if localtime.tm_hour < 6: + periodLength = 6 - localtime.tm_hour + elif localtime.tm_hour >= 6 and localtime.tm_hour < 18: + periodLength = 18 - localtime.tm_hour + else: + periodLength = 30 - localtime.tm_hour + + if periodLength < 3: + periodStart = startTime + periodLength*3600 + period = self.makeTimeRange(periodStart, periodStart+12*3600) + else: + period = self.makeTimeRange(startTime, startTime+periodLength*3600) + + self._periodList.append(period) + for i in range(1,10): + startTime = period.endTime() # Start where the last period leaves off + period = self.makeTimeRange(startTime, startTime+12*3600) + self._periodList.append(period) self._timeRangeList = trList def _calculateStartTime(self, localCreationTime): @@ -301,15 +330,21 @@ class TextProduct(GenericHazards.TextProduct): day = localCreationTime[2] hour = localCreationTime[3] - # If we are more than halfway though a 6 hr period - if hour % 6 > 3: - adjust = 6 # move on to the next 6 hr block + # If we are more than halfway though a 3 hr period + if hour % 3 > 1: + adjust = 3 # move on to the next 3 hr block else: adjust = 0 -# print "MATT: _calculateStartTime %d adjust = %d" % (hour % 6, adjust) + +# if hour % 6 > 3: +# adjust = 6 # move on to the next 6 hr block +# else: +# adjust = 0 +# self.debug_print("MATT: _calculateStartTime %d adjust = %d" % (hour % 6, adjust) # Now "truncate" to a 6-hourly boundary and compute startTime in local Time. - hour = int( (hour/6) * 6) + adjust +# hour = int( (hour/6) * 6) + adjust + hour = int( (hour/3) * 3) + adjust if hour > 23: hour -= 24 elif hour < 0: @@ -328,17 +363,19 @@ class TextProduct(GenericHazards.TextProduct): # DAY + MORNING / AFTERNOON / EVENING / OVERNIGHT. # If wholePeriod, format FROM ... TO... - print "\nMATT Format period wholePeriod = %s, period = %s, useEndTime =%s" % (str(wholePeriod), str(period), str(useEndTime)) - if period is None: return "" + self.debug_print("MATT Format period wholePeriod = %s, period = %s, useEndTime =%s" % + (str(wholePeriod), str(period), str(useEndTime)), 1) + if period is None: + return "" if useEndTime: startTime = period.endTime() else: startTime = period.startTime() result = self._getTimeDesc(startTime, resolution, shiftToLocal) - print "MATT result = '%s'" % (result) + self.debug_print("MATT result = '%s'" % (result), 1) if wholePeriod: endResult = self._getTimeDesc(period.endTime(), resolution, shiftToLocal) - print "MATT endResult = '%s'" % (endResult) + self.debug_print("MATT endResult = '%s'" % (endResult), 1) if result != endResult: result=result + " TO "+ endResult return result @@ -347,15 +384,17 @@ class TextProduct(GenericHazards.TextProduct): # Create phrase such as Tuesday morning # Handle today/tonight and "this" morning/afternoon/etc.. # - print "\n\n**************Formatting Period for GMT starttime ", startTime + self.debug_print("\n\n**************Formatting Period for GMT startTime %s" % + (repr(startTime)), 1) labels = self.Labels()["SimpleWorded"] currentTime = self._timeRange.startTime() - print " currentTime = %s" % (repr(currentTime)) + self.debug_print(" currentTime = %s" % (repr(currentTime)), 1) if shiftToLocal: currentLocalTime, shift = self.determineTimeShift() startTime = startTime + shift currentTime = currentTime + shift - print " shift, shifted start, current", shift/3600, startTime, currentTime + self.debug_print("shift = %s shifted start = %s current = %s" % + (shift/3600, startTime, currentTime), 1) hour = startTime.hour prevDay = False prevDay, partOfDay = self._getPartOfDay(hour, resolution) @@ -372,7 +411,7 @@ class TextProduct(GenericHazards.TextProduct): result = partOfDay.replace('', weekday) else: result = weekday + " " + partOfDay - print "Result = '%s'" % (result) + self.debug_print("Result = '%s'" % (result), 1) return result def _getPartOfDay(self, hour, resolution): @@ -749,11 +788,33 @@ FORECASTER STEWART""" filenames = os.listdir(advisoryDirectoryPath) allAdvisories = filter(lambda filename: filename[-5:] == ".json", filenames) + self.debug_print("allAdvisories = %s" % (repr(allAdvisories))) + stormAdvisories = filter(lambda filename: self._getStormNameFromTCP() in filename, allAdvisories) stormAdvisories = map(lambda filename: filename[:-5], stormAdvisories) - lastTwoAdvisories = sorted(stormAdvisories)[:2] + self.debug_print("stormAdvisories = %s" % (repr(stormAdvisories))) + # We need to reverse the order of the advisories so the latest + # advisories come first in this list + stormAdvisories.reverse() + + lastTwoAdvisories = [] + + if self._awipsWANPil.find("TCV") != -1: + # Get the current storm number string from the TCP + curAdvisoryString = self._getAdvisoryNumberStringFromTCP() + + for advisory in stormAdvisories: + if advisory.find(curAdvisoryString) == -1: + # Different advisory - keep it + lastTwoAdvisories.append(advisory) + + else: # Must be the HLS + lastTwoAdvisories = stormAdvisories[:2] + + self.debug_print("MATT DEBUG: last two advisories = %s" % + (repr(lastTwoAdvisories)), 1) self._previousAdvisory = None if len(lastTwoAdvisories) >= 1: self._previousAdvisory = self._loadAdvisory(lastTwoAdvisories[0]) @@ -771,8 +832,8 @@ FORECASTER STEWART""" self._site, fileName) - print "SARAH: File contents for", fileName, ":" - print pythonDict + self.debug_print("SARAH: File contents for %s:" % (fileName), 1) + self.debug_print(repr(pythonDict), 1) # Only use transmitted advisories if pythonDict["Transmitted"] == False and advisoryName != "pending": @@ -780,7 +841,8 @@ FORECASTER STEWART""" else: return pythonDict except Exception, e: - print "SARAH Load Exception for %s : %s" % (fileName, e) + self.debug_print("SARAH Load Exception for %s : %s" % + (fileName, e), 1) return None def _getAdvisoryPath(self): @@ -879,104 +941,6 @@ FORECASTER STEWART""" "FloodingRainForecast": None, "TornadoThreat": None, } - - def _makeSegmentEditAreas(self, argDict): - areasList = self._segmentList - #print "areaList", areasList - editAreas = [] - self._editAreaDict = {} - for area in areasList: - self._editAreaDict[area] = area - editAreas.append((area, area)) - return editAreas - - def _determineSegments(self): - # Get the segments based on hazards "overlaid" with combinations file - - # Get the segments resulting from Hazards - #print "\nRaw Analyzed", self._hazardsTable.rawAnalyzedTable() - hazSegments = self.organizeHazards(self._hazardsTable.rawAnalyzedTable()) - #print "\nSegments from HazardsTable organizeHazards", hazSegments - - # Get the forecaster entered combinations - accessor = ModuleAccessor.ModuleAccessor() -# print "self._defaultEditAreas", self._defaultEditAreas - combos = accessor.variable(self._defaultEditAreas, "Combinations") - if combos is None: - LogStream.logVerbose("COMBINATION FILE NOT FOUND: " + self._defaultEditAreas) - return [], None - #print "\nSegments from Zone Combiner", combos - # "Overlay" the forecaster-entered combinations onto the segments - segmentList = self._refineSegments(hazSegments, combos) - #print "\nNew segments", segmentList - - # Instead of a segment being a group of zones, it will be just a single zone. - # So collapse this list of lists down to a list of zones (aka. segments) - segments = [] - for segment in segmentList: - segments += segment - - return segments - - def _refineSegments(self, hazSegments, combos): - """Break down each segment further according to combos given. - Make sure the resulting segments follow the ordering of the combos. - """ - if combos == []: - return hazSegments - newSegments = [] # list of lists - newAreas = [] - for combo, label in combos: - # Each combination will be tested to see if it can stay intact - # i.e. if all areas in the combo are in the same segment - # else split it into like segments - # - # segmentMapping is a list where each entry is - # the hazSegment in which the corresponding combo area appears. - # (We need to define self._segmentList for the mapping function - # to use) - self._segmentList = hazSegments - #print "self._segmentList = %s" % (repr(self._segmentList)) - segmentMapping = map(self._findSegment, combo) - #print " segmentMapping", segmentMapping - - # segmentDict keys will be the hazSegments and - # we will gather all the areas of the combos that appear - # in each of these hazSegments - segmentDict = {} - keyList = [] - for areaName in combo: - #print " Adding", areaName - key = tuple(segmentMapping[combo.index(areaName)]) - if key == (): # If no hazard for area, do not include - continue - if key not in keyList: - keyList.append(key) - segmentDict.setdefault(key,[]).append(areaName) - #print " segmentDict", segmentDict - - # Keep track of the areas that we are including - for key in keyList: - segAreas = segmentDict[key] - newAreas = newAreas + segAreas - newSegments.append(segAreas) - #print " newSegments", newSegments - # Now add in the hazAreas that have not been accounted for - # in the combinations - for hazSegment in hazSegments: - newSeg = [] - for hazArea in hazSegment: - if hazArea not in newAreas: - newSeg.append(hazArea) - if newSeg != []: - newSegments.append(newSeg) - return newSegments - - def _findSegment(self, areaName): - for segment in self._segmentList: - if areaName in segment: - return segment - return [] import Tkinter class Common_Dialog(Dialog): @@ -1424,3 +1388,4 @@ class TextProductCommon(DiscretePhrases.DiscretePhrases): return ugcStr + diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/textproducts/AreaDictionaryMaker.java b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/textproducts/AreaDictionaryMaker.java index 1215abd40d..afb67888a2 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/textproducts/AreaDictionaryMaker.java +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/textproducts/AreaDictionaryMaker.java @@ -127,11 +127,8 @@ public class AreaDictionaryMaker { FileUtil.join("gfe", "userPython", "textProducts", "Hazard_TCV.py")); if (lf.exists()) { - // TODO: Sarah add your TCVAreaDictionary creation code here - // if you add your code as a new method in - // createAreaDictionary.py which is probably the easiest way to - // do it, you can run it with the following line: - // pyScript.execute("createTCVAreaDictionary", argMap); + argMap.put("siteID", siteID); + pyScript.execute("createTCVAreaDictionary", argMap); } } catch (JepException e) { @@ -145,4 +142,4 @@ public class AreaDictionaryMaker { long t1 = System.currentTimeMillis(); theLogger.info("Area Dictionary generation time: " + (t1 - t0) + " ms"); } -} +} \ No newline at end of file diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/gfe/createAreaDictionary.py b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/gfe/createAreaDictionary.py index 671890445f..2f0027e6de 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/gfe/createAreaDictionary.py +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/gfe/createAreaDictionary.py @@ -25,6 +25,10 @@ import LogStream, pprint from fips2cities import * from zones2cities import * +from LockingFile import File +from com.raytheon.uf.common.localization import PathManagerFactory +from com.raytheon.uf.common.localization import LocalizationContext_LocalizationType as LocalizationType, LocalizationContext_LocalizationLevel as LocalizationLevel + # # Creates area dictionary specific to a site. Somewhat ported from AWIPS-I. # @@ -299,6 +303,193 @@ AreaDictionary = \ os.close(fh) +def createTCVAreaDictionary(outputDir, mapDict, siteID): + tcvAreaDictionaryContents = \ +""" +# ---------------------------------------------------------------------------- +# 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. +# +# TCV_AreaDictionary +# TCV_AreaDictionary file +# +# Author: GFE Installation Script +# ---------------------------------------------------------------------------- + +# Here is an example TCVAreaDictionary for just a single zone and with comments +# to talk about the structure of the dictionary. +# +# TCV_AreaDictionary = { +# # Zone +# 'FLZ173': { +# # A list of location names. +# 'locationsAffected': [ +# "Miami Beach", +# "Downtown Miami", +# ], +# +# # Potential impacts statements can be overriden here; anything not +# # overriden here will use the generic potential impacts statements +# 'potentialImpactsStatements': { +# # Section name: "Wind", "Storm Surge", "Flooding Rain" or "Tornado" +# "Wind": { +# # Threat level: "None", "Low", "Mod", "High" or "Extreme" +# "Extreme": [ +# # Each string will be on its own line +# "Widespread power outages with some areas experiencing long-term outages", +# "Many bridges and access routes connecting barrier islands impassable", +# "Structural category to sturdy buildings with some having complete wall and roof failures", +# "Complete destruction of mobile homes", +# "Numerous roads impassable from large debris", +# +# ], +# }, +# }, +# +# # Additional information that will be displayed at the end of the segment +# # The structure is a list containing strings and/or lists. Strings in the +# # same list will be idented the same amount. Introducing a list, idents the +# # text until it ends. For example: +# # +# # 'infoSection': [ +# # "This will be at tab level 0", +# # [ +# # "A new list was introduced so this is at tab level 1", +# # [ +# # "Yet another list so this is tab level 2", +# # "Still at tab level 2 here", +# # ], +# # "We are back at tab level 1 because we ended the list", +# # ], +# # "We ended the other list and are back at tab level 0 now", +# # ] +# 'infoSection': [ +# "LOCAL EVACUATION AND SHELTERING: MIAMI-DADE COUNTY EMERGENCY MANAGEMENT", +# [ +# "HTTP://WWW.MIAMIDADE.GOV/EMERGENCY/", +# ], +# "FAMILY EMERGENCY PLANS: FEDERAL EMERGENCY MANAGEMENT AGENCY", +# [ +# "HTTP://READY.GOV/", +# ], +# "LOCAL WEATHER CONDITIONS AND FORECASTS: NWS MIAMI FLORIDA", +# [ +# "HTTP://WWW.SRH.NOAA.GOV/MFL/", +# ], +# ], +# }, +# } + +TCV_AreaDictionary = { +""" + + zoneSkeletonContents = { + 'locationsAffected' : [], + 'potentialImpactsStatements' : {}, + 'infoSection' : [], + } + + existingTCVAreaDictionary = {} + try: + with open(outputDir + "/TCVAreaDictionary.py", "r") as existingFile: + contents = existingFile.read() + exec(contents) + + # TCV_AreaDictionary comes from the existing TCVAreaDictionary when it is exec'ed + existingTCVAreaDictionary = TCV_AreaDictionary + except Exception: + pass + + for zone in _getZones(siteID): + tcvAreaDictionaryContents += " '" + zone + "': {\n" + + # Don't clobber existing dictionary entries + if zone in existingTCVAreaDictionary: + # Add new entries + for key in zoneSkeletonContents: + if key not in existingTCVAreaDictionary[zone]: + existingTCVAreaDictionary[zone][key] = zoneSkeletonContents[key] + + # Remove entries that are no longer needed + existingKeys = existingTCVAreaDictionary[zone].keys() + for key in existingKeys: + if key not in zoneSkeletonContents: + existingTCVAreaDictionary[zone].pop(key) + + tcvAreaDictionaryContents += _formatDictionary(existingTCVAreaDictionary[zone], tabLevel = 2) + else: + tcvAreaDictionaryContents += _formatDictionary(zoneSkeletonContents, tabLevel = 2) + + tcvAreaDictionaryContents += " },\n\n" + + tcvAreaDictionaryContents += "}\n" + + with open(outputDir + "/TCVAreaDictionary.py", "w") as file: + file.write(tcvAreaDictionaryContents) + +def _getZones(siteID): + editAreasFilename = "gfe/combinations/EditAreas_PublicZones_" + \ + siteID + ".py" + zonesKey = "Zones_" + siteID + + editAreasFileContents = _getFileContents(LocalizationType.CAVE_STATIC, + LocalizationLevel.CONFIGURED, + siteID, + editAreasFilename) + exec(editAreasFileContents) + + # EASourceMap comes from the EditAreas file + return EASourceMap[zonesKey] + +def _getFileContents(loctype, loclevel, locname, filename): + pathManager = PathManagerFactory.getPathManager() + context = pathManager.getContext(loctype, loclevel) + context.setContextName(locname) + localizationFile = pathManager.getLocalizationFile(context, filename) + with File(localizationFile.getFile(), filename, 'r') as pythonFile: + fileContents = pythonFile.read() + + return fileContents + +def _formatDictionary(dictionary, tabLevel, output=""): + TAB = " " * 4 + + for key in dictionary: + output += TAB*tabLevel + repr(key) + ": " + + value = dictionary[key] + if type(value) is dict: + output += "{\n" + output = _formatDictionary(value, tabLevel+1, output) + output += TAB*tabLevel + "},\n" + elif type(value) is list: + output += "[\n" + output = _formatList(value, tabLevel+1, output) + output += TAB*tabLevel + "],\n" + else: + output += repr(value) + ",\n" + + return output + +def _formatList(theList, tabLevel, output=""): + TAB = " " * 4 + + for value in theList: + if type(value) is dict: + output += TAB*tabLevel + "{\n" + output = _formatDictionary(value, tabLevel+1, output) + output += TAB*tabLevel + "},\n" + elif type(value) is list: + output += TAB*tabLevel + "[\n" + output = _formatList(value, tabLevel+1, output) + output += TAB*tabLevel + "],\n" + else: + output += TAB*tabLevel + repr(value) + ",\n" + + return output + + # Utility to create the city location dictionary def createCityLocation(outputDir, mapDict): LogStream.logEvent("Generating CityLocation") diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/textproducts/templates/product/HLS.py b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/textproducts/templates/product/HLS.py index 3352125d03..0d406c4967 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/textproducts/templates/product/HLS.py +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/textproducts/templates/product/HLS.py @@ -1,3 +1,5 @@ +# Version 2014.11.24-0 + import GenericHazards import string, time, os, re, types, copy, LogStream, collections import ModuleAccessor, SampleAnalysis, EditAreaUtils @@ -48,6 +50,178 @@ class TextProduct(HLSTCV_Common.TextProduct): "URGENT - IMMEDIATE BROADCAST REQUESTED" # Optional EAS phrase to be include in product header Definition["callToAction"] = 1 + # Add options for debugging + Definition["debug"] = { + "__init__": 0, + "_inlandAreas": 0, + "_coastalAreas": 0, + "_cwa": 0, + "_cwa_descriptor": 0, + "_localReferencePoints": 0, + "_localReferencePoints_defaults": 0, + "_referencePointLimit": 0, + "_analysisList_HLS": 0, + "_analysisList_HLS_WholeDomain": 0, + "_intersectAnalysisList_HLS": 0, + "_productParts_HLS": 0, + "_impactsKeyFunction": 0, + "_ugcHeader": 0, + "_areaList": 0, + "_formatUGC_entries": 0, + "_getUgcInfo": 0, + "_summaryHeadlines": 0, + "_changesHazards": 0, + "_currentHazards": 0, + "_stormInformation": 0, + "_situationOverview": 0, + "_windSection": 0, + "_surgeSection": 0, + "_floodingRainSection": 0, + "_tornadoSection": 0, + "_coastalHazardsSection": 0, + "_preparednessSection": 0, + "_evacuationStatements": 0, + "_otherPreparednessActions": 0, + "_additionalSourcesInfo": 0, + "_nextUpdate": 0, + "_getPotentialImpactsStatements": 0, + "_impactCategoryToThreatLevel": 0, + "generateForecast": 0, + "_determineHazardStates": 0, + "_sampleHLSData": 0, + "_createWholeDomainEditArea": 0, + "_sampleMostSignificantDiscreteValue": 0, + "_sampleRankedDiscreteValue": 0, + "_getDominantThreatLevel": 0, + "_getHighestThreat": 0, + "_getLowestThreat": 0, + "_initializeVariables": 0, + "_initializeHeadlines": 0, + "_initializeSamplingDict": 0, + "_sampleTCVAdvisory": 0, + "_setHazardImpactCategories": 0, + "_grabHeadline": 0, + "_determineHazards": 0, + "SituationOverview": 0, + "_getStormInfo": 0, + "_grabStormInfo": 0, + "_decodeStormInfo": 0, + "_expandBearings": 0, + "_removeKM": 0, + "_cleanText": 0, + "_formatLocalTime": 0, + "_getTimeZoneList": 0, + "_calcLocalReferences": 0, + "_calcReference": 0, + "_distanceFromLatLon": 0, + "_bearing": 0, + "_dirInEnglish": 0, + "_processProductParts": 0, + "_noOpParts": 0, + "_createProductDictionary": 0, + "_formatProductDictionary": 0, + "_groupSegments": 0, + "_findNEWAssociatedWithUPG": 0, + "_initializeProductDict": 0, + "_initializeHazardsTable": 0, + "_setVTECActiveTable": 0, + "_allAreas": 0, + "_overview_list": 0, + "_displayGUI": 0, + "_frame": 0, + + # Legacy formatter class + "execute": 0, + "_processProductParts": 0, + "_noOpParts": 0, + "processWmoHeader": 0, + "processProductHeader": 0, + "formatIssueTime": 0, + "processSummaryHeadlines": 0, + "processHazards": 0, + "_areaWords": 0, + "processStormInformation": 0, + "processSituationOverview": 0, + "processHazardsSection": 0, + "processSubParts": 0, + + # HLSTCV_Common methods + "_wmoHeader": 0, + "_productHeader": 0, + "_initializeHazardsTable": 0, + "_setVTECActiveTable": 0, + "_getAllVTECRecords": 0, + "_ignoreActions": 0, + "_getAllowedHazardList": 0, + "_altFilterMethod": 0, + "_filterHazards": 0, + "_getAdditionalHazards": 0, + "_checkHazard": 0, + "getVtecRecords": 0, + "_getHazardsTable": 0, + "_initializeTimeVariables": 0, + "_determineTimeRanges": 0, + "_calculateStartTime": 0, + "_resolution": 0, + "_formatPeriod": 0, + "_getTimeDesc": 0, + "_getPartOfDay": 0, + "moderated_dict": 0, + "_getStatValue": 0, + "_allAreas": 0, + "_computeIntersectAreas": 0, + "_initializeStormInformation": 0, + "_parseTCP": 0, + "_getStormTypeFromTCP": 0, + "_getStormNameFromTCP": 0, + "_getAdvisoryTypeFromTCP": 0, + "_getAdvisoryNumberStringFromTCP": 0, + "_getStormNumberStringFromTCP": 0, + "_useTestTCP": 0, + "_testTCP": 0, + "_synchronizeAdvisories": 0, + "_getLocalAdvisoryDirectoryPath": 0, + "_loadLastTwoAdvisories": 0, + "_loadAdvisory": 0, + "_getAdvisoryPath": 0, + "_getLocalizationFile": 0, + "_getAdvisoryFilename": 0, + "_processVariableList": 0, + "_GUI_sizing_dict": 0, + "_GUI1_configDict": 0, + "_font_GUI_dict": 0, + "threatKeyOrder": 0, + "allowedHazards": 0, + "allowedHeadlines": 0, + "_initializeAdvisories": 0, + "_initializeSegmentZoneData": 0, + "getVarDict": 0, + "_makeRadioOrCheckList": 0, + "_makeEntry": 0, + "cancelCB": 0, + "_entryName": 0, + "_makeTuple": 0, + "_setVarDict": 0, + "status": 0, + "buttonbox": 0, + "setUp": 0, + "hazardTimeZones": 0, + "getExpireTime": 0, + "getHeadlinesAndSections": 0, + "formatUGCs": 0, + "getFormattedTime": 0, + "formatUGC_names": 0, + "formatNameString": 0, + "getVal": 0, + "formatDatetime": 0, + "flush": 0, + "makeUGCString": 0, + "checkLastArrow": 0, + } + +# Definition["debug"] = 1 # turn on ALL debug messages +# Definition["debug"] = 0 # turn off ALL debug messages + def __init__(self): HLSTCV_Common.TextProduct.__init__(self) @@ -227,7 +401,7 @@ class TextProduct(HLSTCV_Common.TextProduct): if self._ImpactsAnticipated: includedImpacts = sorted(self._IncludedImpacts, key=self._impactsKeyFunction) for ((_, sectionName), _) in includedImpacts: - print "SARAH: adding section = '%s'" % (sectionName) + self.debug_print("SARAH: adding section = '%s'" % (sectionName), 1) partsList.append(sectionName) partsList.append('preparednessSection') @@ -429,12 +603,15 @@ class TextProduct(HLSTCV_Common.TextProduct): elif len(impactParts) == 1: impactRangeRest = impactParts[0] + self.debug_print("MATT DEBUG: impactRange = '%s' impactMax = '%s' impactMin = '%s'" % (impactRange, impactMax, impactMin), 1) # If there are additional life-threatening surge areas if impactRange != impactMax and impactRange != impactMin: curPhrase = "Brace for %s%s damage across %s." % \ (lifeThreatening, impactRange, self._frame("ENTER AREA DESCRIPTION")) + self.debug_print("MATT DEBUG: curPhrase = '%s'" % (curPhrase), 1) + self.debug_print("MATT DEBUG: sectionDict['additionalImpactRange'] = '%s'" % (repr(sectionDict['additionalImpactRange'])), 1) # If this phrase is not already part of the additional impacts if curPhrase not in sectionDict['additionalImpactRange']: @@ -464,7 +641,8 @@ class TextProduct(HLSTCV_Common.TextProduct): # Add it now sectionDict['additionalImpactRange'].append(curPhrase) - + + self.debug_print("Final Surge sectionDict['additionalImpactRange'] = '%s'" % (sectionDict['additionalImpactRange']), 1) productDict['surgeSection'] = sectionDict def _floodingRainSection(self, productDict, productSegmentGroup, productSegment): @@ -645,7 +823,10 @@ class TextProduct(HLSTCV_Common.TextProduct): productDict['additionalSourcesInfo'] = infoDict def _nextUpdate(self, productDict, productSegmentGroup, productSegment): - if self._NextUpdate == "LastIssuance" or not self._ImpactsAnticipated: + + if not self._ImpactsAnticipated: + productDict['nextUpdate'] = "At this time...additional local statements are not anticipated unless conditions warrant." + elif self._NextUpdate == "LastIssuance": # or not self._ImpactsAnticipated: productDict['nextUpdate'] = "As it pertains to this event...this will be the last local statement issued by the National Weather Service in " + \ self._wfoCityState + \ " regarding the effects of tropical cyclone hazards upon the area." @@ -700,19 +881,22 @@ class TextProduct(HLSTCV_Common.TextProduct): return "Could not determine the storm name" self._loadLastTwoAdvisories() - if self._previousAdvisory is None: + if self._previousAdvisory is None and self._ImpactsAnticipated: return "A TCV must be transmitted before an HLS can be run" self._initializeHeadlines() self._initializeHazardsTable(argDict) - self._determineHazardStates() - if self._ImpactsAnticipated: + + self._determineHazardStates() + # Sample the data self._initializeSamplingDict() + self._sampleTCVAdvisory(self._previousAdvisory) + self._sampleHLSData(argDict) for threatName in ['WindThreat', 'StormSurgeThreat', 'FloodingRainThreat', 'TornadoThreat']: @@ -729,11 +913,22 @@ class TextProduct(HLSTCV_Common.TextProduct): self._currentHazardsList = [] self._changesHazardsList = [] + self.debug_print("*"*80) + keys = self._previousAdvisory.keys() + keys.sort() + for key in keys: + self.debug_print("%s : %s" % (key, self._previousAdvisory[key]), 1) for hazard in self._previousAdvisory["HazardsForHLS"]: - print "SARAH DEBUG Hazard: %s" % (repr(hazard)) + self.debug_print("SARAH DEBUG Hazard: %s" % (repr(hazard)), 1) if hazard['act'] != 'CON': self._changesHazardsList.append(hazard) - self._currentHazardsList.append(hazard) + if hazard['act'] != 'CAN': + self._currentHazardsList.append(hazard) + + self.debug_print("-"*80, 1) + self.debug_print("self._changesHazardsList = %s" % (self._changesHazardsList), 1) + self.debug_print("self._currentHazardsList = %s" % (self._currentHazardsList), 1) + ############################################################### ### Sampling and Statistics related methods @@ -765,9 +960,9 @@ class TextProduct(HLSTCV_Common.TextProduct): if decidingField is None or qpfToFfgRatio > decidingField: self._samplingDict['FloodingRainThreat']['decidingField'] = qpfToFfgRatio - print "SARAH: WindThreat = %s" % (self._samplingDict['WindThreat']['inputThreatDominant']) - print "SARAH: FloodingRainThreat = %s" % (self._samplingDict['FloodingRainThreat']['inputThreatDominant']) - print "SARAH: TornadoThreat = %s" % (self._samplingDict['TornadoThreat']['inputThreatDominant']) + self.debug_print("SARAH: WindThreat = %s" % (self._samplingDict['WindThreat']['inputThreatDominant']), 1) + self.debug_print("SARAH: FloodingRainThreat = %s" % (self._samplingDict['FloodingRainThreat']['inputThreatDominant']), 1) + self.debug_print("SARAH: TornadoThreat = %s" % (self._samplingDict['TornadoThreat']['inputThreatDominant']), 1) @@ -810,7 +1005,7 @@ class TextProduct(HLSTCV_Common.TextProduct): if decidingField is None or inundationMax > decidingField: self._samplingDict['StormSurgeThreat']['decidingField'] = inundationMax - print "SARAH: StormSurgeThreat = %s" % (self._samplingDict['StormSurgeThreat']['inputThreatDominant']) + self.debug_print("SARAH: StormSurgeThreat = %s" % (self._samplingDict['StormSurgeThreat']['inputThreatDominant']), 1) def _createWholeDomainEditArea(self, argDict): editAreaUtils = EditAreaUtils.EditAreaUtils() @@ -825,43 +1020,43 @@ class TextProduct(HLSTCV_Common.TextProduct): editAreaUtils.saveEditAreas([refData]) def _sampleMostSignificantDiscreteValue(self, threatName, statDict): - print "SARAH: _sampleMostSignificantDiscreteValue for %s" % (threatName) + self.debug_print("SARAH: _sampleMostSignificantDiscreteValue for %s" % (threatName), 1) threatLevel = self.getStats(statDict, threatName + "__mostSignificantDiscreteValue") - print "SARAH: threatLevel =", threatLevel + self.debug_print("SARAH: threatLevel = %s" % (threatLevel), 1) if threatLevel is not None: inputThreatLow = self._samplingDict[threatName]['inputThreatLow'] - print "SARAH: current inputThreatLow =", inputThreatLow + self.debug_print("SARAH: current inputThreatLow = %s" % (inputThreatLow), 1) if inputThreatLow is None: self._samplingDict[threatName]['inputThreatLow'] = threatLevel else: self._samplingDict[threatName]['inputThreatLow'] = self._getLowestThreat(threatName, threatLevel, inputThreatLow) - print "SARAH: new inputThreatLow =", self._samplingDict[threatName]['inputThreatLow'] + self.debug_print("SARAH: new inputThreatLow = %s" % (self._samplingDict[threatName]['inputThreatLow']), 1) inputThreatHigh = self._samplingDict[threatName]['inputThreatHigh'] - print "SARAH: current inputThreatHigh =", inputThreatHigh + self.debug_print("SARAH: current inputThreatHigh = %s" % (inputThreatHigh), 1) self._samplingDict[threatName]['inputThreatHigh'] = self._getHighestThreat(threatName, threatLevel, inputThreatHigh) - print "SARAH: new inputThreatHigh =", self._samplingDict[threatName]['inputThreatHigh'] + self.debug_print("SARAH: new inputThreatHigh = %s" % (self._samplingDict[threatName]['inputThreatHigh']), 1) def _sampleRankedDiscreteValue(self, threatName, statDict): - print "-" * 60 - print "_sampleRankedDiscreteValue statDict = %s" % (repr(statDict)) + self.debug_print("-" * 60, 1) + self.debug_print("_sampleRankedDiscreteValue statDict = %s" % (repr(statDict)), 1) rankedThreatLevels = self.getStats(statDict, threatName + "__rankedDiscreteValue") - print "SARAH: sampling %s" % (threatName) - print "SARAH: sampleData: rankedThreatLevels = %s" % (repr(rankedThreatLevels)) + self.debug_print("SARAH: sampling %s" % (threatName), 1) + self.debug_print("SARAH: sampleData: rankedThreatLevels = %s" % (repr(rankedThreatLevels)), 1) if rankedThreatLevels is not None: dominantThreatLevel = self._getDominantThreatLevel(threatName, rankedThreatLevels) - print "SARAH: dominantThreatLevel =", dominantThreatLevel + self.debug_print("SARAH: dominantThreatLevel = %s" % (dominantThreatLevel), 1) currentDominantThreatLevel = self._samplingDict[threatName]['inputThreatDominant'] - print "SARAH: currentDominantThreatLevel =", currentDominantThreatLevel + self.debug_print("SARAH: currentDominantThreatLevel = %s" % (currentDominantThreatLevel), 1) self._samplingDict[threatName]['inputThreatDominant'] = self._getHighestThreat(threatName, dominantThreatLevel, currentDominantThreatLevel) - print "SARAH: new dominant =", self._samplingDict[threatName]['inputThreatDominant'] + self.debug_print("SARAH: new dominant = %s" % (self._samplingDict[threatName]['inputThreatDominant']), 1) def _getDominantThreatLevel(self, threatName, rankedThreatLevels): dominantLevelWithHighestRank = None @@ -959,18 +1154,18 @@ class TextProduct(HLSTCV_Common.TextProduct): self._samplingDict['FloodingRainThreat']['catastrophicThreshold'] = 3 # percent def _sampleTCVAdvisory(self, advisory): - print "SARAH: sampling TCV advisory!" + self.debug_print("SARAH: sampling TCV advisory!", 1) for zone in advisory["ZoneData"]: - print "-" * 60 - print "Looking at zone %s" % (zone) + self.debug_print("-" * 60, 1) + self.debug_print("Looking at zone %s" % (zone), 1) for key in advisory["ZoneData"][zone]: if "Threat" not in key: continue - print "Looking at key '%s'" % (key) + self.debug_print("Looking at key '%s'" % (key), 1) threatLevel = advisory["ZoneData"][zone][key] - print " Threat level = %s" % (threatLevel) + self.debug_print(" Threat level = %s" % (threatLevel), 1) if self._samplingDict[key]['inputThreatLow'] is None: self._samplingDict[key]['inputThreatLow'] = threatLevel if self._samplingDict[key]['inputThreatHigh'] is None: @@ -985,13 +1180,13 @@ class TextProduct(HLSTCV_Common.TextProduct): if threatOrder.index(threatLevel) > threatOrder.index(highThreat): highThreat = threatLevel - print " low threat = %s" % (lowThreat) - print " high threat = %s" % (highThreat) + self.debug_print(" low threat = %s" % (lowThreat), 1) + self.debug_print(" high threat = %s" % (highThreat), 1) self._samplingDict[key]['inputThreatLow'] = lowThreat self._samplingDict[key]['inputThreatHigh'] = highThreat - print "Sampling dict = %s" % (repr(self._samplingDict)) + self.debug_print("Sampling dict = %s" % (repr(self._samplingDict)), 1) def _setHazardImpactCategories(self, threatName): inputThreatLow = self._samplingDict[threatName]['inputThreatLow'] @@ -1000,8 +1195,8 @@ class TextProduct(HLSTCV_Common.TextProduct): decidingField = self._samplingDict[threatName]['decidingField'] catastrophicThreshold = self._samplingDict[threatName]['catastrophicThreshold'] - print "-" * 60 - print "MATT DEBUG: _setHazardImpactCategories for %s" % (threatName) + self.debug_print("-" * 60, 1) + self.debug_print("MATT DEBUG: _setHazardImpactCategories for %s" % (threatName), 1) impactMin = None impactMax = None @@ -1044,8 +1239,9 @@ class TextProduct(HLSTCV_Common.TextProduct): impactMax = "none" impactRangeMax = "none" - print "MATT DEBUG: impactMin = '%s' impactMax = '%s' impactRangeMax = '%s'" % \ - (impactMin, impactMax, impactRangeMax) + self.debug_print( + "MATT DEBUG: impactMin = '%s' impactMax = '%s' impactRangeMax = '%s'" % \ + (impactMin, impactMax, impactRangeMax), 1) # Determine dominant impact category for rest of CWA - No impact if impactMin == "none" and impactMax == "none": @@ -1072,15 +1268,19 @@ class TextProduct(HLSTCV_Common.TextProduct): def _grabHeadline(self, text=''): # Get first headline found in text and return it as a string + self.debug_print("_grabHeadline text = '%s'" % (text)) + # Fixed pattern to grab headline (MHB 04/08/2009) # See if there is a headline in this text headlineSearch = re.findall("(?ism)^(\.{3}.+?\.{3}) *\n", text) + self.debug_print("old headlineSearch = %s" % (headlineSearch), 1) + # If we could not find original headlines, try to use 'new' HLS style - if headlineSearch is None: + if headlineSearch is None or headlineSearch == []: headlineSearch = re.findall("(?ism)^\*\*.+?\*\* *\n", text) - self.debug_print("headlineSearch = %s" % (headlineSearch)) + self.debug_print("now headlineSearch = %s" % (headlineSearch), 1) # If we found a headline if len(headlineSearch) > 0: @@ -1122,9 +1322,10 @@ class TextProduct(HLSTCV_Common.TextProduct): hazardList.append(key) else: hazardDict[key] = hazardDict[key]+segment + + #self.debug_print("hazardList = %s" % (repr(hazardList)), 1) return hazardList - #print "\nhazardList", hazardList def SituationOverview(self, title, info): t = title @@ -1132,11 +1333,11 @@ class TextProduct(HLSTCV_Common.TextProduct): ec = self._EventContext if ec == "Abbreviated": hdlns = info.hazardHdlns - #print "\n Headlines" + #self.debug_print("*** Headlines ***") reported = 0 for hazardHdln in hdlns: key = hazardHdln - #print "hazard", hazardHdln + #self.debug_print("hazard = '%s'" % (hazardHdln), 1) hdln, act, phen, sig = key if phen == "HU" and sig == "S": continue @@ -1189,20 +1390,20 @@ class TextProduct(HLSTCV_Common.TextProduct): # Storm intensity in mph and the stated intensity trend. self._stormIntensityTrend = "Storm Intensity " + stormDict.get("StormIntensity","") - print "SARAH: BEGIN STORM INFORMATION" - print "storm dict = %s" % (stormDict) - print "storm name = %s" % (self._stormName) - print "type = %s" % (self._stormType) - print "type name = %s" % (self._stormTypeName) - print "time = %s" % (self._stormTime) - print "lat = %s" % (self._stormLat) - print "lon = %s" % (self._stormLon) - print "location = %s" % (str(self._stormLocation)) - print "reference = %s" % (self._stormReference) - print "references = %s" % (self._stormLocalReferences) - print "movement trend = %s" % (self._stormMovementTrend) - print "intensity trend = %s" % (self._stormIntensityTrend) - print "SARAH: END STORM INFORMATION" + self.debug_print("SARAH: BEGIN STORM INFORMATION", 1) + self.debug_print("storm dict = %s" % (stormDict), 1) + self.debug_print("storm name = %s" % (self._stormName), 1) + self.debug_print("type = %s" % (self._stormType), 1) + self.debug_print("type name = %s" % (self._stormTypeName), 1) + self.debug_print("time = %s" % (self._stormTime), 1) + self.debug_print("lat = %s" % (self._stormLat), 1) + self.debug_print("lon = %s" % (self._stormLon), 1) + self.debug_print("location = %s" % (str(self._stormLocation)), 1) + self.debug_print("reference = %s" % (self._stormReference), 1) + self.debug_print("references = %s" % (self._stormLocalReferences), 1) + self.debug_print("movement trend = %s" % (self._stormMovementTrend), 1) + self.debug_print("intensity trend = %s" % (self._stormIntensityTrend), 1) + self.debug_print("SARAH: END STORM INFORMATION", 1) def _grabStormInfo(self, tcp): # Get the storm information from the selected TCP @@ -1236,7 +1437,7 @@ class TextProduct(HLSTCV_Common.TextProduct): "(SPECIAL |INTERMEDIATE )?ADVISORY", tcp) # Display some debug info - if flag is set - self.debug_print("mndSearch = '%s'" % (mndSearch)) + self.debug_print("mndSearch = '%s'" % (mndSearch), 1) # If we found the storm type and name in the MND header if mndSearch is not None: @@ -1327,7 +1528,7 @@ class TextProduct(HLSTCV_Common.TextProduct): # Assume we only have one NHC reference point by default nhcReference = dict["StormReference"] -## print "referenceIndex = ", referenceIndex +## self.debug_print("referenceIndex = %s" % (referenceIndex), 1) # If we have more than one NHC reference point if referenceIndex != -1: @@ -1375,13 +1576,13 @@ class TextProduct(HLSTCV_Common.TextProduct): # Display some debug info - if flag is set self.debug_print("storminfoSearch = '%s'" % (stormInfoSearch)) -## print stormInfoSearch.groups() +## self.debug_print(repr(stormInfoSearch.groups()), 1) # If we found the storm info section of the product if stormInfoSearch is not None: # for group in stormInfoSearch.groups(): -# print '\t' + '-'*50 -# print "%s\n" % (group) +# self.debug_print('-'*50, 1) +# self.debug_print("%s\n" % (group), 1) # Clean this section up a bit. Keep each paragraph separate # by a single , but remove all others as well as extra @@ -1413,7 +1614,7 @@ class TextProduct(HLSTCV_Common.TextProduct): # If we cannot find the summary, try to find a "repeating" section if repeatInfo is None: repeatInfo = re.search("(?is)(REPEATING.+?\.)\n *\n", tcp) -## print repeatInfo +## self.debug_print(repr(repeatInfo), 1) # If we found the repeated storm information summary if repeatInfo is not None: @@ -1432,8 +1633,8 @@ class TextProduct(HLSTCV_Common.TextProduct): '(\d+\.\d+ *[EW])', summary) # Display some debug info - if flag is set - self.debug_print("locationSearch = '%s'" % (locationSearch)) -## print locationSearch.groups() + self.debug_print("locationSearch = '%s'" % (locationSearch), 1) +## self.debug_print(repr(locationSearch.groups()), 1) # If we found the storm location section of the product if locationSearch is not None: @@ -1453,7 +1654,7 @@ class TextProduct(HLSTCV_Common.TextProduct): # Display some debug info - if flag is set self.debug_print("intensitySearch = '%s'" % - (intensitySearch)) + (intensitySearch), 1) # If we found the storm intensity section of the product if intensitySearch is not None: @@ -1474,7 +1675,7 @@ class TextProduct(HLSTCV_Common.TextProduct): summary) # Display some debug info - if flag is set - self.debug_print("motionSearch = '%s'" % (motionSearch)) + self.debug_print("motionSearch = '%s'" % (motionSearch), 1) # If we found the storm motion section of the product if motionSearch is not None: @@ -1492,18 +1693,18 @@ class TextProduct(HLSTCV_Common.TextProduct): #======================================================================== # Display final decoded information from TCP -## print "\n\n" + "*" *80 -## print "Final TCP Info...\n" -## print 'dict["StormType"] = ', dict["StormType"] -## print 'dict["StormName"] = ', dict["StormName"] -## print 'dict["StormTime"] = ', dict["StormTime"] -## print 'dict["StormLat"] = ', dict["StormLat"] -## print 'dict["StormLon"] = ', dict["StormLon"] -## print 'dict["StormReference"] = ', dict["StormReference"] -## print 'dict["StormIntensity"] = ', dict["StormIntensity"] -## print 'dict["StormMotion"] = ', dict["StormMotion"] -## print 'dict["StormInfo"] = ', dict["StormInfo"] -## print 'dict["StormCenter"] = ', dict["StormCenter"] +## self.debug_print("*" *80, 1) +## self.debug_print("Final TCP Info...\n", 1) +## self.debug_print('dict["StormType"] = %s' % (dict["StormType"]), 1) +## self.debug_print('dict["StormName"] = %s' % (dict["StormName"]), 1) +## self.debug_print('dict["StormTime"] = %s' % (dict["StormTime"]), 1) +## self.debug_print('dict["StormLat"] = %s' % (dict["StormLat"]), 1) +## self.debug_print('dict["StormLon"] = %s' % (dict["StormLon"]), 1) +## self.debug_print('dict["StormReference"] = %s' % (dict["StormReference"]), 1) +## self.debug_print('dict["StormIntensity"] = %s' % (dict["StormIntensity"]), 1) +## self.debug_print('dict["StormMotion"] = %s' % (dict["StormMotion"]), 1) +## self.debug_print('dict["StormInfo"] = %s' % (dict["StormInfo"]), 1) +## self.debug_print('dict["StormCenter"] = %s' % (dict["StormCenter"]), 1) # Return the dictionary will all the information we found in the TCP return dict @@ -1516,7 +1717,7 @@ class TextProduct(HLSTCV_Common.TextProduct): self._stormReference = "" self._stormLocalReferences = "" para = stormDict.get("StormCenter", "") - # print "\npara", len(para), para + # self.debug_print("para %d %s" % (len(para), para), 1) if len(para)<= 0: return @@ -1540,13 +1741,13 @@ class TextProduct(HLSTCV_Common.TextProduct): # Try to find these patterns in the text coordPtnMatch = coordPtn.search(para) -## print "+" * 90 -## print "\ncoordinate search..." -## print coordPtnMatch.groups() +## self.debug_print("+" * 90, 1) +## self.debug_print("coordinate search...", 1) +## self.debug_print(coordPtnMatch.groups(), 1) refPtnMatch = refPtn.search(para) -## print "\nreference search..." -## print refPtnMatch.groups() +## self.debug_print("reference search...", 1) +## self.debug_print(refPtnMatch.groups(), 1) # If we found the coordinates we were after if coordPtnMatch is not None: @@ -1590,12 +1791,12 @@ class TextProduct(HLSTCV_Common.TextProduct): self._stormLocalReferences = self._calcLocalReferences( self._stormLat, self._stormLon) -## print "stormLocalRefs = ", self._stormLocalReferences +## self.debug_print("stormLocalRefs = %s" % (self._stormLocalReferences), 1) # Compare the NHC reference to the local references for localRef in self._stormLocalReferences: -## print self._stormReference, localRef +## self.debug_print("self._stormReference = '%s', localRef = '%s'" % (self._stormReference, localRef), 1) # Get the locations from these statements nhcRef = re.search('(?i)(north|south|east|west) of (.+)', @@ -1603,7 +1804,7 @@ class TextProduct(HLSTCV_Common.TextProduct): testRef = re.search('(?i)(north|south|east|west) of (.+)', localRef) -## print "nhcRef = '%s'\ttestRef = '%s'" % (nhcRef.group(2), testRef.group(2)) +## self.debug_print("nhcRef = '%s'\ttestRef = '%s'" % (nhcRef.group(2), testRef.group(2)), 1) # If we have a local reference that matches the national # center reference @@ -1642,7 +1843,7 @@ class TextProduct(HLSTCV_Common.TextProduct): # Remove references to KM e.g. # 420 KM... 100 KM/HR... -# print "words = '%s'" % (words) +# self.debug_print("words = '%s'" % (words), 1) kmSearch = re.compile("\.\.\. *[0-9]+ +(KM|KM/HR?) *\.?\.?\.?") @@ -1655,7 +1856,7 @@ class TextProduct(HLSTCV_Common.TextProduct): for doubleSpace in doubleSpaces: words = re.sub(doubleSpace, ' ', words) -# print "\tfinal words = '%s'" % (words) +# self.debug_print("\tfinal words = '%s'" % (words), 1) return words def _cleanText(self, text=''): @@ -1780,7 +1981,7 @@ class TextProduct(HLSTCV_Common.TextProduct): direction = self._bearing(lat1, lon1, lat0, lon0) direction = self._dirInEnglish(direction) localRef ="ABOUT "+distMph_str+" MILES "+direction - #print "localRef", localRef +# self.debug_print("localRef = %s" % (localRef), 1) return localRef # Returns the distance from lat0, lon0 to lat1, lon1 in kilometers @@ -1809,14 +2010,6 @@ class TextProduct(HLSTCV_Common.TextProduct): return direction -## lat0 = 30.0 -## lat1 = 20.0 -## lon0 = -80.0 -## lon1 = -90.0 - -## print "complex dist:", distComplex(lat0, lon0, lat1, lon1) -## print "bearing:", bearing(lat0, lon0, lat1, lon1) - def _dirInEnglish(self, direction): dirList = ["North", "North-Northeast", "Northeast", "East-Northeast", @@ -1877,7 +2070,7 @@ class TextProduct(HLSTCV_Common.TextProduct): removedParts.append(part) for part in removedParts: - print "SARAH: Removing part = %s" % (part) + self.debug_print("SARAH: Removing part = %s" % (part), 1) partsList.remove(part) def _noOpParts(self): @@ -1924,7 +2117,7 @@ class TextProduct(HLSTCV_Common.TextProduct): upgPhenSig = record['phen'] + "." + record['sig'] newRecord = self._findNEWAssociatedWithUPG(upgPhenSig, vtecRecords) record['new_record'] = newRecord - print "SARAH: vtecRecord = %s" % (repr(record)) + self.debug_print("SARAH: vtecRecord = %s" % (repr(record))) segment_vtecRecords_tuples.append((segment, vtecRecords)) productSegmentGroup = { @@ -2022,9 +2215,9 @@ class TextProduct(HLSTCV_Common.TextProduct): dataMgr = argDict["dataMgr"] gfeMode = dataMgr.getOpMode().name() - print "*" *100 - print "gfeMode = '%s'" % (gfeMode) - print "*" *100 + self.debug_print("*" *100, 1) + self.debug_print("gfeMode = '%s'" % (gfeMode), 1) + self.debug_print("*" *100, 1) if gfeMode == "PRACTICE": @@ -2035,21 +2228,6 @@ class TextProduct(HLSTCV_Common.TextProduct): def _allAreas(self): return self._inlandAreas() + self._coastalAreas() - ############################################################### - ### Time related methods - - def _initializeTimeVariables(self, argDict): - argDict['creationTime'] = int(time.time()/60)*60.0 - self._issueTime_secs = argDict['creationTime'] - self._issueTime = self._issueTime_secs * 1000 # in milliseconds - - self._ddhhmmTime = self.getCurrentTime( - argDict, "%d%H%M", shiftToLocal=0, stripLeading=0) - self._currentTime = self._issueTime_secs - self._expireTime = self._issueTime_secs + self._purgeTime*3600 - self._timeLabel = self.getCurrentTime( - argDict, "%l%M %p %Z %a %b %e %Y", stripLeading=1) - ############################################################### ### GUI related methods @@ -2112,14 +2290,14 @@ class TextProduct(HLSTCV_Common.TextProduct): ("Last Issuance", "LastIssuance"), ("Enter Approximate Time (below)", "Enter") ], - "default": "Shortly", + "default": "Enter Approximate Time (below)", "entryField": " e.g. 6 AM EDT", }, { "name": "MainHeadline", "label": "Step 7. Input Main Headline (required)", "options": [ - ("Enter Unique Headline (below)", "Enter"), + ("Enter Unique Headline (to right)", "Enter"), ("Use Previous HLS Headline", "UsePrev"), ("Use Latest TCP Headline", "UseTCP"), ], @@ -2274,7 +2452,7 @@ class Overview_Dialog(HLSTCV_Common.Common_Dialog): # pull the data from the tkObject_dict before they get toasted tkObject_dict = self._tkObject_dict overviewList = self._parent._overview_list() - print "SARAH: in okCB!" + print("SARAH: in okCB!") for infoDict in overviewList: name = infoDict["name"] label = infoDict["label"] @@ -2293,7 +2471,7 @@ class Overview_Dialog(HLSTCV_Common.Common_Dialog): checkList.append((options[i], svar.get())) else: if ivarList[i].get(): - print "SARAH: adding option = %s" % (repr(options[i])) + print("SARAH: adding option = %s" % (repr(options[i]))) checkList.append(options[i]) value = checkList self._setVarDict(name, value) @@ -2331,7 +2509,7 @@ class LegacyFormatter(): @return text -- product string ''' text = '' - print "SARAH: productParts = %s" % (productParts) + print("SARAH: productParts = %s" % (productParts)) for part in productParts: valtype = type(part) if valtype is str: @@ -2339,15 +2517,15 @@ class LegacyFormatter(): elif valtype is tuple: name = part[0] infoDicts = part[1] - print "SARAH: name = %s" % (str(name)) - print "SARAH: infoDicts = %s" % (repr(infoDicts)) + self.debug_print("SARAH: name = %s" % (str(name)), 1) + self.debug_print("SARAH: infoDicts = %s" % (repr(infoDicts)), 1) newtext = self.processSubParts(productDict.get(name), infoDicts) - print "SARAH: newtext type = %s" % (type(newtext)) - print "SARAH: newtext = %s" % (repr(newtext)) + self.debug_print("SARAH: newtext type = %s" % (type(newtext)), 1) + self.debug_print("SARAH: newtext = %s" % (repr(newtext)), 1) text += newtext continue elif valtype is list: - print 'GOT HERE -- found list' + self.debug_print('GOT HERE -- found list', 1) self._tpc.flush() # TODO THIS SHOULD BE REMOVED AFTER THE REFACTOR OF HazardServicesProductGenerationHandler.JAVA tup = (part[0], part[1]) @@ -2397,13 +2575,15 @@ class LegacyFormatter(): if productDict[name]['genericAction'] is not None: text += self._textProduct.indentText(productDict[name]['genericAction'], maxWidth=self._textProduct._lineLength) + "\n" elif name == "evacuationStatements": - text += "* " + productDict[name]['title'] + ":\n" + text += "* " + productDict[name]['title'] + ":\n|* " for statement in productDict[name]['statements']: text += self._textProduct.indentText(statement, maxWidth=self._textProduct._lineLength) + "\n" + text += "*|\n" elif name == "otherPreparednessActions": - text += "* " + productDict[name]['title'] + ":\n" + text += "* " + productDict[name]['title'] + ":\n|* " for action in productDict[name]['actions']: text += self._textProduct.indentText(action, maxWidth=self._textProduct._lineLength) + "\n" + text += "*|\n" elif name == "additionalSourcesInfo": text += "* " + productDict[name]['title'] + ":\n" for source in productDict[name]['sources']: @@ -2427,8 +2607,8 @@ class LegacyFormatter(): text += '&&\n' elif name not in self._noOpParts(): textStr = productDict.get(name) - print "SARAH: name = %s" % (name) - print "SARAH: textStr = '%s'" % (textStr) + self.debug_print("SARAH: name = %s" % (name), 1) + self.debug_print("SARAH: textStr = '%s'" % (textStr), 1) if textStr: text += textStr + '\n' return text @@ -2594,8 +2774,13 @@ class LegacyFormatter(): additionalImpactRangeText = "" curAdditionalImpactText = "" count = 1 + + print("MATT DEBUG: %d sectionDict['additionalImpactRange'] = '%s'" % (len(sectionDict['additionalImpactRange']), sectionDict['additionalImpactRange'])) for additionalImpact in sectionDict['additionalImpactRange']: + print("additionalImpact = '%s'" % (additionalImpact)) + print("count = %d" % (count)) + curAdditionalImpactText += \ self._textProduct.indentText(additionalImpact, maxWidth=self._textProduct._lineLength) @@ -2604,18 +2789,16 @@ class LegacyFormatter(): len(curAdditionalImpactText) > 0: curAdditionalImpactText += "\n" - # If this additional impact is not already included in the output - if additionalImpactRangeText.find(curAdditionalImpactText) == -1: + print("MATT DEBUG: curAdditionalImpactText ='%s'" % (curAdditionalImpactText)) - # Add this additional impact text - print "Adding current impact." - additionalImpactRangeText += curAdditionalImpactText count += 1 - # Remove the trailing space -# additionalImpactRangeText = additionalImpactRangeText[:-1] - -# text += self._textProduct.indentText(additionalImpactRangeText, maxWidth=self._textProduct._lineLength) + # If this additional impact is not already included in the output + if additionalImpactRangeText.find(curAdditionalImpactText) == -1: + + # Add this additional impact text + print("Adding current impact. '%s'" % (curAdditionalImpactText)) + additionalImpactRangeText += curAdditionalImpactText text += additionalImpactRangeText @@ -2631,10 +2814,11 @@ class LegacyFormatter(): """ text = '' for i in range(len(subParts)): - print "SARAH: subpart subParts[i] = %s" % (subParts[i]) - print "SARAH: subpart infoDicts[i] = %s" % (infoDicts[i]) + print("SARAH: subpart subParts[i] = %s" % (subParts[i])) + print("SARAH: subpart infoDicts[i] = %s" % (infoDicts[i])) newtext = self._processProductParts(subParts[i], infoDicts[i].get('partsList')) - print "SARAH: subpart newtext type = %s" % (type(newtext)) - print "SARAH: subpart newtext = '%s'" % (repr(newtext)) + print("SARAH: subpart newtext type = %s" % (type(newtext))) + print("SARAH: subpart newtext = '%s'" % (repr(newtext))) text += newtext return text + diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/textproducts/templates/product/Hazard_TCV.py b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/textproducts/templates/product/Hazard_TCV.py index 6027e14a6c..3013faf0db 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/textproducts/templates/product/Hazard_TCV.py +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/textproducts/templates/product/Hazard_TCV.py @@ -1,8 +1,8 @@ +# Version 2014.12.03-0 import GenericHazards import JsonSupport -import LocalizationSupport -import string, time, os, types, copy, LogStream, collections -import ModuleAccessor, SampleAnalysis, EditAreaUtils +import time, types, copy, LogStream, collections +import ModuleAccessor import math @@ -47,6 +47,158 @@ class TextProduct(HLSTCV_Common.TextProduct): Definition["easPhrase"] = \ "URGENT - IMMEDIATE BROADCAST REQUESTED" # Optional EAS phrase to be include in product header Definition["callToAction"] = 1 + + Definition["debug"] = { + # TextProduct class + "__init__": 0, + "_inlandAreas": 0, + "_coastalAreas": 0, + "_cwa": 0, + "_analysisList": 0, + "_intersectAnalysisList": 0, + "_productParts_TCV": 0, + "_segmentParts_TCV": 0, + "generateForecast": 0, + "_initializeVariables": 0, + "_createProductDictionary": 0, + "_formatProductDictionary": 0, + "_easMessage": 0, + "_setup_segment": 0, + "_ugcHeader": 0, + "_vtecRecords": 0, + "_areaList": 0, + "_locationsAffected": 0, + "_fcstConfidence": 0, + "_infoSection": 0, + "_endSection": 0, + "_issuanceTimeDate": 0, + "_summaryHeadlines": 0, + "_processProductParts": 0, + "_noOpParts": 0, + "_initializeProductDict": 0, + "_wrapUpProductDict": 0, + "_groupSegments": 0, + "_makeSegmentEditAreas": 0, + "_determineSegments": 0, + "_refineSegments": 0, + "_findSegment": 0, + "_formatUGC_entries": 0, + "_getUgcInfo": 0, + "_hazardDefinition": 0, + "_sampleData": 0, + "_getStats": 0, + "_formatPeriod": 0, + "_getTimeDesc": 0, + "_getPartOfDay": 0, + "_convertToISO": 0, + "_convertToDatetime": 0, + "_overview_list": 0, + "_displayGUI": 0, + "_archiveCurrentAdvisory": 0, + "_saveAdvisory": 0, + "_getHazardsForHLS": 0, + + # SectionCommon + "_setProductPartValue": 0, + "_finalSectionParts": 0, + "_sectionHeader": 0, + "_lifePropertyThreatSummary": 0, + "_getThreatTrendSentence": 0, + "_getThreatTrendValue": 1, + "_threatDifference": 1, + "_isThreatDecreasing": 1, + "_isThreatIncreasing": 1, + "_advisoryHasValidKey": 0, + "_isMagnitudeIncreasing": 1, + "_calculateThreatStatementTr": 0, + "_setThreatStatementsProductParts": 0, + "_getThreatStatements": 0, + "_potentialImpactsSummary": 0, + "_getPotentialImpactsSummaryText": 0, + "_potentialImpactsStatements": 0, + "_getPotentialImpactsStatements": 0, + "_preparationStatement": 0, + + # Unique to each section, but common method name + "_threatSubsection": 0, + "_threatTrend": 0, + "_threatStatements": 0, + "_impactsSubsection": 0, + + # WindSection + "sectionParts": 0, + "_forecastSubsection": 0, + "_latestForecastSummary": 0, + "_peakWind": 0, + "_windowTS": 0, + "_windowHU": 0, + "_moderatedMaxWindMph_categories": 0, + "_ktToMph": 0, + "_increment": 0, + + # StormSurgeSection + "sectionParts": 0, + "_forecastSubsection": 0, + "_latestForecastSummary": 1, + "_peakSurge": 0, + "_surgeWindow": 1, + "_lifePropertyThreatSummary": 0, + "_potentialImpactsSummary": 0, + + # FloodingRainSection + "sectionParts": 0, + "_forecastSubsection": 0, + "_latestForecastSummary": 0, + "_peakRain": 0, + "_rainRange": 0, + + # TornadoSection + "sectionParts": 0, + "_forecastSubsection": 0, + "_latestForecastSummary": 0, + + # SectionCommonStats + "_initializeAdvisories": 0, + "_updateThreatStats": 0, + "_calculateHourOffset": 0, + + # Common to all SectionStats classes + "_setStats": 1, + + # WindsSectionStats + "_updateStatsForPwsXXint": 0, + "_updateStatsForPwsTXX": 1, + "windSpdProb_thresholds": 0, + "_updateWindTimeInfo": 0, + "_computeWindOnsetAndEnd": 1, + "_createWindow": 1, + + # XMLFormatter + "execute": 0, + "xmlKeys": 0, + "sectionKeys": 0, + "getSectionKey": 0, + "dictionary": 0, + "list": 0, + + # LegacyFormatter + "_processProductParts": 0, + "_noOpParts": 0, + "processWmoHeader": 0, + "processProductHeader": 0, + "processLocationsAffected": 0, + "processSubsection": 0, + "processThreatStatements": 0, + "processImpactsStatements": 0, + "processInfoSection": 0, + "_buildInfoSection": 0, + "formatIssueTime": 0, + "processSummaryHeadlines": 0, + "processSubParts": 0, + } +# Definition["debug"] = 1 # turn on ALL debug messages +# Definition["debug"] = 0 # turn off ALL debug messages + def __init__(self): HLSTCV_Common.TextProduct.__init__(self) @@ -65,6 +217,10 @@ class TextProduct(HLSTCV_Common.TextProduct): ### analysis related methods ############################################################### + ############################################################### + ### TCV Product and Segment Parts Definition + ############################################################### + ############################################################### ### Hazards and Additional Hazards ### allowedHazards is used for VTEC records and summary @@ -72,10 +228,6 @@ class TextProduct(HLSTCV_Common.TextProduct): ### allowedHeadlines are additional hazards reported in ### certain sections ############################################################### - - ############################################################### - ### TCV Product and Segment Parts Definition - ############################################################### ############################################################### # CODE @@ -88,7 +240,8 @@ class TextProduct(HLSTCV_Common.TextProduct): ############################################################### ############################################################### - ### Product Dictionary methods + ### Product Dictionary methods for creating the dictionary, + ### formatting the dictionary and populating product parts ############################################################### ############################################################### @@ -179,6 +332,14 @@ class TextProduct(HLSTCV_Common.TextProduct): return analysisList + def _extraRainfallAnalysisList(self): + # The grids for the Surge Section will be intersected with a special edit area + analysisList = [ + ("QPF", self.accumSum), + ] + + return analysisList + ############################################################### ### TCV Product and Segment Parts Definition @@ -234,6 +395,10 @@ class TextProduct(HLSTCV_Common.TextProduct): ### High level flow of formatter def generateForecast(self, argDict): + import pprint + pp = pprint.PrettyPrinter() + self.debug_print("argDict = %s" % (pp.pformat(argDict)), 1) + # Generate Text Phrases for a list of edit areas error = self._initializeVariables(argDict) @@ -241,7 +406,7 @@ class TextProduct(HLSTCV_Common.TextProduct): return error self._segmentList = self._determineSegments() - print "Segment Information: ", self._segmentList, "\n\n" + self.debug_print("Segment Information: %s" % (repr(self._segmentList)), 1) if len(self._segmentList) == 0: return "NO HAZARDS TO REPORT" @@ -256,7 +421,7 @@ class TextProduct(HLSTCV_Common.TextProduct): productOutput = self._formatProductDictionary(productDict) self._archiveCurrentAdvisory() - + return productOutput def _initializeVariables(self, argDict): @@ -332,7 +497,7 @@ class TextProduct(HLSTCV_Common.TextProduct): def _setup_segment(self, segmentDict, productSegmentGroup, productSegment): segment, vtecRecords = productSegment - print 'setup_segment productSegment', productSegment + self.debug_print('setup_segment productSegment %s' % (repr(productSegment)), 1) # NOTE -- using getVtecRecords to change to milliseconds self._segmentVtecRecords = self.getVtecRecords(segment) @@ -347,13 +512,19 @@ class TextProduct(HLSTCV_Common.TextProduct): self._productTimeZones.append(tz) self._purgeHours = self._purgeTime self._expireTime = self._tpc.getExpireTime( - self._issueTime, self._purgeHours, self._segmentVtecRecords) + self._issueTime_ms, self._purgeHours, self._segmentVtecRecords) segmentDict['expireTime'] = self._convertToISO(self._expireTime) # CAP Specific Fields segmentDict['status'] = 'Actual' + + # Don't show UPG headlines + nonUPGrecords = [] + for record in self._segmentVtecRecords: + if record['act'] != "UPG": + nonUPGrecords.append(record) self._summaryHeadlines_value, self._headlines = self._tpc.getHeadlinesAndSections( - self._segmentVtecRecords, self._productID, self._issueTime_secs) + nonUPGrecords, self._productID, self._issueTime_secs) def _ugcHeader(self, segmentDict, productSegmentGroup, productSegment): segmentDict['ugcCodes'] = self._formatUGC_entries() @@ -364,10 +535,15 @@ class TextProduct(HLSTCV_Common.TextProduct): segment, vtecRecords = productSegment records = [] for vtecRecord in vtecRecords: - print "SARAH: vtecRecord dict:\n%s" % (vtecRecord) vstr = None vstr = vtecRecord["vtecstr"] + self.debug_print("vtecRecord = %s" % (repr(vtecRecord)), 1) + + # Post-process some VTEC codes which should not exist + vstr = vstr.replace(".EXT.", ".CON.") + vstr = vstr.replace(".EXB.", ".EXA.") + if vtecRecord["phen"] == "SS": # SARAH: Temporary? Change the vtec mode for SS hazards to be experimental vstr = vstr[0] + 'X' + vstr[2:] @@ -489,7 +665,6 @@ class TextProduct(HLSTCV_Common.TextProduct): removedParts.append(part) for part in removedParts: - print "SARAH: Removing part = %s" % (part) partsList.remove(part) def _noOpParts(self): @@ -546,35 +721,43 @@ class TextProduct(HLSTCV_Common.TextProduct): return productDict def _wrapUpProductDict(self, productDict): - productDict['sentTimeZ'] = self._convertToISO(self._issueTime) - productDict['sentTimeZ_datetime'] = self._convertToDatetime(self._issueTime) - productDict['sentTimeLocal'] = self._convertToISO(self._issueTime, local=True) + productDict['sentTimeZ'] = self._convertToISO(self._issueTime_ms) + productDict['sentTimeZ_datetime'] = self._convertToDatetime(self._issueTime_ms) + productDict['sentTimeLocal'] = self._convertToISO(self._issueTime_ms, local=True) productDict['timeZones'] = self._productTimeZones return productDict ############################################################### ### Area, Zone and Segment related methods - def _groupSegments(self, segments): + def _groupSegments(self, segmentsWithHazards): ''' Group the segments into the products return a list of productSegmentGroup dictionaries ''' segment_vtecRecords_tuples = [] - for segment in segments: - vtecRecords = self.getVtecRecords(segment) - segment_vtecRecords_tuples.append((segment, vtecRecords)) + # We need to preserve the ordering of the zones based off the zone combiner ordering + sortedAreas = sorted(self._allAreas(), + key=lambda x: segmentsWithHazards.index(x) if x in segmentsWithHazards else 9999) + for segment in sortedAreas: self._initializeSegmentZoneData(segment) + # We need stats for all zones to be saved in the advisory, + # regardless of whether or not it has a hazard in it windStats, stormSurgeStats, floodingRainStats, tornadoStats = \ self._getStats(self._argDict, segment, self._editAreaDict, self._timeRangeList) - self._windSection[segment] = WindSection(self, segment, windStats) - self._stormSurgeSection[segment] = StormSurgeSection(self, segment, stormSurgeStats) - self._floodingRainSection[segment] = FloodingRainSection(self, segment, floodingRainStats) - self._tornadoSection[segment] = TornadoSection(self, segment, tornadoStats) + # Only show zones with hazards in the output + if segment in segmentsWithHazards: + vtecRecords = self.getVtecRecords(segment) + segment_vtecRecords_tuples.append((segment, vtecRecords)) + + self._windSection[segment] = WindSection(self, segment, windStats) + self._stormSurgeSection[segment] = StormSurgeSection(self, segment, stormSurgeStats) + self._floodingRainSection[segment] = FloodingRainSection(self, segment, floodingRainStats) + self._tornadoSection[segment] = TornadoSection(self, segment, tornadoStats) productSegmentGroup = { 'productID' : 'TCV', @@ -587,6 +770,104 @@ class TextProduct(HLSTCV_Common.TextProduct): } return productSegmentGroup + + def _makeSegmentEditAreas(self, argDict): + areasList = self._allAreas() + #self.debug_print("areaList = %s" % (repr(areasList)), 1) + editAreas = [] + self._editAreaDict = {} + for area in areasList: + self._editAreaDict[area] = area + editAreas.append((area, area)) + return editAreas + + def _determineSegments(self): + # Get the segments based on hazards "overlaid" with combinations file + + # Get the segments resulting from Hazards + #self.debug_print("\nRaw Analyzed %s" % (repr(self._hazardsTable.rawAnalyzedTable())), 1) + hazSegments = self.organizeHazards(self._hazardsTable.rawAnalyzedTable()) + #self.debug_print("\nSegments from HazardsTable organizeHazards %s" % (repr(hazSegments)), 1) + + # Get the forecaster entered combinations + accessor = ModuleAccessor.ModuleAccessor() +# self.debug_print("self._defaultEditAreas = %s" % (repr(self._defaultEditAreas)), 1) + combos = accessor.variable(self._defaultEditAreas, "Combinations") + if combos is None: + LogStream.logVerbose("COMBINATION FILE NOT FOUND: " + self._defaultEditAreas) + return [], None + #self.debug_print("\nSegments from Zone Combiner = %s" % (repr(combos)), 1) + # "Overlay" the forecaster-entered combinations onto the segments + segmentList = self._refineSegments(hazSegments, combos) + #self.debug_print("\nNew segments = %s" % (repr(segmentList)), 1) + + # Instead of a segment being a group of zones, it will be just a single zone. + # So collapse this list of lists down to a list of zones (aka. segments) + segments = [] + for segment in segmentList: + segments += segment + + return segments + + def _refineSegments(self, hazSegments, combos): + """Break down each segment further according to combos given. + Make sure the resulting segments follow the ordering of the combos. + """ + if combos == []: + return hazSegments + newSegments = [] # list of lists + newAreas = [] + for combo, label in combos: + # Each combination will be tested to see if it can stay intact + # i.e. if all areas in the combo are in the same segment + # else split it into like segments + # + # segmentMapping is a list where each entry is + # the hazSegment in which the corresponding combo area appears. + # (We need to define self._segmentList for the mapping function + # to use) + self._segmentList = hazSegments + #self.debug_print("self._segmentList = %s" % (repr(self._segmentList)), 1) + segmentMapping = map(self._findSegment, combo) + #self.debug_print(" segmentMapping = %s" % (repr(segmentMapping)), 1) + + # segmentDict keys will be the hazSegments and + # we will gather all the areas of the combos that appear + # in each of these hazSegments + segmentDict = {} + keyList = [] + for areaName in combo: + #self.debug_print(" Adding %s" % (areaName), 1) + key = tuple(segmentMapping[combo.index(areaName)]) + if key == (): # If no hazard for area, do not include + continue + if key not in keyList: + keyList.append(key) + segmentDict.setdefault(key,[]).append(areaName) + #self.debug_print(" segmentDict = %s" % (repr(segmentDict)), 1) + + # Keep track of the areas that we are including + for key in keyList: + segAreas = segmentDict[key] + newAreas = newAreas + segAreas + newSegments.append(segAreas) + #self.debug_print(" newSegments = %s" % (repr(newSegments)), 1) + # Now add in the hazAreas that have not been accounted for + # in the combinations + for hazSegment in hazSegments: + newSeg = [] + for hazArea in hazSegment: + if hazArea not in newAreas: + newSeg.append(hazArea) + if newSeg != []: + newSegments.append(newSeg) + return newSegments + + def _findSegment(self, areaName): + for segment in self._segmentList: + if areaName in segment: + return segment + return [] def _formatUGC_entries(self): ugcCodeList = [] @@ -657,11 +938,16 @@ class TextProduct(HLSTCV_Common.TextProduct): self._intersectSampler = self.getSampler(argDict, (self._intersectAnalysisList(), self._timeRangeList, intersectAreas)) - + + + # Make a sample period for the previous rainfall + self._previousRainfallTR = [(self._extraSampleTimeRange, "PrevRainfall")] + self._extraRainfallSampler = self.getSampler(argDict, + (self._extraRainfallAnalysisList(), self._previousRainfallTR, + editAreas)) + def _getStats(self, argDict, segment, editAreaDict, timeRangeList): # Get statistics for this segment - print "SARAH: issue time seconds = %s" % (self._issueTime_secs) - print "SARAH: GMT issue time = %s" % (repr(time.gmtime(self._issueTime_secs))) editArea = editAreaDict[segment] statList = self.getStatList(self._sampler, @@ -669,8 +955,25 @@ class TextProduct(HLSTCV_Common.TextProduct): timeRangeList, editArea) + import pprint + pp = pprint.PrettyPrinter() + self.debug_print("*"*80) + for index in range(len(timeRangeList)): + self.debug_print("editArea =" + editArea, 1) + self.debug_print("timeRange = %s" % (pp.pformat(timeRangeList[index])), 1) + self.debug_print("statList = %s" % (pp.pformat(statList[index])), 1) + self.debug_print("-"*40, 1) + + # These stats are for handling the extra rainfall + extraRainfallStatList = self.getStatList(self._extraRainfallSampler, + self._extraRainfallAnalysisList(), + self._previousRainfallTR, + editArea) + windStats = WindSectionStats(self, segment, statList, timeRangeList) - floodingRainStats = FloodingRainSectionStats(self, segment, statList, timeRangeList) + floodingRainStats = FloodingRainSectionStats(self, segment, + statList, timeRangeList, + extraRainfallStatList, self._previousRainfallTR) tornadoStats = TornadoSectionStats(self, segment, statList, timeRangeList) # The surge section needs sampling done with an intersected edit area @@ -681,95 +984,11 @@ class TextProduct(HLSTCV_Common.TextProduct): intersectEditArea) stormSurgeStats = StormSurgeSectionStats(self, segment, intersectStatList, timeRangeList) - + return (windStats, stormSurgeStats, floodingRainStats, tornadoStats) ############################################################### ### Time related methods - - def _formatPeriod(self, period, wholePeriod=False, shiftToLocal=True, useEndTime=False, - resolution=3): - # Format period (a timeRange) resulting in - # DAY + MORNING / AFTERNOON / EVENING / OVERNIGHT. - # If wholePeriod, format FROM ... TO... - - print "\nMATT Format period wholePeriod = %s, period = %s, useEndTime =%s" % (str(wholePeriod), str(period), str(useEndTime)) - if period is None: return "" - if useEndTime: - startTime = period.endTime() - else: - startTime = period.startTime() - result = self._getTimeDesc(startTime, resolution, shiftToLocal) - print "MATT result = '%s'" % (result) - if wholePeriod: - endResult = self._getTimeDesc(period.endTime(), resolution, shiftToLocal) - print "MATT endResult = '%s'" % (endResult) - if result != endResult: - result=result + " TO "+ endResult - return result - - def _getTimeDesc(self, startTime, resolution=3, shiftToLocal=True): - # Create phrase such as Tuesday morning - # Handle today/tonight and "this" morning/afternoon/etc.. - # - print "\n\n**************Formatting Period for GMT starttime ", startTime - labels = self.Labels()["SimpleWorded"] - currentTime = self._timeRange.startTime() - print " currentTime", currentTime - if shiftToLocal: - currentLocalTime, shift = self.determineTimeShift() - startTime = startTime + shift - currentTime = currentTime + shift - print " shift, shifted start, current", shift/3600, startTime, currentTime - hour = startTime.hour - prevDay = False - prevDay, partOfDay = self._getPartOfDay(hour, resolution) -# if prevDay: -# startTime = startTime - 24*3600 - todayFlag = currentTime.day == startTime.day - if todayFlag: - if partOfDay.upper().find("MIDNIGHT")>0: todayWord = "tonight" - else: todayWord = "THIS" - weekday = todayWord - else: - weekday = labels["Weekday"][startTime.weekday()] - if partOfDay.find("") >= 0: - result = partOfDay.replace('', weekday) - else: - result = weekday + " " + partOfDay - print "Result", result - return result - - def _getPartOfDay(self, hour, resolution): - prevDay = False - if resolution == 3: - if hour < 3: - prevDay = True - partOfDay = "early morning" -# partOfDay = "AFTER MIDNIGHT" - elif hour < 6: - partOfDay = "early morning" - elif hour < 9: - partOfDay = "morning" - elif hour < 12: - partOfDay = "late morning" - elif hour < 15: - partOfDay = "early afternoon" - elif hour < 18: - partOfDay = "late afternoon" - elif hour < 21: - partOfDay = "early evening" - else: - partOfDay = "late evening" - else: - if hour < 6: - prevDay = True -# partOfDay = "AFTER MIDNIGHT" - partOfDay = "early morning" - elif hour < 12: partOfDay = "morning" - elif hour < 18: partOfDay = "afternoon" - else: partOfDay = "evening" - return prevDay, partOfDay def _convertToISO(self, time_ms, local=None): import datetime @@ -826,7 +1045,6 @@ class TextProduct(HLSTCV_Common.TextProduct): allCAN = True for vtecRecord in self._getAllVTECRecords(): action = vtecRecord['act'] - #print "vtecRecord", vtecRecord if action != "CAN": allCAN = False break @@ -844,6 +1062,23 @@ class TextProduct(HLSTCV_Common.TextProduct): def _saveAdvisory(self, advisoryName, advisoryDict): self._synchronizeAdvisories() fileName = self._getAdvisoryFilename(advisoryName) + +# print "*"*80 +# print "advisoryDict = ", advisoryDict +# print "-"*80 + "\n" +# print type(advisoryDict["HazardsForHLS"]) + newList = [] + for item in advisoryDict["HazardsForHLS"]: +# print item, "\n" + + # Now handle the action code we should not have + if item["act"] == "EXT": + item["act"] = "CON" + elif item["act"] == "EXB": + item["act"] = "EXA" + + newList.append(item) + advisoryDict["HazardsForHLS"] = newList try: JsonSupport.saveToJson(LocalizationType.CAVE_STATIC, @@ -851,25 +1086,27 @@ class TextProduct(HLSTCV_Common.TextProduct): fileName, advisoryDict) - print "SARAH: Wrote file contents for", fileName + self.debug_print("SARAH: Wrote file contents for: %s" % (fileName), 1) self._synchronizeAdvisories() except Exception, e: - print "SARAH Save Exception for", fileName, ":", e + self.debug_print("SARAH Save Exception for %s : %s" % (fileName, e), 1) def _getHazardsForHLS(self): hazardTable = self._argDict["hazards"] hazSegments = self.organizeHazards(hazardTable.rawAnalyzedTable()) - print "\nSegments from HazardsTable organizeHazards", hazSegments + self.debug_print("Segments from HazardsTable organizeHazards %s" % + (repr(hazSegments)), 1) combos = [([self._allAreas()], "AllAreas")] - print "\nSegments from Zone Combiner", combos + self.debug_print("Segments from Zone Combiner %s" % (repr(combos)), 1) # "Overlay" the forecaster-entered combinations onto the segments segmentList = self._refineSegments(hazSegments, combos) - print "\nSegmentList from refineSegments =", segmentList + self.debug_print("SegmentList from refineSegments = %s" % + (repr(segmentList)), 1) allHazards = [] for segment in segmentList: @@ -1035,8 +1272,10 @@ class SectionCommon(): threatKey = elementName + "Threat" forecastKey = elementName + "Forecast" - print "SARAH: getThreatTrendValue _currentAdvisory =\n%s" % (repr(self._stats._currentAdvisory)) - print "SARAH: getThreatTrendValue _previousAdvisory =\n%s" % (repr(self._stats._previousAdvisory)) + self._textProduct.debug_print("SARAH: THREAT DEBUG for %s" % elementName, 1) + + self._textProduct.debug_print("SARAH: getThreatTrendValue _currentAdvisory =\n%s" % (repr(self._stats._currentAdvisory)), 1) + self._textProduct.debug_print("SARAH: getThreatTrendValue _previousAdvisory =\n%s" % (repr(self._stats._previousAdvisory)), 1) if (self._stats._currentAdvisory is None) or (self._stats._previousAdvisory is None): # Only compute a threat trend if we have 2 or more advisories @@ -1046,31 +1285,38 @@ class SectionCommon(): previousThreat = self._stats._previousAdvisory[threatKey] shorterTermTrendDifference = self._threatDifference(currentThreat, previousThreat) - print "SARAH: shorterTermTrendDifference = %s" % (shorterTermTrendDifference) + self._textProduct.debug_print("SARAH: currentThreat = %s" % currentThreat, 1) + self._textProduct.debug_print("SARAH: previousThreat = %s" % previousThreat, 1) + self._textProduct.debug_print("SARAH: shorterTermTrendDifference = %s" % shorterTermTrendDifference, 1) previousPreviousThreat = None longerTermTrendDifference = None if self._stats._previousPreviousAdvisory is not None: + self._textProduct.debug_print("SARAH: _previousPreviousAdvisory is not None", 1) previousPreviousThreat = self._stats._previousPreviousAdvisory[threatKey] + self._textProduct.debug_print("SARAH: previousPreviousThreat = %s" % previousPreviousThreat, 1) longerTermTrendDifference = self._threatDifference(currentThreat, previousPreviousThreat) + self._textProduct.debug_print("SARAH: longerTermTrendDifference = %s" % longerTermTrendDifference, 1) threatTrendValue = "NEARLY STEADY" + self._textProduct.debug_print("magnitudeIncreaseThreshold = %s forecastKey = '%s'" % (magnitudeIncreaseThreshold, forecastKey), 1) if self._isThreatDecreasing(shorterTermTrendDifference, longerTermTrendDifference): + self._textProduct.debug_print("SARAH: threat is decreasing", 1) threatTrendValue = "DECREASING" elif self._isThreatIncreasing(shorterTermTrendDifference, longerTermTrendDifference): + self._textProduct.debug_print("SARAH: threat is increasing", 1) threatTrendValue = "INCREASING" elif currentThreat == "Extreme" and \ - self._advisoriesHaveValidKey(forecastKey) and \ - self._isMagnitudeIncreasing(self._stats._currentAdvisory[forecastKey], - self._stats._previousAdvisory[forecastKey], - self._stats._previousPreviousAdvisory[forecastKey], - magnitudeIncreaseThreshold): + self._isMagnitudeIncreasing(forecastKey, magnitudeIncreaseThreshold): + self._textProduct.debug_print("Increasing based on magnitude", 1) threatTrendValue = "INCREASING" return threatTrendValue def _threatDifference(self, threat1, threat2): threatLevels = self._textProduct.threatKeyOrder() + self._textProduct.debug_print("SARAH: threat1 index = %s" % threatLevels.index(threat1), 1) + self._textProduct.debug_print("SARAH: threat2 index = %s" % threatLevels.index(threat2), 1) return threatLevels.index(threat1) - threatLevels.index(threat2) def _isThreatDecreasing(self, shorterTermTrendDifference, longerTermTrendDifference): @@ -1078,11 +1324,14 @@ class SectionCommon(): if (shorterTermTrendDifference < 0 and \ longerTermTrendDifference is not None and \ longerTermTrendDifference < 0): + self._textProduct.debug_print("SARAH: the current threat is at least 1 category lower than both previous advisories", 1) return True #Or if the current threat decreased by more than 1 category elif shorterTermTrendDifference < -1: + self._textProduct.debug_print("SARAH: the current threat decreased by more than 1 category", 1) return True else: + self._textProduct.debug_print("SARAH: the current threat is not decreasing", 1) return False def _isThreatIncreasing(self, shorterTermTrendDifference, longerTermTrendDifference): @@ -1090,36 +1339,61 @@ class SectionCommon(): if (shorterTermTrendDifference > 0 and \ longerTermTrendDifference is not None and \ longerTermTrendDifference > 0): + self._textProduct.debug_print("SARAH: the current threat is at least 1 category higher than both previous advisories", 1) return True #Or if the current threat increased by more than 1 category elif shorterTermTrendDifference > 1: + self._textProduct.debug_print("SARAH: the current threat increased by more than 1 category", 1) return True else: + self._textProduct.debug_print("SARAH: the current threat is not increasing", 1) return False - def _advisoriesHaveValidKey(self, key): - return self._advisoryHasValidKey(self._stats._currentAdvisory, key) and \ - self._advisoryHasValidKey(self._stats._previousAdvisory, key) and \ - self._advisoryHasValidKey(self._stats._previousPreviousAdvisory, key) - def _advisoryHasValidKey(self, advisory, key): return (advisory is not None) and \ (advisory.has_key(key)) and \ (advisory[key] is not None) - def _isMagnitudeIncreasing(self, currentValue, previousValue, previousPreviousValue, threshold): - if (currentValue > previousValue and currentValue > previousPreviousValue) or \ - (currentValue - previousValue) >= threshold: - return True + def _isMagnitudeIncreasing(self, forecastKey, threshold): +# currentValue, previousValue, previousPreviousValue + self._textProduct.debug_print("SARAH: _isMagnitudeIncreasing", 1) + self._textProduct.debug_print("SARAH: forecastKey = %s" % forecastKey, 1) + self._textProduct.debug_print("SARAH: threshold = %s" % threshold, 1) + + if self._advisoryHasValidKey(self._stats._currentAdvisory, forecastKey) and \ + self._advisoryHasValidKey(self._stats._previousAdvisory, forecastKey): + currentValue = self._stats._currentAdvisory[forecastKey] + previousValue = self._stats._previousAdvisory[forecastKey] + self._textProduct.debug_print("SARAH: currentValue = %s" % currentValue, 1) + self._textProduct.debug_print("SARAH: previousValue = %s" % previousValue, 1) + + if (currentValue - previousValue) >= threshold: + self._textProduct.debug_print("SARAH: the current magnitude has increased by more than the threshold since the last advisory", 1) + return True + elif self._advisoryHasValidKey(self._stats._previousPreviousAdvisory, forecastKey): + previousPreviousValue = self._stats._previousPreviousAdvisory[forecastKey] + self._textProduct.debug_print("SARAH: previousPreviousValue = %s" % previousPreviousValue, 1) + + if (currentValue - previousPreviousValue) >= threshold: + self._textProduct.debug_print("SARAH: the current magnitude has increased by more than the threshold since the previous previous advisory", 1) + return True + else: + self._textProduct.debug_print("SARAH: the current magnitude does not meet the requirements to be considered increasing", 1) + return False + else: + self._textProduct.debug_print("SARAH: the current magnitude did not increase past threshold and could not look at the previous previous advisory", 1) + return False else: + self._textProduct.debug_print("SARAH: the current advisory and/or previous advisory did not have key: %s" % forecastKey, 1) return False def _calculateThreatStatementTr(self, onsetHour, endHour, threatTrendValue): tr = None - print "SARAH: onset hour = %s" % (onsetHour) - print "SARAH: end hour = %s" % (endHour) - print "SARAH: threatTrendValue = %s" % (threatTrendValue) + self._textProduct.debug_print("SARAH: onset hour = %s" % (onsetHour), 1) + self._textProduct.debug_print("SARAH: end hour = %s" % (endHour), 1) + self._textProduct.debug_print("SHANNON: threatTrendValue = %s" % + (threatTrendValue), 1) if (onsetHour is not None) and \ (endHour is not None): @@ -1130,18 +1404,19 @@ class SectionCommon(): tr = "complete preparations" elif onsetHour <= 6 and endHour > 0: tr = "hunker down" - elif (threatTrendValue is not None) and (threatTrendValue.upper() == "DECREASING"): - tr = "recovery" - else: - tr = "nothing to see here" + elif (threatTrendValue is not None) and (threatTrendValue.upper() in ["DECREASING", "NEARLY STEADY"]): + tr = "recovery" + else: + tr = "nothing to see here" return tr def _setThreatStatementsProductParts(self, segmentDict, productSegment, tr): -# print "MATT: tr = %s self._stats.maxThreat = %s" % (repr(tr), -# repr(self._stats.maxThreat)) - if tr is not None and self._stats._maxThreat is not None: + self._textProduct.debug_print("SHANNON: tr = %s %s" % + (repr(tr), self._sectionHeaderName), 1) +# if tr is not None and self._stats._maxThreat is not None: + if tr is not None: (planning, action, preparation) = self._getThreatStatements(productSegment, self._sectionHeaderName, self._stats._maxThreat, @@ -1149,10 +1424,28 @@ class SectionCommon(): self._setProductPartValue(segmentDict, 'threatStatements', [planning, action, preparation]) + else: + self._textProduct.debug_print("SHANNON messed up", 1) + return def _getThreatStatements(self, productSegment, sectionName, maxThreat, tr): - import TCVDictionary - threatStatements = TCVDictionary.ThreatStatements +# import TCVDictionary +# threatStatements = TCVDictionary.ThreatStatements + + with open("/awips2/cave/etc/gfe/userPython/utilities/TCVDictionary.py", 'r') as pythonFile: + fileContents = pythonFile.read() + exec(fileContents) + + # ThreatStatements comes from TCVDictionary.py when it is exec'ed + threatStatements = ThreatStatements + + self._textProduct.debug_print(40*"-", 1) + self._textProduct.debug_print("sectionName = %s, maxThreat = %s, tr = %s" % + (sectionName, maxThreat, repr(tr)), 1) + +# if maxThreat is None: +# maxThreat = "None" + statements = threatStatements[sectionName][maxThreat][tr] planning = statements["planning"] preparation = statements["preparation"] @@ -1180,12 +1473,15 @@ class SectionCommon(): return "Potential Impacts: " + impactLevel def _potentialImpactsStatements(self, segmentDict, productSegmentGroup, productSegment): + self._textProduct.debug_print("SHANNON: segment = %s, elementName = %s, maxThreat = %s" % + (productSegment[0], self._sectionHeaderName, self._stats._maxThreat), 1) if self._stats._maxThreat is not None: statements = self._getPotentialImpactsStatements(productSegment, self._sectionHeaderName, self._stats._maxThreat) self._setProductPartValue(segmentDict, 'potentialImpactsStatements', statements) def _getPotentialImpactsStatements(self, productSegment, elementName, maxThreat): import TCVDictionary + potentialImpactStatements = TCVDictionary.PotentialImpactStatements statements = potentialImpactStatements[elementName][maxThreat] @@ -1193,6 +1489,10 @@ class SectionCommon(): tcv_AreaDictionary = TCVAreaDictionary.TCV_AreaDictionary segment, vtecRecords = productSegment + + self._textProduct.debug_print("SHANNON: zone number = %s, elementName = %s, maxThreat = %s" % + (segment, elementName, maxThreat), 1) + if segment in tcv_AreaDictionary: potentialImpactStatements = tcv_AreaDictionary[segment]["potentialImpactsStatements"] @@ -1273,9 +1573,9 @@ class WindSection(SectionCommon): for i in range(numRecords): vtecRecord = vtecRecords[i] - if (vtecRecord["phensig"] in ["HU.A", "HU.W", "TR.A", "TR.W"] and \ - vtecRecord["act"] not in self._textProduct._ignoreActions()) or \ - self._stats._windowTS is not None: + if (vtecRecord["phensig"] in ["HU.A", "HU.W", "TR.A", "TR.W"] or \ + self._stats._windowTS is not None) and \ + vtecRecord["act"] != "CAN": forecastText += "Tropical storm force winds remain possible" possibleHazardsFound = True break @@ -1328,7 +1628,7 @@ class WindSection(SectionCommon): def _threatTrend(self, segmentDict, productSegmentGroup, productSegment): self._threatTrendValue = \ self._getThreatTrendValue("Wind", - magnitudeIncreaseThreshold=self._textProduct.mphToKt(20)) + magnitudeIncreaseThreshold=self._textProduct.mphToKt(15)) if self._threatTrendValue is not None: # Convert the threat trend to a sentence @@ -1339,11 +1639,10 @@ class WindSection(SectionCommon): threatTrendSentence) def _threatStatements(self, segmentDict, productSegmentGroup, productSegment): - print "SARAH: Wind Threat Statements" windTr = self._calculateThreatStatementTr(self._stats._onset34Hour, self._stats._end34Hour, self._threatTrendValue) -# print "MATT: in _threatStatements tr = %s" % (repr(windTr)) +# self._textProduct.debug_print("MATT: in _threatStatements tr = %s" % (repr(windTr)) if not hasattr(self._textProduct, "_windThreatStatementsTr"): self._textProduct._windThreatStatementsTr = dict() @@ -1371,12 +1670,7 @@ class WindSection(SectionCommon): windTr = self._calculateThreatStatementTr(self._stats._onset34Hour, self._stats._end34Hour, self._threatTrendValue) - - # If we are after the hunker down phase - if windTr in ["recovery", "nothing to see here"]: - # Do not include this section at all - return - + subsectionDict = collections.OrderedDict() self._potentialImpactsSummary(subsectionDict, productSegmentGroup, productSegment) self._potentialImpactsStatements(subsectionDict, productSegmentGroup, productSegment) @@ -1443,7 +1737,8 @@ class StormSurgeSection(SectionCommon): if not self._textProduct._PopulateSurge: self._setProductPartValue(segmentDict, 'latestForecastSummary', "Latest local forecast: Not available at this time. To be updated shortly.") - elif "None" not in self._stats._windowSurge or \ + + elif "None" in self._stats._windowSurge or \ self._stats._inundationMax is None or \ self._stats._inundationMax < 1: self._setProductPartValue(segmentDict, 'latestForecastSummary', @@ -1457,10 +1752,10 @@ class StormSurgeSection(SectionCommon): elif 4 <= max and max < 12: summary += "Life-threatening" else: - summary += "Historic" + summary += "Life-threatening and historic" self._setProductPartValue(segmentDict, 'latestForecastSummary', - summary + " storm surge flooding") + summary + " storm surge possible") def _peakSurge(self, segmentDict, productSegmentGroup, productSegment): if self._stats._inundationMax is not None and self._stats._inundationMax >= 1: @@ -1486,8 +1781,12 @@ class StormSurgeSection(SectionCommon): else: words = "" - self._setProductPartValue(segmentDict, 'peakSurge', - "Peak Storm Surge Inundation: " + words + " somewhere within surge prone areas") + if len(words) > 0: + self._setProductPartValue(segmentDict, 'peakSurge', + "Peak Storm Surge Inundation: The potential for " + words + " somewhere within surge prone areas") + else: + self._setProductPartValue(segmentDict, 'peakSurge', + "Peak Storm Surge Inundation: The potential for little to no storm surge inundation") def _surgeWindow(self, segmentDict, productSegmentGroup, productSegment): if "None" not in self._stats._windowSurge: @@ -1521,7 +1820,7 @@ class StormSurgeSection(SectionCommon): threatTrendSentence) def _threatStatements(self, segmentDict, productSegmentGroup, productSegment): - print "SARAH: Surge Threat Statements" + self._textProduct.debug_print("SARAH: Surge Threat Statements", 1) surgeTr = self._calculateThreatStatementTr(self._stats._onsetSurgeHour, self._stats._endSurgeHour, self._threatTrendValue) @@ -1574,7 +1873,7 @@ class FloodingRainSection(SectionCommon): self._setProductPartValue(segmentDict, 'forecastSubsection', subsectionDict) def _latestForecastSummary(self, segmentDict, productSegmentGroup, productSegment): - summary = "No flood watch is in effect" + summary = "" # was "No flood watch is in effect" segment, vtecRecords = productSegment headlines, _ = self._textProduct._getAdditionalHazards() @@ -1599,6 +1898,10 @@ class FloodingRainSection(SectionCommon): def _peakRain(self, segmentDict, productSegmentGroup, productSegment): if self._stats._sumAccum is not None: words = self._rainRange(int(math.ceil(self._stats._sumAccum))) + + # If we have previous rainfall + if self._stats._prevAccum not in [0.0, None]: + words = "Additional " + words self._setProductPartValue(segmentDict, 'peakRain', "Peak Rainfall Amounts: " + words) def _rainRange(self, sumAccum): @@ -1686,7 +1989,7 @@ class TornadoSection(SectionCommon): self._setProductPartValue(segmentDict, 'forecastSubsection', subsectionDict) def _latestForecastSummary(self, segmentDict, productSegmentGroup, productSegment): - summary = "No Tornado Watch is in effect" + summary = "There is no Tornado Watch in effect" segment, vtecRecords = productSegment headlines, _ = self._textProduct._getAdditionalHazards() @@ -1754,26 +2057,28 @@ class SectionCommonStats(): self._currentAdvisory = self._textProduct._currentAdvisory['ZoneData'][self._segment] self._previousAdvisory = None -# print "MATT textProduct._previousAdvisory = '%s'" % (textProduct._previousAdvisory) +# self._textProduct.debug_print("MATT textProduct._previousAdvisory = '%s'" % (textProduct._previousAdvisory)) if self._textProduct._previousAdvisory is not None: - self._previousAdvisory = self._textProduct._previousAdvisory['ZoneData'][self._segment] + if self._textProduct._previousAdvisory['ZoneData'].has_key(self._segment): + self._previousAdvisory = self._textProduct._previousAdvisory['ZoneData'][self._segment] -# print "MATT textProduct._previousPreviousAdvisory = '%s'" % \ -# (textProduct._previousPreviousAdvisory) +# self._textProduct.debug_print("MATT textProduct._previousPreviousAdvisory = '%s'" % \ +# (textProduct._previousPreviousAdvisory)) self._previousPreviousAdvisory = None if self._textProduct._previousPreviousAdvisory is not None: self._previousPreviousAdvisory = self._textProduct._previousPreviousAdvisory['ZoneData'][self._segment] def _updateThreatStats(self, tr, statDict, threatGridName): - print "SARAH: updateThreatStats for %s" % (threatGridName) + self._textProduct.debug_print("statDict = '%s'" % (repr(statDict)), 1) threatLevel = self._textProduct.getStats(statDict, threatGridName) if threatLevel is not None: threatLevels = self._textProduct.threatKeyOrder() - print "SARAH: threatLevel = %s" % (threatLevel) - print "SARAH: maxThreat = %s" % (self._maxThreat) + self._textProduct.debug_print("SARAH: updateThreatStats for %s" % (threatGridName), 1) + self._textProduct.debug_print("SARAH: threatLevel = %s" % (threatLevel), 1) + self._textProduct.debug_print("SARAH: maxThreat = %s" % (self._maxThreat), 1) if self._maxThreat is None or \ threatLevels.index(threatLevel) > threatLevels.index(self._maxThreat): - print "SARAH: updating max threat to = %s" % (threatLevel) + self._textProduct.debug_print("SARAH: updating max threat to = %s" % (threatLevel), 1) self._maxThreat = threatLevel def _calculateHourOffset(self, targetTime): @@ -1784,7 +2089,6 @@ class SectionCommonStats(): return hour - class WindSectionStats(SectionCommonStats): def __init__(self, textProduct, segment, statList, timeRangeList): SectionCommonStats.__init__(self, textProduct, segment) @@ -1796,6 +2100,9 @@ class WindSectionStats(SectionCommonStats): self._end64Hour = None self._windowTS = None self._windowHU = None + + print "*"*90 + print "Setting wind stats for %s" % (segment) self._setStats(statList, timeRangeList) @@ -1804,8 +2111,12 @@ class WindSectionStats(SectionCommonStats): onsetHour = None class PwsTXXStats(): - onsetHour = None - endHour = None + firstRun = True + dropFirstGridType = None + droppedFirstGrid = False + periodWithFirstCorrectGrid = None + onsetTime = None + endTime = None class TimeInfo(): onsetHour = None @@ -1822,37 +2133,34 @@ class WindSectionStats(SectionCommonStats): pwsT64Stats = self.PwsTXXStats() wind34timeInfo = self.TimeInfo() wind64timeInfo = self.TimeInfo() - - events34 = self.EventsOccurring() - events64 = self.EventsOccurring() - - for period in range(len(statList)): - tr, _ = timeRangeList[period] - statDict = statList[period] - + + currentPeriod = None + for index in range(len(statList)): + tr, _ = timeRangeList[index] + statDict = statList[index] + + for periodIndex, periodTr in enumerate(self._textProduct._periodList): + if (periodIndex == 0) and (tr.startTime().unixTime() < periodTr.startTime().unixTime()): + # If the tr is before the first period, use the first period + currentPeriod = periodIndex + break + elif periodTr.contains(tr.startTime()): + currentPeriod = periodIndex + break + self._updateStatsForPwsXXint(tr, statDict, "pws34int", pws34intStats) self._updateStatsForPwsXXint(tr, statDict, "pws64int", pws64intStats) - self._updateStatsForPwsTXX(tr, statDict, "pwsD34", "pwsN34", pwsT34Stats, events34, period) - self._updateStatsForPwsTXX(tr, statDict, "pwsD64", "pwsN64", pwsT64Stats, events64, period) + self._updateStatsForPwsTXX(tr, statDict, "pwsD34", "pwsN34", pwsT34Stats, currentPeriod) + self._updateStatsForPwsTXX(tr, statDict, "pwsD64", "pwsN64", pwsT64Stats, currentPeriod) wind = self._textProduct._getStatValue(statDict, "Wind", "Max", self._textProduct.VECTOR()) if wind is not None: - if wind >= 34: - events34.windXXEvent = True - if wind >= 64: - events64.windXXEvent = True - else: - events64.windXXEvent = False - else: - events34.windXXEvent = False - events64.windXXEvent = False - if self._maxWind is None or wind >= self._maxWind: self._maxWind = wind - self._updateWindTimeInfo(tr, wind34timeInfo, speed=34) - self._updateWindTimeInfo(tr, wind64timeInfo, speed=64) + self._updateWindTimeInfo(tr, wind, wind34timeInfo, speed=34) + self._updateWindTimeInfo(tr, wind, wind64timeInfo, speed=64) windGust = self._textProduct._getStatValue(statDict, "WindGust", "Max") if windGust is not None: @@ -1866,30 +2174,20 @@ class WindSectionStats(SectionCommonStats): self._onset34Hour = onsetEndInfo.onsetHour self._end34Hour = onsetEndInfo.endHour - nonEnding34Event = False - if events34.pwsTXXEvent and (wind34timeInfo.endHour is None or events34.windXXEvent): - nonEnding34Event = True - - print "SARAH: Tropical Storm Window:" + self._textProduct.debug_print("SARAH: Tropical Storm Window:", 1) self._windowTS = self._createWindow("Tropical Storm", self._onset34Hour, - self._end34Hour, - nonEnding34Event) + self._end34Hour) #Hurricane onsetEndInfo = self._computeWindOnsetAndEnd(wind64timeInfo, pws64intStats, pwsT64Stats) self._onset64Hour = onsetEndInfo.onsetHour self._end64Hour = onsetEndInfo.endHour - nonEnding64Event = False - if events64.pwsTXXEvent and (wind64timeInfo.endHour is None or events64.windXXEvent): - nonEnding64Event = True - - print "SARAH: Hurricane Window:" + self._textProduct.debug_print("SARAH: Hurricane Window:", 1) self._windowHU = self._createWindow("Hurricane", self._onset64Hour, - self._end64Hour, - nonEnding64Event) + self._end64Hour) self._currentAdvisory["WindThreat"] = self._maxThreat self._currentAdvisory["WindForecast"] = self._maxWind @@ -1902,274 +2200,192 @@ class WindSectionStats(SectionCommonStats): pwsXXintStats.max = pwsXXint pwsXXintStats.onsetHour = self._calculateHourOffset(tr.startTime()) - print "SARAH: Window Debug: In _updateStatsForPwsXXint" - print "SARAH: Window Debug: pwsXXintStats gridName = %s" % (gridName) - print "SARAH: Window Debug: pwsXXintStats pwsXXint = %s" % (pwsXXint) - print "SARAH: Window Debug: pwsXXintStats tr = %s" % (repr(tr)) - print "SARAH: Window Debug: pwsXXintStats onsetHour = %s" % (pwsXXintStats.onsetHour) + self._textProduct.debug_print("SARAH: Window Debug: pwsXXintStats gridName = %s" % (gridName), 1) + self._textProduct.debug_print("SARAH: Window Debug: pwsXXintStats onsetHour = %s" % (pwsXXintStats.onsetHour), 1) - def _updateStatsForPwsTXX(self, tr, statDict, dayGridName, nightGridName, pwsTXXStats, events, period): - - # Convert this time to localtime - trStartLocalHour = time.localtime(tr.startTime().unixTime()).tm_hour - dayStartHour = self._textProduct.DAY() - nightStartHour = self._textProduct.NIGHT() - print "*" * 100 - print "MATT _updateStatsForPwsTXX = %s localStartHr = %d" % (repr(tr), - trStartLocalHour) - print "MATT dayStart = %s nightStart = %s" % (dayStartHour, - nightStartHour) - print "MATT _updateStatsForPwsTXX statDict\n%s" % (repr(statDict)) - + def _updateStatsForPwsTXX(self, tr, statDict, dayGridName, nightGridName, pwsTXXStats, period): pwsDXX = self._textProduct._getStatValue(statDict, dayGridName, "Max") pwsNXX = self._textProduct._getStatValue(statDict, nightGridName, "Max") + + if pwsTXXStats.firstRun: + self._textProduct.debug_print("SARAH: first run for _updateStatsForPwsTXX!", 1) + self._textProduct.debug_print("SARAH: grids: %s %s" % (dayGridName, nightGridName), 1) + pwsTXXStats.firstRun = False + localtime = time.localtime(self._textProduct._issueTime_secs) + self._textProduct.debug_print("SARAH: localtime = %s" % (localtime), 1) + + if localtime.tm_hour >= 15: # 3PM to midnight + self._textProduct.debug_print("SARAH: between 3PM and midnight!", 1) + pwsTXXStats.dropFirstGridType = "DAY" + self._textProduct.debug_print("SARAH: need to drop the day grid(s) if they come first", 1) + elif localtime.tm_hour >= 3 and localtime.tm_hour < 12: # 3AM to noon + self._textProduct.debug_print("SARAH: between 3AM and noon!", 1) + pwsTXXStats.dropFirstGridType = "NIGHT" + self._textProduct.debug_print("SARAH: need to drop the night grid(s) if they come first", 1) + else: + self._textProduct.debug_print("SARAH: not dropping any grids!", 1) + maxPws = None - print "MATT pwsDXX = %s pwsNXX = %s " % (pwsDXX, pwsNXX) + self._textProduct.debug_print("MATT %s pwsDXX = %s pwsNXX = %s " % + (repr(tr),pwsDXX, pwsNXX), 1) -# if pwsDXX is not None: -# print "SARAH: Window Debug: pwsTXXStats DAY" -# maxPws = pwsDXX -# elif pwsNXX is not None: -# print "SARAH: Window Debug: pwsTXXStats NIGHT" -# maxPws = pwsNXX - - # SARAH - if we are close to the end of a day/night period, the first - # period we would really want to consider would be the next period. - # This is hard-coded to 3 hours to prove the concept. - - # If we are close to starting a DAY period - if ((dayStartHour >= trStartLocalHour and - (dayStartHour - trStartLocalHour) <= 3) or - (trStartLocalHour >= dayStartHour and - trStartLocalHour < nightStartHour and - (nightStartHour - trStartLocalHour) > 3)): - - print "MATT: Window Debug: pwsTXXStats DAY" - if pwsDXX is not None: - maxPws = pwsDXX - else: - maxPws = max(pwsDXX, pwsNXX) - print "MATT:\t\tTaking max pwsDXX/pwsNXX value (%s)" % (maxPws) - - -# # If we are in the middle of a DAY period, and not close to a NIGHT -# elif (trStartLocalHour >= dayStartHour and -# trStartLocalHour < nightStartHour and -# (nightStartHour - trStartLocalHour) > 3): -# -# print "MATT: Window Debug: pwsTXXStats DAY - middle" -# maxPws = pwsDXX - - # If we are close to starting a NIGHT period - elif ((nightStartHour >= trStartLocalHour and - (nightStartHour - trStartLocalHour) <= 3) or - ((trStartLocalHour >= nightStartHour or - trStartLocalHour < dayStartHour) and - abs(dayStartHour - trStartLocalHour) > 3)): + if pwsDXX is not None: + self._textProduct.debug_print("SARAH: Window Debug: pwsTXXStats DAY", 1) - print "MATT: Window Debug: pwsTXXStats NIGHT" - if pwsDXX is not None: - maxPws = pwsNXX - else: - print "Taking max pwsDXX/pwsNXX value" - maxPws = max(pwsDXX, pwsNXX) - -# # If we are in the middle of a NIGHT period, and not close to a DAY -# elif ((trStartLocalHour >= nightStartHour or -# trStartLocalHour < dayStartHour) and -# abs(dayStartHour - trStartLocalHour) > 3): -# # trStartLocalHour < dayStartHour) or pwsDXX is None): -# print "MATT: Window Debug: pwsTXXStats NIGHT - middle" -# maxPws = pwsNXX + if pwsTXXStats.dropFirstGridType == "DAY": + self._textProduct.debug_print("SARAH: Window Debug: dropping a day grid", 1) + self._textProduct.debug_print("SARAH: Window Debug: tr = %s, period = %s" % (tr, period), 1) + pwsTXXStats.droppedFirstGrid = True + return + elif pwsTXXStats.dropFirstGridType == "NIGHT": + # We dropped all the necessary grids now that we found a day grid so stop dropping + pwsTXXStats.dropFirstGridType = None + pwsTXXStats.periodWithFirstCorrectGrid = period + self._textProduct.debug_print("SARAH: Window Debug: found day grid; done dropping night grids", 1) + self._textProduct.debug_print("SARAH: Window Debug: tr = %s, period = %s" % (tr, period), 1) + + maxPws = pwsDXX + + elif pwsNXX is not None: + self._textProduct.debug_print("SARAH: Window Debug: pwsTXXStats NIGHT", 1) + + if pwsTXXStats.dropFirstGridType == "NIGHT": + self._textProduct.debug_print("SARAH: Window Debug: dropping a night grid", 1) + self._textProduct.debug_print("SARAH: Window Debug: tr = %s, period = %s" % (tr, period), 1) + pwsTXXStats.droppedFirstGrid = True + return + elif pwsTXXStats.dropFirstGridType == "DAY": + # We dropped all the necessary grids now that we found a night grid so stop dropping + pwsTXXStats.dropFirstGridType = None + pwsTXXStats.periodWithFirstCorrectGrid = period + self._textProduct.debug_print("SARAH: Window Debug: found night grid; done dropping day grids", 1) + self._textProduct.debug_print("SARAH: Window Debug: tr = %s, period = %s" % (tr, period), 1) + + maxPws = pwsNXX threshold34index = 0 threshold64index = 1 if maxPws is not None: + # Don't shift if the period with the first correct grid is period 0 + if pwsTXXStats.droppedFirstGrid and pwsTXXStats.periodWithFirstCorrectGrid != 0: + period = period - 1 # We dropped the first grid so we are off-by-one + self._textProduct.debug_print("SARAH: shifting period back 1...new period = %s" % + (period), 1) + if "64" in dayGridName: index = threshold64index else: #if "34" index = threshold34index threshold = None - thresholds = self.windSpdProb_thresholds() + thresholds = self._textProduct.windSpdProb_thresholds(threshold, threshold) if period == 0: (thresholdLow, thresholdHigh) = thresholds[period][index] threshold = thresholdLow else: - if period >= 10: # SARAH: TODO - remove??? - period = 9 threshold = thresholds[period][index] if maxPws > threshold: - events.pwsTXXEvent = True + pwsTXXStats.onsetTime = tr.startTime() + pwsTXXStats.endTime = tr.endTime() - configuredEndTime = self._getCorrespondingConfiguredTime(tr.endTime(), isOnset = False) - pwsTXXStats.endHour = self._calculateHourOffset(configuredEndTime) - - print "SARAH: Window Debug: pwsTXXStats dayGridName = %s" % (dayGridName) - print "SARAH: Window Debug: pwsTXXStats nightGridName = %s" % (nightGridName) - print "SARAH: Window Debug: pwsTXXStats original tr = %s" % (repr(tr)) - print "SARAH: Window Debug: pwsTXXStats maxPws = %s" %(repr(maxPws)) - print "SARAH: Window Debug: pwsTXXStats endHour = %s" % (repr(pwsTXXStats.endHour)) - - if pwsTXXStats.onsetHour is None: - configuredStartTime = self._getCorrespondingConfiguredTime(tr.startTime(), isOnset = True) - pwsTXXStats.onsetHour = self._calculateHourOffset(configuredStartTime) - - print "SARAH: Window Debug: pwsTXXStats dayGridName = %s" % (dayGridName) - print "SARAH: Window Debug: pwsTXXStats nightGridName = %s" % (nightGridName) - print "SARAH: Window Debug: pwsTXXStats original tr = %s" % (repr(tr)) - print "SARAH: Window Debug: pwsTXXStats maxPws = %s" %(repr(maxPws)) - print "SARAH: Window Debug: pwsTXXStats onsetHour = %s" % (repr(pwsTXXStats.onsetHour)) - else: - events.pwsTXXEvent = False + self._textProduct.debug_print("SARAH: Window Debug: pwsTXXStats dayGridName = %s" % (dayGridName), 1) + self._textProduct.debug_print("SARAH: Window Debug: pwsTXXStats nightGridName = %s" % (nightGridName), 1) + self._textProduct.debug_print("SARAH: Window Debug: pwsTXXStats original tr = %s" % (repr(tr)), 1) + self._textProduct.debug_print("SARAH: Window Debug: pwsTXXStats maxPws = %s" %(repr(maxPws)), 1) + self._textProduct.debug_print("SARAH: Window Debug: pwsTXXStats onsetTime = %s" % (repr(pwsTXXStats.onsetTime)), 1) + self._textProduct.debug_print("SARAH: Window Debug: pwsTXXStats endTime = %s" % (repr(pwsTXXStats.endTime)), 1) - def _getCorrespondingConfiguredTime(self, gmtTime, isOnset): - dayStartHour = self._textProduct.DAY() - nightStartHour = self._textProduct.NIGHT() - - print "SARAH: In _getCorrespondingConfiguredTime" - print "SARAH: gmtTime = %s" % (repr(gmtTime)) - - gmtSeconds = gmtTime.unixTime() - localTime = time.localtime(gmtSeconds) - print "SARAH: localTime = %s" % (repr(localTime)) - - localHour = localTime.tm_hour - print "SARAH: localHour = %s" % (localHour) - - if isOnset: - print "SARAH: Window Debug: Adjusting start time" - else: - print "SARAH: Window Debug: Adjusting end time" - - newHour = None - if localHour < dayStartHour: - if isOnset: - # Subtract 24 hours to get to the previous day - newGmtTime = gmtTime - 24*60*60 - gmtSeconds = newGmtTime.unixTime() - localTime = time.localtime(gmtSeconds) - print "SARAH: new localTime = %s" % (repr(localTime)) - - newHour = nightStartHour - else: - newHour = dayStartHour - elif dayStartHour <= localHour and localHour < nightStartHour: - if isOnset: - newHour = dayStartHour - else: - newHour = nightStartHour - else: - if isOnset: - newHour = nightStartHour - else: - # Add 24 hours to get to the next day - newGmtTime = gmtTime + 24*60*60 - gmtSeconds = newGmtTime.unixTime() - localTime = time.localtime(gmtSeconds) - print "SARAH: new localTime = %s" % (repr(localTime)) - - newHour = dayStartHour - - print "SARAH: new localHour = %s" % (localHour) - - newTimeTuple = localTime[:3] + (newHour,) + localTime[4:] - import calendar - seconds = calendar.timegm(newTimeTuple) - adjustedGmtTime = AbsTime(seconds) - - # SARAH; Is this the correct variable? - print "SARAH: new local time = %s" % (repr(adjustedGmtTime)) - - seconds = time.mktime(newTimeTuple) - adjustedGmtTime = AbsTime(seconds) - print "SARAH: new GMT time = %s" % (repr(adjustedGmtTime)) - return adjustedGmtTime - - # SARAH - we don't want this here. Use the inherited version from the - # VectorRelatedPhrases module instead. This way, changes only need to be - # made in one place. - def windSpdProb_thresholds(self): - return [ - ((45.0, 80.0), (25.0, 60.0)), # Per 1 - (35.0, 20.0), # Per 2 - (30.0, 15.0), # Per 3 - (25.0, 12.5), # Per 4 - (22.5, 10.0), # Per 5 - (20.0, 8.0), # Per 6 - (17.5, 7.0), # Per 7 - (15.0, 6.0), # Per 8 - (12.5, 5.0), # Per 9 - (10.0, 4.0), # Per 10 - ] - - def _updateWindTimeInfo(self, tr, timeInfo, speed): - if self._maxWind is not None and self._maxWind >= speed: + def _updateWindTimeInfo(self, tr, wind, timeInfo, speed): + if wind >= speed: timeInfo.endHour = self._calculateHourOffset(tr.endTime()) - print "SARAH: Window Debug: In _updateWindTimeInfo" - print "SARAH: Window Debug: timeInfo speed = %s" % (speed) - print "SARAH: Window Debug: timeInfo maxWind = %s" % (self._maxWind) - print "SARAH: Window Debug: timeInfo tr = %s" % (repr(tr)) - print "SARAH: Window Debug: timeInfo endHour = %s" % (timeInfo.endHour) + self._textProduct.debug_print("SARAH: Window Debug: In _updateWindTimeInfo", 1) + self._textProduct.debug_print("SARAH: Window Debug: timeInfo speed = %s" % (speed), 1) + self._textProduct.debug_print("SARAH: Window Debug: timeInfo maxWind = %s" % (self._maxWind), 1) + self._textProduct.debug_print("SARAH: Window Debug: timeInfo tr = %s" % (repr(tr)), 1) + self._textProduct.debug_print("SARAH: Window Debug: timeInfo endHour = %s" % (timeInfo.endHour), 1) if timeInfo.onsetHour is None: timeInfo.onsetHour = self._calculateHourOffset(tr.startTime()) - print "SARAH: Window Debug: onsetHour was None" - print "SARAH: Window Debug: timeInfo speed = %s" % (speed) - print "SARAH: Window Debug: timeInfo maxWind = %s" % (self._maxWind) - print "SARAH: Window Debug: timeInfo tr = %s" % (repr(tr)) - print "SARAH: Window Debug: timeInfo onsetHour = %s" % (timeInfo.onsetHour) + self._textProduct.debug_print("SARAH: Window Debug: onsetHour was None", 1) + self._textProduct.debug_print("SARAH: Window Debug: timeInfo speed = %s" % (speed), 1) + self._textProduct.debug_print("SARAH: Window Debug: timeInfo maxWind = %s" % (self._maxWind), 1) + self._textProduct.debug_print("SARAH: Window Debug: timeInfo tr = %s" % (repr(tr)), 1) + self._textProduct.debug_print("SARAH: Window Debug: timeInfo onsetHour = %s" % (timeInfo.onsetHour), 1) def _computeWindOnsetAndEnd(self, windTimeInfo, pwsXXintStats, pwsTXXStats): onsetEndInfo = self.TimeInfo() - print "SARAH: Window Debug: In _computeWindOnsetAndEnd" - print "SARAH: Window Debug: windTimeInfo.onsetHour = %s" % (windTimeInfo.onsetHour) - print "SARAH: Window Debug: pwsTXXStats.onsetHour = %s" % (pwsTXXStats.onsetHour) - print "SARAH: Window Debug: pwsXXintStats.onsetHour = %s" % (pwsXXintStats.onsetHour) - print "SARAH: Window Debug: windTimeInfo.endHour = %s" % (windTimeInfo.endHour) - print "SARAH: Window Debug: pwsTXXStats.endHour = %s" % (pwsTXXStats.endHour) + self._textProduct.debug_print("SARAH: Window Debug: In _computeWindOnsetAndEnd", 1) + self._textProduct.debug_print("SARAH: Window Debug: windTimeInfo.onsetHour = %s" % (windTimeInfo.onsetHour), 1) + self._textProduct.debug_print("SARAH: Window Debug: pwsXXintStats.onsetHour = %s" % (pwsXXintStats.onsetHour), 1) + self._textProduct.debug_print("SARAH: Window Debug: windTimeInfo.endHour = %s" % (windTimeInfo.endHour), 1) + self._textProduct.debug_print("SARAH: Window Debug: pwsTXXStats.endTime = %s" % (pwsTXXStats.endTime), 1) if windTimeInfo.onsetHour is None: -# print "SARAH: Window Debug: windTimeInfo.onsetHour was None; using pwsTXXStats" -# windTimeInfo.onsetHour = pwsTXXStats.onsetHour -# print "SARAH: Window Debug: pwsTXXStats.onsetHour =", pwsTXXStats.onsetHour - - # Short-circuit this logic as a temporary measure. Basically, do - # not include a window if the deterministic winds do not support - # a particular threshold - onsetEndInfo.endHour = None - - if windTimeInfo.onsetHour is not None and pwsXXintStats.onsetHour is not None: - print "SARAH: Window Debug: windTimeInfo.onsetHour & pwsXXintStats.onsetHour not None; taking min" + # We won't have a timing window + self._textProduct.debug_print("onsetHour for wind is None", 1) + return onsetEndInfo + + if windTimeInfo.onsetHour < 6: + self._textProduct.debug_print("onsetHour for wind is < 6, using that as window onset hour", 1) + onsetEndInfo.onsetHour = windTimeInfo.onsetHour + self._textProduct.debug_print("onsetHour = " + str(onsetEndInfo.onsetHour), 1) + elif pwsXXintStats.onsetHour is not None: + self._textProduct.debug_print("using min onset hour betweeen wind and pwsXXintStats", 1) onsetEndInfo.onsetHour = min(windTimeInfo.onsetHour, pwsXXintStats.onsetHour) - print "SARAH: Window Debug: min onsetHour = %s" % (onsetEndInfo.onsetHour) + self._textProduct.debug_print("onsetHour = " + str(onsetEndInfo.onsetHour), 1) + else: + self._textProduct.debug_print("ERROR: onsetHour for pwsXXintStats is None. Check the grids.", 1) + return onsetEndInfo + + if windTimeInfo.endHour > 114 or windTimeInfo.endHour < 6: + self._textProduct.debug_print("using endHour for wind as window end hour", 1) + onsetEndInfo.endHour = windTimeInfo.endHour + self._textProduct.debug_print("endHour = " + str(onsetEndInfo.endHour), 1) + elif pwsTXXStats.endTime is not None: + self._textProduct.debug_print("endTime for pwsTXXStats is not None", 1) + endUnixTime = pwsTXXStats.endTime.unixTime() + endLocalTime = time.localtime(endUnixTime) + startUnixTime = pwsTXXStats.onsetTime.unixTime() + startLocalTime = time.localtime(startUnixTime) - if onsetEndInfo.onsetHour is not None: - if windTimeInfo.endHour is None: - print "SARAH: Window Debug: windTimeInfo.endHour was None; using pwsTXXStats" - onsetEndInfo.endHour = pwsTXXStats.endHour - print "SARAH: Window Debug: pwsTXXStats.endHour = %s" % (pwsTXXStats.endHour) - elif pwsTXXStats.endHour is not None: - print "SARAH: windendHour = %s" % (windTimeInfo.endHour) - print "SARAH: probendHour = %s" % (pwsTXXStats.endHour) - onsetEndInfo.endHour = int(round(self._textProduct.average(windTimeInfo.endHour, pwsTXXStats.endHour))) - print "SARAH: endHour = %s" % (onsetEndInfo.endHour) + if endLocalTime.tm_hour >= 6 and endLocalTime.tm_hour < 18: + configuredTime = absTimeYMD(endLocalTime.tm_year, + endLocalTime.tm_mon, + endLocalTime.tm_mday, + self._textProduct.DAY()) + elif endLocalTime.tm_hour < 6: + # Use 6 PM of previous day + configuredTime = absTimeYMD(startLocalTime.tm_year, + startLocalTime.tm_mon, + startLocalTime.tm_mday, + self._textProduct.NIGHT()) + else: + configuredTime = absTimeYMD(endLocalTime.tm_year, + endLocalTime.tm_mon, + endLocalTime.tm_mday, + self._textProduct.NIGHT()) + + probEndHour = self._calculateHourOffset(configuredTime) + onsetEndInfo.endHour = int(round(self._textProduct.average(windTimeInfo.endHour, probEndHour))) + self._textProduct.debug_print("endHour = " + str(onsetEndInfo.endHour), 1) + else: + self._textProduct.debug_print("ERROR: endHour for pwsTXXStats is None. Check the grids.", 1) + return onsetEndInfo + return onsetEndInfo - def _createWindow(self, windowName, onsetHour, endHour, nonEndingEvent): + def _createWindow(self, windowName, onsetHour, endHour): window = "Window for " + windowName + " force winds: " - print "SARAH: In _createWindow" - print "SARAH: window stats:" - print "SARAH: onsetHour = %s" % (onsetHour) - print "SARAH: endHour = %s" % (endHour) - print "SARAH: window nonEndingEvent = %s" % (nonEndingEvent) + self._textProduct.debug_print("SARAH: In _createWindow", 1) + self._textProduct.debug_print("SARAH: window stats:", 1) + self._textProduct.debug_print("SARAH: onsetHour = %s" % (onsetHour), 1) + self._textProduct.debug_print("SARAH: endHour = %s" % (endHour), 1) if onsetHour is None: - # SARAH - we do not want a statement of a non-existent window # window += "None" window = None @@ -2180,7 +2396,7 @@ class WindSectionStats(SectionCommonStats): windowPeriod = self._textProduct.makeTimeRange(startTime, endTime) else: windowPeriod = self._textProduct.makeTimeRange(startTime, startTime + 1) - print "SARAH: window period = %s" % (windowPeriod) + self._textProduct.debug_print("SARAH: window period = %s" % (windowPeriod), 1) startTimeDescriptor = "" if onsetHour >= 18: @@ -2188,11 +2404,10 @@ class WindSectionStats(SectionCommonStats): elif 6 <= onsetHour and onsetHour < 18: startTimeDescriptor = self._textProduct._formatPeriod(windowPeriod, resolution = 3) - if endHour is None or nonEndingEvent: - if len(startTimeDescriptor) != 0: - window += "Begins " + startTimeDescriptor - else: - window += "None" + if len(startTimeDescriptor) == 0 and endHour is None: + window = None + elif len(startTimeDescriptor) != 0 and endHour > 114: + window += "Begins " + startTimeDescriptor else: connector = "through " endTimeDescriptor = "the next few hours" @@ -2231,8 +2446,9 @@ class StormSurgeSectionStats(SectionCommonStats): phishEndTime = None possibleStop = 0 -# print "*"*100 -# print "MATT phishStartTime = %s phishEndTime = %s possibleStop = %d" % (str(phishStartTime), str(phishEndTime), possibleStop) + self._textProduct.debug_print("*"*100, 1) + self._textProduct.debug_print("MATT phishStartTime = %s phishEndTime = %s possibleStop = %d" % + (str(phishStartTime), str(phishEndTime), possibleStop), 1) for period in range(len(statList)): tr, _ = timeRangeList[period] @@ -2244,12 +2460,14 @@ class StormSurgeSectionStats(SectionCommonStats): self._inundationMax = phishPeak curPhish = self._textProduct._getStatValue(statDict, "InundationTiming", "Max") -# print "MATT tr = %s" % (repr(tr)) -# print "MATT curPhish = '%s' possibleStop = %d" % (str(curPhish), possibleStop) -# print "MATT phishStartTime = %s phishEndTime = %s" % (str(phishStartTime), str(phishEndTime)) + self._textProduct.debug_print("MATT tr = %s" % (repr(tr)), 1) + self._textProduct.debug_print("MATT curPhish = '%s' possibleStop = %d" % + (str(curPhish), possibleStop), 1) + self._textProduct.debug_print("MATT phishStartTime = %s phishEndTime = %s" % + (str(phishStartTime), str(phishEndTime)), 1) if curPhish is not None and possibleStop != 2: - if curPhish > 0: + if curPhish >= 1: if phishStartTime is None: phishStartTime = tr.startTime() possibleStop = 0 @@ -2262,7 +2480,7 @@ class StormSurgeSectionStats(SectionCommonStats): self._updateThreatStats(tr, statDict, "StormSurgeThreat") - self._windowSurge = "Window for Storm Surge Inundation: " + self._windowSurge = "Window of concern: " if phishStartTime is None or self._inundationMax is None or self._inundationMax < 1: self._windowSurge += "None" @@ -2270,14 +2488,15 @@ class StormSurgeSectionStats(SectionCommonStats): self._onsetSurgeHour = self._calculateHourOffset(phishStartTime) startTime = AbsTime(self._textProduct._issueTime_secs + self._onsetSurgeHour*60*60) -# print "MATT surge startTime = %s self._onsetSurgeHour = %s " % (repr(startTime), self._onsetSurgeHour) + self._textProduct.debug_print("MATT surge startTime = %s self._onsetSurgeHour = %s " % + (repr(startTime), self._onsetSurgeHour), 1) if phishEndTime is not None: self._endSurgeHour = self._calculateHourOffset(phishEndTime) endTime = AbsTime(self._textProduct._issueTime_secs + self._endSurgeHour*60*60) windowPeriod = self._textProduct.makeTimeRange(startTime, endTime) else: windowPeriod = self._textProduct.makeTimeRange(startTime, startTime + 1) - print "SARAH: window period =", windowPeriod + self._textProduct.debug_print("SARAH: surge window period = %s" % (windowPeriod), 1) startTimeDescriptor = self._textProduct._formatPeriod(windowPeriod) @@ -2306,13 +2525,17 @@ class StormSurgeSectionStats(SectionCommonStats): class FloodingRainSectionStats(SectionCommonStats): - def __init__(self, textProduct, segment, statList, timeRangeList): + def __init__(self, textProduct, segment, statList, timeRangeList, + extraRainfallStatList, previousRainfallTRlist): SectionCommonStats.__init__(self, textProduct, segment) self._sumAccum = None + self._prevAccum = 0.00 - self._setStats(statList, timeRangeList) + self._setStats(statList, timeRangeList, extraRainfallStatList, + previousRainfallTRlist) - def _setStats(self, statList, timeRangeList): + def _setStats(self, statList, timeRangeList, extraRainfallStatList, + previousRainfallTRlist): for period in range(len(statList)): tr, _ = timeRangeList[period] statDict = statList[period] @@ -2335,6 +2558,26 @@ class FloodingRainSectionStats(SectionCommonStats): self._currentAdvisory["FloodingRainForecast"] = \ self._textProduct.round(self._sumAccum, "Nearest", 0.5) + # Now compute the previous rainfall + for period in range(len(extraRainfallStatList)): + tr, _ = timeRangeList[period] + prevStatDict = extraRainfallStatList[period] + + prevStats = self._textProduct.getStats(prevStatDict, "QPF") + print "prevStats = ", prevStats + if prevStats is not None: + self._prevAccum += prevStats + else: + self._prevAccum = 0.00 + + if self._prevAccum is not None and self._prevAccum >= 0.10: + # Round so that we don't end up with stats like 4.03143835067749 + self._currentAdvisory["PreviousRainfall"] = \ + self._textProduct.round(self._prevAccum, "Nearest", 0.1) + else: + # Otherwise, do not consider this sgnificant rainfall + self._currentAdvisory["PreviousRainfall"] = 0.00 + class TornadoSectionStats(SectionCommonStats): def __init__(self, textProduct, segment, statList, timeRangeList): @@ -2363,8 +2606,8 @@ class XMLFormatter(): def execute(self, productDict): xml = Element('product') self.dictionary(xml, productDict) - print "SARAH: XML =", xml - print "SARAH: XML dump =", dump(xml) + self._textProduct.debug_print("SARAH: XML = %s" % (xml), 1) + self._textProduct.debug_print("SARAH: XML dump = %s", dump(xml), 1) prettyXML = minidom.parseString(tostring(xml)) return prettyXML.toprettyxml() #tostring(xml) @@ -2468,7 +2711,7 @@ class XMLFormatter(): if "._" in sectionKey: sectionKey = re.sub(".*\._", "", sectionKey) - print "SARAH: sectionKey = %s" % (sectionKey) + self._textProduct.debug_print("SARAH: sectionKey = %s" % (sectionKey), 1) return sectionKey def dictionary(self, xml, productDict): @@ -2488,7 +2731,7 @@ class XMLFormatter(): if key not in self.xmlKeys(): sectionKey = self.getSectionKey(key) if sectionKey not in self.sectionKeys(): - print "SARAH: skipping '%s' in XML" % (key) + self._textProduct.debug_print("SARAH: skipping '%s' in XML" % (key), 1) continue else: key = sectionKey @@ -2530,8 +2773,8 @@ class XMLFormatter(): if data is not None: if 'info' in key and 'Section' in key: subElement = SubElement(xml, key) - print "SARAH: info key = '%s'" % (key) - print "SARAH: value = %s" % (data) + self._textProduct.debug_print("SARAH: info key = '%s'" % (key), 1) + self._textProduct.debug_print("SARAH: value = %s" % (data), 1) if isinstance(data, list): subkey = 'info' + 'Sub' + key[4:] for value in data: @@ -2580,7 +2823,7 @@ class LegacyFormatter(): @return text -- product string ''' text = '' - print "SARAH: productParts = %s" % (repr(productParts)) + self._textProduct.debug_print("productParts = %s" % (repr(productParts)), 1) for part in productParts: valtype = type(part) if valtype is str: @@ -2588,15 +2831,10 @@ class LegacyFormatter(): elif valtype is tuple: name = part[0] infoDicts = part[1] - print "SARAH: name = %s" % (str(name)) - print "SARAH: infoDicts = %s" % (repr(infoDicts)) newtext = self.processSubParts(productDict.get(name), infoDicts) - print "SARAH: newtext type = %s" % (type(newtext)) - print "SARAH: newtext = '%s'" % (repr(newtext)) text += newtext continue elif valtype is list: - print 'GOT HERE -- found list' self._tpc.flush() # TODO THIS SHOULD BE REMOVED AFTER THE REFACTOR OF HazardServicesProductGenerationHandler.JAVA tup = (part[0], part[1]) @@ -2636,8 +2874,6 @@ class LegacyFormatter(): text += '&&\n' elif name not in self._noOpParts(): textStr = productDict.get(name) - print "SARAH: name = %s" % (name) - print "SARAH: textStr = %s" % (textStr) if textStr: text += textStr + '\n' @@ -2803,11 +3039,8 @@ class LegacyFormatter(): """ text = '' for i in range(len(subParts)): - print "SARAH: subpart subParts[i] = %s" % (subParts[i]) - print "SARAH: subpart infoDicts[i] = %s" % (infoDicts[i]) newtext = self._processProductParts(subParts[i], infoDicts[i].get('partsList')) - print "SARAH: subpart newtext type = %s" % (type(newtext)) - print "SARAH: subpart newtext = '%s'" % (repr(newtext)) text += newtext return text +