awips2/cave/com.raytheon.viz.gfe/localization/gfe/userPython/textUtilities/TableBuilder.py
2022-05-05 12:34:50 -05:00

933 lines
33 KiB
Python

##
# This software was developed and / or modified by Raytheon Company,
# pursuant to Contract DG133W-05-CQ-1067 with the US Government.
#
# U.S. EXPORT CONTROLLED TECHNICAL DATA
# This software product contains export-restricted data whose
# export/transfer/disclosure is restricted by U.S. law. Dissemination
# to non-U.S. persons whether in the United States or abroad requires
# an export license or other authorization.
#
# Contractor Name: Raytheon Company
# Contractor Address: 6825 Pine Street, Suite 340
# Mail Stop B8
# Omaha, NE 68106
# 402.291.0100
#
# See the AWIPS II Master Rights File ("Master Rights File.pdf") for
# further licensing information.
##
# ----------------------------------------------------------------------------
# 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.
#
# TableBuilder.py
# Methods for Smart Table products.
#
# Author: hansen
# ----------------------------------------------------------------------------
# SOFTWARE HISTORY
#
# Date Ticket# Engineer Description
# ------------ ---------- ----------- --------------------------
# 01/22/2015 4027 randerso Removed upper casing of weather and discrete phrases
# 03/15/2020 DR21820 NFTF Add Wx duration values to wxCoveragePercent
#
##
##
# This is a base file that is not intended to be overridden.
##
import importlib
import AbsTime
import TextUtils
import TimeRange
from WxMethods import *
from com.raytheon.uf.common.dataplugin.gfe.discrete import DiscreteKey
class TableBuilder(TextUtils.TextUtils):
def __init__(self):
pass
def columnLabels(self, statDict, argDict, weList):
# Return the time labels and the column length for
# the elements in weList
# Could be made more general, but currently gets
# grid time labels for each we listed
hours = []
maxLen = 0
for we in weList:
stats = statDict[we.name]
#print stats
for value, start in stats:
hourStr = repr(start.hour)+"Z/"+repr(start.day)
hours.append(hourStr)
if len(hourStr) > maxLen:
maxLen = len(hourStr)
colLen = maxLen + 4
colLabels = ""
for hour in hours:
colLabels = colLabels + hour.center(colLen)
colLabels = colLabels + "\n"
return colLabels,colLen
def columnValues(self, statDict, argDict, weList, label):
# Return the column values as text strings
values = []
for we in weList:
stats = statDict[we.name()]
for value, hour in stats:
values.append(fformat(value, we.roundVal()))
return label, values
def makeRow(self, rowLabel, colWidth, timeRangeList,
statList, method, argList=None, rowLabelWidth=None,
firstValWidth=None, justify = "r"):
# Produce a row beginning with the label followed by a value
# for each period. Note that colWidth can be a number, for a
# fixed column width, or a list, for a variable column width. If
# a list, then it is a parallel list with the timeRangeList.
# If provided, firstValWidth overrides the colWidth (list or value)
# Justify is "r" for right justify values, "l" for left justify values
# Justify may also be a list, which then is specified for each
# column entry.
# Each value is obtained via the given method which is given arguments:
# (statDict, timeRange, argList)
# and must return a text string value (i.e. numerical values
# must be converted to text strings before being returned).
if type(colWidth) is list:
fixedColWidth = colWidth[0]
else:
fixedColWidth = colWidth
if rowLabelWidth is None:
rowLabelWidth = fixedColWidth
if firstValWidth is None:
firstValWidth = fixedColWidth
values = []
index = 0
for timeRange, label in timeRangeList:
statDict = statList[index]
if statDict is None or method is None:
value = ""
else:
value = method(statDict, timeRange, argList)
values.append(value)
index = index + 1
row = rowLabel.ljust(rowLabelWidth)
for x in range(len(values)):
if x == 0:
width = firstValWidth
elif type(colWidth) is list:
width = colWidth[x]
else:
width = fixedColWidth
if type(justify) is list:
curJustify = justify[x]
else:
curJustify = justify
if curJustify == 'r':
row += str(values[x]).strip().rjust(width)
else:
row += str(values[x]).strip().ljust(width)
width = fixedColWidth
return row + "\n"
def addColValue(self, fcst, str, width):
return fcst + str.strip().rjust(width)
def addRowLabel(self, fcst, str, width):
return fcst + str.strip().ljust(width)
def maxVal(self, stats, timeRange, argList):
# Return a scalar text string value representing the max value
# The desired element name must be the first element of argList
element = argList[0]
value = self.getStats(stats, element)
if value is None:
return ""
min, max = value
return self.getScalarVal(max)
def minVal(self, stats, timeRange, argList):
# Return a scalar text string value representing the min value
# The desired element name must be the first element of argList
element = argList[0]
value = self.getStats(stats, element)
if value is None:
return ""
min, max = value
return self.getScalarVal(min)
def scalarVal(self, stats, timeRange, argList):
# Return a scalar text string value
# The desired element name must be the first element of argList
element = argList[0]
value = self.getStats(stats, element)
if value is None:
return ""
return self.getScalarVal(value)
def vectorVal(self, stats, timeRange, argList):
# Return a vector text string value
# The desired element name must be the first element of argList
# E.g. SW 19
element = argList[0]
value = self.getStats(stats, element)
if value is None:
return ""
return self.getVectorVal(value)
def wxVal(self, stats, timeRange, argList):
# Return a weather text string value
# The desired element name must be the first element of argList
# E.g. SNOW
element = argList[0]
wxStats = self.getStats(stats, element)
if wxStats is None:
return ""
value = ""
#print "\nIn wxVal"
for wxValue, timeRange in wxStats:
#print wxValue, timeRange
val = self.short_weather_phrase(
element,wxValue)
val = val.replace("|", " ")
val = val.replace("THUNDER STORMS", "THUNDERSTORMS")
val = val.replace("THUNDERSTORMS", "TSTMS")
if self.wxOrder(val) < self.wxOrder(value) or value == "":
value = val
#print "value", value
if value == "":
value = "NONE"
#print "Returning ", value
return value
def dayOrNightVal(self, statDict, timeRange, argList):
# Return a min or max value based on the timeRange
# as Day or Night
# The argList contains the weather element for the
# daytime value followed by the element for the nighttime
# value
# Try to report a trend as well
day = self.getPeriod(timeRange,1)
dayElement = argList[0]
nightElement = argList[1]
dayMinMax = argList[2]
nightMinMax = argList[3]
trendElement = argList[4]
priorStatDict = argList[5]
statList = argList[6]
timeRangeList = argList[7]
if day == self.DAYTIME():
element = dayElement
minMax = dayMinMax
else:
element = nightElement
minMax = nightMinMax
curVal = self.getStats(statDict, element)
if curVal is not None:
curVal = self.getValue(curVal, minMax)
value = self.getScalarVal(curVal)
else:
return ""
# Try to get trend
if trendElement is None:
return value
# Get trend 1st or 2nd period
index = 0
for i in range(len(timeRangeList)):
tr, label = timeRangeList[i]
if timeRange == tr:
index = i
break
if index >= 2:
return value
# Try the trend element first
stats = self.getStats(statDict, trendElement)
rawDiff = None
if stats is not None:
rawDiff = int(self.getValue(stats))
else:
# Get data from prior day
# Since we want 24 hours prior AND we are only
# looking at the first 2 periods for trends,
# we always look at the priorStatDict
val = None
val = self.getStats(priorStatDict, element)
if val is not None:
rawDiff = int(curVal) - int(self.getValue(val, minMax))
if rawDiff is None:
return value
else:
if rawDiff > 0:
sign = "+"
else:
sign = ""
return value + " (" + sign + repr(rawDiff) + ")"
####################################################################
# Weather Codes
####################################################################
def getCode(self, stats, timeRange):
# Return the weather code
# Assumes analysis list:
# "analysisList": [
# ("MinT", AnalysisMethods.avg),
# ("MaxT", AnalysisMethods.avg),
# ("PoP", AnalysisMethods.stdDevMaxAvg),
# ("Wx", AnalysisMethods.dominantWx),
# ("Sky", AnalysisMethods.avg),
# ("Wind", AnalysisMethods.vectorTextAvg)
# ],
#
# Get the statistics
popMax = self.getStats(stats, "PoP__stdDevMaxAvg")
maxT = self.getStats(stats,"MaxT__avg")
wxKey = self.getStats(stats,"Wx__dominantWx")
sky = self.getStats(stats,"Sky__avg")
wind = self.getStats(stats,"Wind__vectorAvg")
if wind is not None:
windMag, windDir = wind
else:
windMag = None
if wxKey is not None:
wxSize = len(wxKey)
wxStr = ""
for x in range(wxSize):
wxStr += str(wxKey[x])
if x < wxSize - 1:
wxStr += '^'
wxKey = wxStr
else:
return "?"
# the first if code statement satisfied is used
# the order of the if statements prioritizes the code returned
# eg. if fzrain code = Y is higher priority than snowrain code=O
# then move block of code
# code = self.getCode_Y(popMax, maxT, wxKey, sky, windMag)
# if code is not None:
# return code
##ahead of
# code = self.getCode_O(popMax, maxT, wxKey, sky, windMag)
# if code is not None:
# return code
code = self.getCode_P(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
code = self.getCode_T(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
code = self.getCode_O(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
code = self.getCode_R(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
code = self.getCode_S(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
code = self.getCode_W(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
code = self.getCode_J(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
code = self.getCode_L(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
code = self.getCode_X(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
code = self.getCode_Y(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
code = self.getCode_Z(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
code = self.getCode_M(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
code = self.getCode_Q(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
code = self.getCode_N(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
code = self.getCode_F(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
code = self.getCode_G(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
code = self.getCode_I(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
code = self.getCode_D(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
code = self.getCode_H(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
code = self.getCode_K(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
code = self.getCode_Sky(popMax, maxT, wxKey, sky, windMag, timeRange)
if code is not None:
return code
code = self.getCode_A(popMax, maxT, wxKey, sky, windMag)
if code is not None:
return code
return None
def getCode_P(self, popMax, maxT, wxKey, sky, windMag):
if windMag:
# P -- BLZZRD
# edit conditions for Blizzard below
if (WxContains(wxKey, "Wide S + 1/4SM") or \
WxContains(wxKey, "Wide S + 0SM")) and \
windMag > 35/1.15: # 35 = wind speed in mph
return "P"
return None
def getCode_T(self, popMax, maxT, wxKey, sky, windMag):
#RW and T needed
#Sct,Num,Wide,Chc,Lkly,Def needed
if popMax is not None:
# T -- TSTRMS
# ->first block (commented out) requires RW and T Chc or
# greater be in wx to satisfy. Second block requires
# only T Chc or greater to satisfy
# both blocks require pop >= 45 to satisfy
# if (WxContains(wxKey,"Lkly RW") or WxContains(wxKey,"Wide RW") or \
# WxContains(wxKey,"Num RW") or WxContains(wxKey,"Sct RW") or \
# WxContains(wxKey,"Ocnl RW") or \
# WxContains(wxKey,"Chc RW") or WxContains(wxKey,"Def RW")) and \
# (WxContains(wxKey,"Lkly T") or WxContains(wxKey,"Wide T") or \
# WxContains(wxKey,"Num T") or WxContains(wxKey,"Sct T") or \
# WxContains(wxKey,"Chc T") or WxContains(wxKey,"Def T")) and \
# popMax >= 45:
if (WxContains(wxKey,"Lkly T") or WxContains(wxKey,"Wide T") or \
WxContains(wxKey,"Num T") or WxContains(wxKey,"Sct T") or \
WxContains(wxKey,"Chc T") or WxContains(wxKey,"Def T") or \
WxContains(wxKey,"Ocnl T")) and \
popMax >= 45:
return "T"
return None
def getCode_O(self, popMax, maxT, wxKey, sky, windMag):
if popMax is not None:
# O -- RNSNOW
if WxContains(wxKey, "* R") and WxContains(wxKey, "* S") \
and popMax >= 45:
return "O"
return None
def getCode_R(self, popMax, maxT, wxKey, sky, windMag):
if popMax is not None:
# R -- RAIN
if WxContains(wxKey, "* R") and popMax >= 45:
return "R"
return None
def getCode_S(self, popMax, maxT, wxKey, sky, windMag):
if popMax is not None:
# S -- SNOW
if WxContains(wxKey, "* S") and popMax >= 45:
return "S"
return None
def getCode_W(self, popMax, maxT, wxKey, sky, windMag):
if popMax is not None:
# W -- SHWRS
if WxContains(wxKey, "* RW") and popMax >= 45:
return "W"
return None
def getCode_J(self, popMax, maxT, wxKey, sky, windMag):
if popMax is not None:
# J -- SNOWSHWR
if WxContains(wxKey, "* SW") and popMax >= 45:
return "J"
return None
def getCode_L(self, popMax, maxT, wxKey, sky, windMag):
# L -- DRZL
if WxContains(wxKey, "* L"):
return "L"
return None
def getCode_X(self, popMax, maxT, wxKey, sky, windMag):
if popMax is not None:
# X -- SLEET
if WxContains(wxKey, "* IP") and popMax >= 45:
return "X"
return None
def getCode_Y(self, popMax, maxT, wxKey, sky, windMag):
if popMax is not None:
# Y -- FZRAIN
if WxContains(wxKey, "* ZR") and popMax >= 45:
return "Y"
return None
def getCode_Z(self, popMax, maxT, wxKey, sky, windMag):
# Z -- FZDRZL
if WxContains(wxKey, "* ZL"):
return "Z"
return None
def getCode_M(self, popMax, maxT, wxKey, sky, windMag):
# M -- FLURRIES
# SW-- or S-- will return flurries as defined below
# if you want S-- to be returned as light snow then use
#if WxContains(wxKey, "* SW --"):
if WxContains(wxKey, "* SW --") or WxContains(wxKey, "* S --"):
return "M"
return None
def getCode_Q(self, popMax, maxT, wxKey, sky, windMag):
# Q -- BLGSNO
if WxContains(wxKey, "* BS"):
return "Q"
return None
def getCode_N(self, popMax, maxT, wxKey, sky, windMag):
# edit windthresh for threshhold for getting N windy code returned
windthresh = 25.0 # wind threshold in mph
#print "wind ",windthresh,windMag,"\n"
if windMag is not None:
# N -- WINDY
# the uncommented assumes all your wind speeds in gfe are in knts
# if you have converted them to mph then use
#if windMag > windthresh : #
if windMag > windthresh/1.15: #mph from kts
return "N"
return None
def getCode_F(self, popMax, maxT, wxKey, sky, windMag):
# F -- FOGGY
# if wx contains F+ any coverage return F
# you may want to change this to include F only if Areas of F
# are fcst any intensity
# if WxContains(wxKey, "Areas F *"):
if WxContains(wxKey, "* F +"):
return "F"
return None
def getCode_G(self, popMax, maxT, wxKey, sky, windMag):
if maxT is not None:
# G -- VRYHOT edit your threshhold in degs F
threshhold = 105
if maxT > threshhold:
return "G"
return None
def getCode_I(self, popMax, maxT, wxKey, sky, windMag):
if maxT is not None:
# I -- VRYCOLD edit your threshhold in degs F
threshhold = 20
if maxT < threshhold:
return "I"
return None
def getCode_D(self, popMax, maxT, wxKey, sky, windMag):
# D -- DUST
if WxContains(wxKey, "* BD"):
return "D"
return None
def getCode_H(self, popMax, maxT, wxKey, sky, windMag):
# H -- HAZE
if WxContains(wxKey, "* H"):
return "H"
return None
def getCode_K(self, popMax, maxT, wxKey, sky, windMag):
# K -- SMOKE
if WxContains(wxKey, "* K"):
return "K"
return None
def getCode_Sky(self, popMax, maxT, wxKey, sky, windMag, timeRange):
# C, E, B, U, V, depending upon sky and time of day
if sky is not None:
# C -- CLDY
if sky > 94:
return "C"
# E -- MCLDY
elif sky > 69:
return "E"
# B -- PTCLDY
elif sky > 31:
return "B"
# U -- SUNNY
else:
localTimeRange = self.shiftedTimeRange(timeRange)
dayNight = self.getPeriod(localTimeRange)
if dayNight == self.DAYTIME():
return "U"
else:
return "V"
return None
def getCode_A(self, popMax, maxT, wxKey, sky, windMag):
# A -- FAIR
return "A"
def getScalarVal(self, value):
# Return 4-digit right-justified text representation of value
# Check for no data
if not value:
return " "
else:
# Convert to integer string
return str(int(value)).rjust(4)
def getVectorVal(self, value):
# Return text representation of vector value
# Value is a tuple of magnitude and direction
# E.g. returned value: SW 19
# Check for no data
if value == () or value is None:
return " "
else:
mag = value[0]
dir = value[1]
magStr = str(int(mag)).rjust(3)
if type(dir) is not str:
dir = self.dirToText(dir)
dirStr = dir.rjust(2)
return dirStr + magStr
def getWxVal(self, value):
# Return text representation of value
return self.wx_phrase({}, {}, value)
def wxOrder(self, value):
value = value.lower()
if value == "thunderstorms":
return 0
elif value == "rain showers":
return 1
elif value == "rain":
return 2
elif value == "snow":
return 3
else:
return 4
def short_weather_phrase(self, element, stats):
" Develop short phrase for weather in a table"
# Weather Stats:
# SubKey List : list of all subkeys mentioned in time period
if stats is None:
return ""
subkeyList = self.makeSubkeyList(stats)
if len(subkeyList) == 0:
return ""
value = ""
#print "In short_weather_phrase"
for subKey in subkeyList:
val, cov = self.weather_value(None, None, subKey, typeOnly=1)
#print "subKey", val
if self.wxOrder(val) < self.wxOrder(value) or value == "":
value = val
#print "value", value
value = value.replace(" ", "|")
value = value.replace("thunderstorm", "thunder|storm")
#print "returning", value
return value
def long_weather_phrase(self, element, stats):
# Stats from SampleAnalysis method: weather_percentages
words = ""
index = 0
prevCoverage = None
conjunction = "|"
if stats is None:
return "None"
length = len(stats)
for subkey, percentage in stats:
#print "subkey, percent", subkey, percentage
if subkey is None or subkey.wxType() == "<NoWx>":
index += 1
continue
# If not last one, determine nextCoverage
if index < length-1:
nextSubkey, percentage = stats[index+1]
else:
nextSubkey = None
value, prevCoverage = self.weather_value(
None, None, subkey, prevCoverage, nextSubkey)
percentage = int(self.round(percentage,"Nearest", 1))
words = words + value + " (" + repr(percentage) + "%) "
#prevSubkey = subkey
# if last one, do not add conjunction
if index == length - 1:
break
words = words + conjunction
index = index + 1
if words == "":
words = "None"
return words
def discrete_value(self, element, stats):
" Return string of hazards"
# Weather Stats:
# SubKey List : list of all subkeys mentioned in time period
if stats is None:
return ""
subkeyList = self.makeSubkeyList(stats)
if len(subkeyList) == 0:
return ""
from com.raytheon.uf.viz.core.localization import LocalizationManager
siteId = LocalizationManager.getInstance().getSite()
value = ""
#print "In discrete_value"
for subKey in subkeyList:
str = subKey.split(":")[0]
discreteWords = DiscreteKey.discreteDefinition(siteId).keyDesc(
"Hazards" + "_SFC", str)
value = value + discreteWords + " "
#print "returning", value
return value
def long_discrete_phrase(self, element, stats):
# Stats from SampleAnalysis method: discrete_percentages
words = ""
index = 0
prevCoverage = None
conjunction = "|"
if stats is None:
return "None"
length = len(stats)
from com.raytheon.uf.viz.core.localization import LocalizationManager
siteId = LocalizationManager.getInstance().getSite()
for subkey, percentage in stats:
#print "subkey, percent", subkey, percentage
if subkey is None or subkey == "<None>":
index += 1
continue
percentage = int(self.round(percentage,"Nearest", 1))
str = subkey.split(":")[0]
discreteWords = DiscreteKey.discreteDefinition(siteId).keyDesc(
"Hazards" + "_SFC", str)
words = words + discreteWords + " (" + repr(percentage) + "%) "
# if last one, do not add conjunction
if index == length - 1:
break
words = words + conjunction
index = index + 1
if words == "":
words = "None"
return words
def cloudCover(self, element, stats):
# Return a text cloud cover given Sky average value
valStr = ""
if stats is None:
return valStr
val = self.callMethod(stats, element.conversion())
value = self.round(val, "Nearest", element.roundVal())
if value < 20:
shift = self.determineShift()
period = element.getPeriod()
tr = TimeRange.TimeRange(period.startTime() + shift, period.endTime() + shift)
dayNight = self.getPeriod(tr)
if dayNight == self.NIGHTTIME():
valStr = "Clear"
else:
valStr = "Sunny"
elif value < 55:
valStr = "Partly|Cloudy"
elif value < 85:
valStr = "Mostly|Cloudy"
else:
valStr = "Cloudy"
return valStr
def wxPrecipSubkey(self, subkey):
# List of wxTypes that should be counted as precipitation
# for the calculation of Wx Duration
if subkey.wxType() in ["R", "RW", "S", "SW", "ZR", "IP", "SA"]:
return 1
else:
return 0
def wxCoveragePercent(self, coverage):
percents = {
"Def": 100,
"Wide": 100,
"Pds": 90,
"Frq": 80,
"Ocnl": 80,
"Lkly": 75,
"Num": 60,
"Inter": 60,
"Sct": 40,
"Brf": 30,
"Chc": 20,
"SChc": 10,
"Iso": 10,
}
return percents.get(coverage, 0)
def wxDuration(self, statsByRange, timeRange):
# Used in the FWM and FWFTable for weather duration
# Weather duration is determined as follows:
# Coverages are weighted according to the
# values in wxCoveragePercent. For example, "Wide"
# gets 100% weighting while "Sct" on get 40%.
# Only precip weather types (according to the wxPrecipSubkey)
# are counted toward the duration.
#
# Create a total duration by adding a contribution from each grid:
# For each grid in the time range:
# Among the precip subkeys, find the maximum Coverage percent.
# Weight it's contibution according to the amount of time
# it overlaps the given time range and the maximum coverage percent.
#
total = 0.0
for subkeyList, subRange in statsByRange:
subkeyList = self.makeSubkeyList(subkeyList)
maxPercent = 0
for subkey in subkeyList:
if self.wxPrecipSubkey(subkey):
percent = self.wxCoveragePercent(subkey.coverage())
#print subkey.coverage(), percent
if percent > maxPercent:
maxPercent = percent
subRange = timeRange.intersection(subRange)
value = maxPercent/100.0 * subRange.duration()/3600
#print value
total = total + value
total = self.round(total, "Nearest", 1)
#print "wxDur", total, timeRange
return repr(int(total))
# Special interface to Multiple Element Table
def makeMultipleElementTable(self, areaLabel, timeRange, argDict,
byTimeRange=0, product="MultipleElementTable_Aux_Local"):
# For each area in the areaLabel group of Combinations,
# For each city
# Generate a MultipleElementTable
comboList = self.getCurrentAreaNames(argDict)
cityDictModule = importlib.import_module(self._cityDictionary)
cityDict = cityDictModule.CityDictionary
table = ""
if type(argDict) is not dict:
# "argDict" is really "tree"
argDict = argDict.get("argDict")
argDict["elementList"] = self._elementList
argDict["singleValueFormat"] = self._singleValueFormat
argDict["includeTitle"] = 1
argDict["byTimeRange"] = byTimeRange
for area in comboList:
try:
cities = cityDict[area]
except:
continue
for city, cityLabel in cities:
table = table + self.generateProduct(
product, argDict, city,
timeRange=timeRange, areaLabel=cityLabel)
# ensure table has valid data
if table == "":
continue
argDict["includeTitle"] = 0
table = "\n" + table + "\n\n"
return table
def getMultipleElementTableRanges(self, productIssuance, singleValueFormat,
timeRange=None):
if productIssuance in [
"Morning", "Morning Update", "Afternoon Update",
"Morning with Pre-1st Period"]:
self._productIssuance = "Morning"
startHour = self.DAY()
numPeriods = 3
else: # "Afternoon", "Afternoon with Pre-1st Period",
# "Evening Update", "Early Morning Update"
self._productIssuance = "Afternoon"
startHour = self.NIGHT()
numPeriods = 4
labelMethod = self.getLocalWeekday
#print "\nMET Ranges", productIssuance, timeRange
if timeRange is None:
currentLocalTime, self._shift = self.determineTimeShift()
day = currentLocalTime.day
month = currentLocalTime.month
year = currentLocalTime.year
# Use getPeriods to set up a list of
# time periods to be sampled
# Returns a list of tuples: (timeRange, timeRangeLabel)
# Convert to GMT time before making time range
if productIssuance == "Early Morning Update":
self._shift = self._shift + 24*3600
labelMethod = self.getLocalWeekdayName
startTime = AbsTime.absTimeYMD(year,month,day,startHour)
startTime = startTime - self._shift
timeRange = TimeRange.TimeRange(startTime, startTime + 12*3600)
if singleValueFormat == 1:
numPeriods = 1
timeRangeList = self.getPeriods(timeRange, 12, 12, numPeriods,
labelMethod=labelMethod)
# Adjust the first time range if an update issuance
if productIssuance not in ["Morning", "Afternoon"]:
updateTime = AbsTime.absTimeYMD(year, month, day, currentLocalTime.hour)
updateTime = updateTime - self._shift
tr, label = timeRangeList[0]
updateTR = TimeRange.TimeRange(updateTime, tr.endTime())
timeRangeList[0] = (updateTR, labelMethod(updateTR))
#print "Returning", timeRangeList
return timeRangeList