%
import time
import os
import os.path
from os.path import join as pjoin
##############################################################################
# The inline stylesheet, handles warning borders
g_sStyleInfo = """
a:link.warn{ color: red; }
a:visited.warn{ color: red; }
table{ border-collapse: separate; border-spacing: 0px }
td { padding: 3px; margin: 0px}
td.warn_topbtm_left { border-style: solid none solid solid; border-color: red; border-width: 1px; }
td.warn_topbtm_mid { border-style: solid none solid none; border-color: red; border-width: 1px; }
td.warn_topbtm_right{ border-style: solid solid solid none; border-color: red; border-width: 1px; }
td.warn_top_left { border-style: solid none none solid; border-color: red; border-width: 1px; }
td.warn_top_mid { border-style: solid none none none; border-color: red; border-width: 1px; }
td.warn_top_right { border-style: solid solid none none; border-color: red; border-width: 1px; }
td.warn_mid_left { border-style: none none none solid; border-color: red; border-width: 1px; }
td.warn_mid_mid { border-style: none none none none; border-color: red; border-width: 1px; }
td.warn_mid_right { border-style: none solid none none; border-color: red; border-width: 1px; }
td.warn_btm_left { border-style: none none solid solid; border-color: red; border-width: 1px; }
td.warn_btm_mid { border-style: none none solid none; border-color: red; border-width: 1px; }
td.warn_btm_right { border-style: none solid solid none; border-color: red; border-width: 1px; }
"""
##############################################################################
# Cal Warnings and Borders
g_sCalWarn = 'Cal Note'
g_llBorder = [
# Type 0, no boarder style info
["", "", "", "" ],
# Type 1, single row box info
[' class="warn_topbtm_left"', ' class="warn_topbtm_mid"', ' class="warn_topbtm_mid"', ' class="warn_topbtm_right"'],
# Type 2, top row of multi-row box
[' class="warn_top_left"', ' class="warn_top_mid"', ' class="warn_top_mid"', ' class="warn_top_right"'],
# Type 3, mid row of a multi-row box
[' class="warn_mid_left"', ' class="warn_mid_mid"', ' class="warn_mid_mid"', ' class="warn_mid_right"'],
# Type 4, bottom row of a multi-row box
[' class="warn_btm_left"', ' class="warn_btm_mid"', ' class="warn_btm_mid"', ' class="warn_btm_right"']
]
##############################################################################
g_sServer = 'vap+das2server:http://emfisis.physics.uiowa.edu/das/das2Server'
##############################################################################
# Information URLs by product type
g_dInfoURLs = {
'HFR-spectra':'http://emfisis.physics.uiowa.edu/node/31',
'HFR-spectra-burst':'http://emfisis.physics.uiowa.edu/node/33'
}
##############################################################################
# This is a mapping from directories and file components to Das2Streams.
# The general format of each entry is:
#
# Key: (dir_match, filename_match1)
# Value: [ (Link Name, dataset), (Link Name, dataset), ...]
g_dDsdfStubs = {
# Spacecraft A...
('RBSP-A/Quick-Look', 'HFR-spectra') : [
('Spectra', 'rbsp/RBSP-A/HFR_spectra.dsdf')
],
('RBSP-A/Quick-Look', 'HFR-waveform'): [
# Nothing Yet
],
('RBSP-A/Quick-Look', 'WFR-spectral-matrix'): [
('BuBu', 'rbsp/RBSP-A/WFR_spectral_matrices/BuBu.dsdf'),
('BvBw', 'rbsp/RBSP-A/WFR_spectral_matrices/BvBv.dsdf'),
('BwBw', 'rbsp/RBSP-A/WFR_spectral_matrices/BwBw.dsdf'),
('EvEv', 'rbsp/RBSP-A/WFR_spectral_matrices/EvEv.dsdf'),
('EuEu', 'rbsp/RBSP-A/WFR_spectral_matrices/EuEu.dsdf'),
('EwEw', 'rbsp/RBSP-A/WFR_spectral_matrices/EwEw.dsdf'),
('B-Power', 'rbsp/RBSP-A/WFR_spectral_matrices/TotalPowerB.dsdf')
],
('RBSP-A/Quick-Look', 'WFR-waveform-burst'): [
# Nothing Yet
],
('RBSP-A/Quick-Look', 'WFR-waveform-continuous-burst') : [
('Bu', 'rbsp/RBSP-A/WFR_continuous_waveform/Bu.dsdf')
],
('RBSP-A/Quick-Look', 'WFR-waveform') : [
# Nothing Yet
],
('RBSP-A/Quick-Look', 'mag-292_QL-uvw' ) : [
('Components', 'rbsp/RBSP-A/Magnetometer.dsdf'),
('B-Magnitude', 'rbsp/RBSP-A/BMag.dsdf'),
('Fce', 'rbsp/RBSP-A/fce.dsdf')
],
# Spacecraft B...
('RBSP-B/Quick-Look', 'HFR-spectra') : [
('Spectra', 'rbsp/RBSP-B/HFR_spectra.dsdf')
],
('RBSP-B/Quick-Look', 'HFR-waveform'): [
# Nothing Yet
],
('RBSP-B/Quick-Look', 'WFR-spectral-matrix'): [
('BuBu', 'rbsp/RBSP-B/WFR_spectral_matrices/BuBu.dsdf'),
('BvBw', 'rbsp/RBSP-B/WFR_spectral_matrices/BvBv.dsdf'),
('BwBw', 'rbsp/RBSP-B/WFR_spectral_matrices/BwBw.dsdf'),
('EvEv', 'rbsp/RBSP-B/WFR_spectral_matrices/EvEv.dsdf'),
('EuEu', 'rbsp/RBSP-B/WFR_spectral_matrices/EuEu.dsdf'),
('EwEw', 'rbsp/RBSP-B/WFR_spectral_matrices/EwEw.dsdf'),
('B-Power', 'rbsp/RBSP-B/WFR_spectral_matrices/TotalPowerB.dsdf')
],
('RBSP-B/Quick-Look', 'WFR-waveform-burst'): [
# Nothing Yet
],
('RBSP-B/Quick-Look', 'WFR-waveform-continuous-burst') : [
('Bu', 'rbsp/RBSP-B/WFR_continuous_waveform/Bu.dsdf')
],
('RBSP-B/Quick-Look', 'WFR-waveform') : [
# Nothing Yet
],
('RBSP-B/Quick-Look', 'mag-292_QL-uvw' ) : [
('Components', 'rbsp/RBSP-B/Magnetometer.dsdf'),
('B-Magnitude', 'rbsp/RBSP-B/BMag.dsdf'),
('Fce', 'rbsp/RBSP-B/fce.dsdf')
]
}
##############################################################################
def _getType(sFile):
# Get the product name (and maybe a description)
lName = sFile.split('_')
sType = lName[1]
# Mag has to be different
if sType == 'magnetometer':
sType = "%s_%s"%(lName[1], lName[2])
return sType
def _typeMatch(lFiles, iOne, iTwo):
"""Given a list of file names in the lFiles list, see
if index 1 has the same type as index two"""
iMax = len(lFiles) - 1
if iTwo < 0 or iTwo > iMax:
return False
sTypeOne = _getType(lFiles[iOne])
sTypeTwo = _getType(lFiles[iTwo])
return sTypeOne == sTypeTwo
##############################################################################
def getHdr(req):
"""Get the page title, the rules:
1. Remove '/Flight' if present
2. Remove /index.psp
3. Take the last three path components and turn into a date
4. Take all remaining path components and use Arrow seperations"""
sUri = req.uri.replace("/Flight/", "")
sUri = os.path.dirname(sUri)
lUri = sUri.split('/')
if len(lUri) >= 3:
sDate = "%s-%s-%s"%(lUri[-3], lUri[-2], lUri[-1])
lUri = lUri[:-3]
else:
return sUri
if lUri[0].startswith('RBSP'):
lUri[0] = lUri[0].replace('RBSP','Van Allen Probes')
sLogo = ''
return "%s %s: %s"%(sLogo, " -> ".join(lUri), sDate)
##############################################################################
def getHour(sFile):
"""sFile is the base filename, returns an empty string if this is not
an hourly file, and a string similar to '(hour 03)' if this is an hourly
file."""
lParts = sFile.split('_')
if len(lParts) < 2:
return ''
# File format is stuff_stuff_stuff_201210005T16_stuff
if lParts[-2][-3] != 'T':
return ''
try:
nHour = int(lParts[-2][-2:])
except ValueError:
return ''
if nHour < 0 or nHour > 23:
return ''
return "(hour %02d)"%nHour
##############################################################################
# Replacing last three directory names with aggregation symbols
def getAggDir(sDir):
"""Assumes last three directory components are YYYY/MM/DD"""
lDirs = sDir.split('/')
if len(lDirs) < 3:
return sDir
lDirs[-3] = '$Y'
lDirs[-2] = '$m'
lDirs[-1] = '$d'
return '/'.join(lDirs)
##############################################################################
# Replacing next to last underscore chunk with aggregation symbols, plus
# replaces everything from the last v to the .cdf with $(v,sep)
def getAggFile(sFile):
lParts = sFile.split('_')
if len(lParts) < 2:
return sFile
if lParts[-2][4] == '-' and lParts[-2][7] == '-':
lParts[-2] = '$Y-$m-$d'
else:
if lParts[-2].find('T') == -1:
lParts[-2] = '$Y$m$d'
else:
lParts[-2] = '$Y$m$dT$H'
lParts[-1] = 'v$(v,sep).cdf'
return '_'.join(lParts)
#############################################################################
# reads the next to last underscore sep chunk and returns that at the time
# range
g_dDaysInMon = {1:31, 2:28, 3:31, 4:30, 5:31, 6:30, 7:31, 8:31, 9:30, 10:31, 11:30, 12:31}
g_dDaysInMonLeap = {1:31, 2:29, 3:31, 4:30, 5:31, 6:30, 7:31, 8:31, 9:30, 10:31, 11:30, 12:31}
def isLeapYear(nYear):
"""
Returns True if given year is leap year, returns False if not,
Throws if Year is equal to or less than 0
"""
if (nYear % 4) != 0:
return False
elif (nYear % 400) == 0:
return True
elif (nYear % 100) == 0:
return False
else:
return True
def _getNextDay(sDay):
lDate = sDay.split('-')
try:
nYear = int(lDate[0])
nMonth = int(lDate[1])
nDom = int(lDate[2])
except:
return None
nDom += 1
if isLeapYear(nYear):
if nDom > g_dDaysInMonLeap[nMonth]:
nDom -= g_dDaysInMonLeap[nMonth]
nMonth += 1
else:
if nDom > g_dDaysInMon[nMonth]:
nDom -= g_dDaysInMon[nMonth]
nMonth += 1
if nMonth > 12:
nMonth -= 12
nYear += 1
return "%04d-%02d-%02d"%(nYear, nMonth, nDom)
def getTimeRng(sFile, bDasVer=False):
"""sFile is the base filename"""
lParts = sFile.split('_')
if len(lParts) < 2:
return None
sTime = lParts[-2]
# Needs time seperators
if sTime.find('-') == -1:
sTime = "%s-%s-%s"%(lParts[-2][:4], lParts[-2][4:6], lParts[-2][6:])
# This is a royal PITA for hourly files, have to pull off the hour,
# advance to the next one handling day boundaries
iT = sTime.find('T')
if iT != -1:
sDay1 = sTime[:iT]
try:
nHour1 = int(sTime[-2:])
except ValueError:
return None
nHour2 = nHour1 + 1
if nHour2 >= 24:
nHour2 -= 24
sDay2 = _getNextDay(sDay1)
if sDay2 == None:
return None
else:
sDay2 = sDay1
if bDasVer:
sRng = "start_time=%sT%02d&end_time=%sT%02d"%(sDay1, nHour1, sDay2, nHour2)
else:
sRng = "%sT%02d:00 to %sT%02d:00"%(sDay1, nHour1, sDay2, nHour2)
return sRng
else:
if bDasVer:
# Crap, das has to have a stop time
return "start_time=%s&end_time=%s"%(sTime, _getNextDay(sTime))
else:
return sTime
##############################################################################
def getStreamEnts(sDir, sFile):
lRet = []
for tKey in g_dDsdfStubs.keys():
#lRet.append('(%s:%s):(%s:%s)
'%(sDir, sFile, tKey[0], tKey[1]) )
# This file in this directory may have associated streams
if sDir.find(tKey[0]) != -1 and sFile.find(tKey[1]) != -1:
lStreams = g_dDsdfStubs[tKey]
sRange = None
for (sName, sDataSet) in lStreams:
if sRange == None:
sRange = getTimeRng(sFile, True)
if sRange == None:
continue
sLink = '%s'%(
"http://autoplot.org/autoplot.jnlp",g_sServer,sDataSet,
sRange,sName)
lRet.append(sLink)
if len(lRet) > 1:
return '%s '%(' '.join(lRet))
else:
return ''
##############################################################################
# Main
#req.write("Right now it's %s
\n"%time.strftime("%04Y-%02m-%02dT%02H:%02M:%02S"))
sDir = os.path.dirname(req.filename)
lFiles = os.listdir(sDir)
lFiles = filter(lambda s: s.lower().endswith('.cdf'), lFiles)
# Make a dictionary by primary filename, then have a list of versions.
dFiles = {}
for sFile in lFiles:
iV = sFile.rfind('v')
if iV != -1:
sKey = sFile[:iV]
sVer = sFile[iV:]
if not dFiles.has_key(sKey):
dFiles[sKey] = [sVer]
else:
dFiles[sKey].append(sVer)
# Make a dictonary of warning row types by file key
lKeys = dFiles.keys()
lKeys.sort()
dRowType = {}
for iFile in xrange(0, len(lKeys)):
sKey = lKeys[iFile]
if sKey.find('WFR-waveform') == -1:
dRowType[sKey] = 0
continue
bHaveAbove = _typeMatch(lKeys, iFile, iFile - 1)
bHaveBelow = _typeMatch(lKeys, iFile, iFile + 1)
if not bHaveAbove and not bHaveBelow: # I am a singleton
dRowType[sKey] = 1
elif not bHaveAbove and bHaveBelow: # I am the top entry
dRowType[sKey] = 2
elif bHaveAbove and bHaveBelow: # I a mid entry
dRowType[sKey] = 3
elif bHaveAbove and not bHaveBelow: # I am a bottom entry
dRowType[sKey] = 4
# Sent the spacecraft ID string
sUrlDir = os.path.dirname(req.uri)
if sUrlDir.find("RBSP-A") != -1:
sCraft = "RBSP-A"
elif sUrlDir.find("RBSP-B") != -1:
sCraft = "RBSP-B"
else:
sCraft = "UNKNOWN!"
# Now it's output time
req.write("\n")
req.write("
Product Information | ') #req.write('Size | ') #req.write('Modified (UTC) | \n') req.write('Load to Autoplot | \n') req.write('Download | \n') req.write('  |
  |   |   |   | ||
  |   |   |   | ||
  |   |   |   | ||
%s | %s | %s | %s%s | %s | %s%s | %s | %s | '''%(sRowStart, lStyle[0], sProduct, lStyle[1], sDsdfEntries, sAllEntry, lStyle[2], sFileEntry, lStyle[3], sWarnLink)) req.write('