diff --git a/cgi-bin~/data_data.py b/cgi-bin~/data_data.py
new file mode 100755
index 0000000000000000000000000000000000000000..7a83e866e47842ed32661ba997595e3b7373d15f
--- /dev/null
+++ b/cgi-bin~/data_data.py
@@ -0,0 +1,240 @@
+#!/usr/bin/python
+# data_data.py
+# CGI retrieves RIG data in an ascii table.
+#
+# Author: Maciek Smuga-Otto <maciek@ssec.wisc.edu>
+# Copyright 2003-2007 University of Wisconsin-Madison
+#
+# example invocation:
+# .../data_data.py?start=2003-03-27+14:05:57&end=2003-03-27+15:05:57&symbols=TEMP41372:CS10162:PAROSCI
+# if no end value provided, assume end = most recent sample.
+# if no begin is provided, assume it is the same as end (return one value only).
+# thus, calling with no begin OR end gives the single most recent sample.
+#
+CVS_ID="$Id: data_data.py,v 1.25 2009/04/29 18:36:57 brucef Exp $"
+
+import cgi, os, sys # system/cgi utils
+import math
+import psycopg2 as db
+from data_symbols import symalias, precision # symbol alias table and symbol precision table
+from data_usage import usage
+from dewpoint import dewpoint
+
+DB = ('rig','rig_client','borabora.ssec.wisc.edu','wXn0w')
+
+def get_slice(con, begin, end, resample, resolution, *symbols):
+ """Get data for the specified list of symbols, every resample'th timestamp in the
+ range defined by [begin, end).
+ """
+ begin, end = adjust_ts(con, begin, end, resolution)
+ #print 'begin:',begin, 'end:', end # DEBUG
+
+ # construct the wide query
+ dbsymbols, dbtables, dbwhereclause = ("", "", "")
+ c = con.cursor()
+ for symbol in symbols:
+ c.execute("SELECT symbol_id FROM symbols WHERE name='%s'" % symbol)
+ symbol_id = c.fetchone()[0]
+ dbsymbols += ", d_%s.value AS %s" % (symbol, symbol)
+ dbtables += ", data d_" + symbol
+ dbwhereclause += "AND samples.sample_id = d_%s.sample_id AND d_%s.symbol_id = %s " % \
+ (symbol, symbol, symbol_id)
+ # FIXME - this is a temporary patch to compensate for spurious error values written
+ # into the database for accurain, which will have to get fixed in the java ingest.
+ if symbol_id == 102:
+ dbwhereclause += "AND d_%s.value != -99999 " % symbol
+
+ query = "SELECT stamp%s FROM samples%s WHERE stamp >= '%s' AND stamp <= '%s' %s \
+AND resolution = %s " % (dbsymbols, dbtables, begin, end, dbwhereclause, resolution)
+ if resample > 1:
+ query += "AND floor(extract( epoch from stamp::timestamp - stamp::date)/%s)::int %% (%s) = 0 "% (resolution, resample)
+ query += "ORDER BY stamp"
+
+ #print query # for debugging only
+ c.execute(query)
+ return c.fetchall()
+
+def adjust_ts(dbconnection, begin, end, resolution):
+ if not end:
+ c = dbconnection.cursor()
+ if not begin:
+ c.execute("SELECT max(stamp), max(stamp) from samples where resolution=%s" % resolution)
+ return c.fetchone()
+ # the following several lines are for the case where begin timestamp is -hh:mm:ss
+ # indicating that the data be gathered for the past hh hours, mm minutes and ss seconds.
+ begin_seconds_ago = resolution
+ if '-' == begin[0]:
+ (hh,mm,ss) = [int(t) for t in begin[1:].split(':')]
+ begin_seconds_ago = max(ss + 60 * mm + 3600 * hh, resolution)
+ c.execute( "SELECT now(), now() - '%s seconds'::interval" % begin_seconds_ago )
+ (end, maybegin) = c.fetchone()
+ if '-' == begin[0]:
+ begin = maybegin
+
+ elif not begin:
+ c = dbconnection.cursor()
+ c.execute( "SELECT '%s'::timestamp - '%s seconds'::interval" % ( end, resolution ) )
+ begin = c.fetchone()[0]
+
+ return ( begin, end )
+
+
+
+def cgiheader( html = None ):
+ if html:
+ print "Content-Type: text/html"
+ else:
+ print "Content-Type: text/plain"
+ print "Cache-control: private, no-cache"
+ print "Expires: Thu, 1 Aug 2002 12:23:00 GMT"
+ print "Pragma: no-cache"
+ print
+
+def linearConvert( data, factor = 0, offset = 0 ):
+ return data * factor + offset
+
+def normalize_rh(rh):
+ return rh > 100 and 100 or rh
+
+#######################
+# MAIN
+
+def cgimain():
+ form = cgi.FieldStorage()
+
+ # process CGI input
+ if 'symbols' not in form.keys():
+ cgiheader(1)
+ usage()
+ sys.exit(0)
+
+ begin,end = '','' # begin, end timestamps
+ if 'begin' in form.keys():
+ begin = form['begin'].value
+ if 'end' in form.keys():
+ end = form['end'].value
+
+ altts = 0 # alternate timestamp display - replace dashes and colons with spaces
+ if 'altts' in form.keys():
+ altts = 1
+
+ separator = '' # separates individual fields in an output row. Defaults to aligned column output.
+ if 'separator' in form.keys():
+ separator = form['separator'].value
+
+ resolution = 5 # in seconds. Default for raw Campbell data, must be changed to get Ceilometer (15) or
+ # DB-averaged Campbell data.
+ if 'resolution' in form.keys():
+ resolution = int( form['resolution'].value )
+
+ resample = 1
+ if 'interval' in form.keys():
+ interval_str = form['interval'].value
+ interval_chunks = interval_str.split(':')
+ int_ss, int_mm, int_hh = 0, 0, 0
+ if len(interval_chunks) == 1:
+ int_ss = int(interval_chunks[0])
+ if len(interval_chunks) == 2:
+ int_mm = int(interval_chunks[0])
+ int_ss = int(interval_chunks[1])
+ if len(interval_chunks) > 2:
+ int_hh = int(interval_chunks[0])
+ int_mm = int(interval_chunks[1])
+ int_ss = int(interval_chunks[2])
+ resample = (int_ss + 60 * int_mm + 3600 * int_hh) / resolution
+
+ # symaliases are the aliases for (internal) symbols requested by the user (ex. 't' for 'TEMP41372')
+ symaliases = filter( lambda x: x in symalias.keys(), form['symbols'].value.split( ':' ))
+ symbols_raw = [symalias.get( s, None ) for s in symaliases ] # get associated symbols
+ symbols = filter( lambda x: x not in ('DEWPOINT',), symbols_raw ) # strip out DEWPOINT
+ # get around the database problem of hanging on too many symbols
+ symbols1 = symbols[:]
+ symbols2 = []
+ if len(symbols) >= 4:
+ symbols1 = symbols[:4]
+ symbols2 = symbols[4:]
+
+
+
+
+ ###################################################################
+ # DB OPS
+ #
+ con = db.connect("dbname='%s' user='%s' host='%s' password='%s'" % DB)
+
+ dataset1 = get_slice(con, begin, end, resample, resolution, *symbols1)
+ if symbols2:
+ dataset2 = get_slice(con, begin, end, resample, resolution, *symbols2)
+
+ # dewpoint needs a little help...
+
+ if 'DEWPOINT' in symbols_raw:
+ dewdata_in = get_slice(con, begin, end, resample, resolution, 'TEMP41372', 'RH41372')
+ #print "dataset1: %s, dewdata_in: %s" % (len(dataset1), len(dewdata_in)) #DEBUG
+ dewdata = [dewpoint(temp,normalize_rh(relhum)) for (timestamp, temp, relhum) in dewdata_in]
+
+ con.close()
+ #
+ # END DB OPS
+ ###################################################################
+
+
+
+
+ cgiheader()
+
+
+ # print column names
+ symstr= (separator or ' ').join(('YYYY-MM-DD','hh:mm:ss'))
+ if altts:
+ symstr= (separator or ' ').join(('YYYY','MM','DD','hh','mm','ss'))
+ for s in symaliases:
+ if separator:
+ symstr += "%s%s" % (separator, s)
+ else:
+ symstr += " %+7.7s" % s
+ print symstr
+
+ # print data
+ foldindex = 0 # index into fold-in (computed) arrays, such as dewpoint
+ for row in dataset1:
+ outstr = ''
+ date = row[0].strftime('%Y-%m-%d')
+ time = row[0].strftime('%H:%M:%S')
+ if altts: # replace dashes and colons with spaces
+ outstr = (separator or ' ').join(tuple(date.split('-') + time.split(':')))
+ else: # don't
+ outstr = "%s%s%s" % (date, separator or ' ', time)
+
+ for s in symbols_raw:
+ if s == 'DEWPOINT':
+ if foldindex < len(dewdata):
+ data = dewdata[foldindex] # fold in the dewdata array, one element at a time
+ else:
+ data = 0
+ # again, reading from the second dataset, to deal with the database bug
+ elif s in symbols2:
+ data = dataset2[foldindex][ symbols2.index(s)+1 ]
+ else:
+ data = row[ symbols1.index(s)+1 ]
+ if s in ( 'RAIN380M', 'ACCURAIN' ):
+ # every tick is 1/100th of an inch, not 1/10'th of a mm as initially supposed.
+ data = linearConvert( data, factor=0.1, offset=0 )
+
+ if s is 'RH41372': # force RH to be no greater than 100
+ data = normalize_rh(data)
+
+ if separator:
+ format_str = '%s%0' + precision( s ) + 'f'
+ outstr += format_str % (separator,data)
+ else:
+ format_str = ' %7' + precision( s ) + 'f'
+ outstr += format_str % data
+ print outstr
+ foldindex += 1
+
+cgimain()
+##### If database down, comment out above line, uncomment the code below
+#cgiheader()
+#print "Sorry, RIG data app temporarily out of service"
+#print "Please email maciek@ssec.wisc.edu with any questions."
diff --git a/cgi-bin~/data_symbols.py b/cgi-bin~/data_symbols.py
new file mode 100644
index 0000000000000000000000000000000000000000..4142610c257b125ba901f1a2faec2a14ad9273e7
--- /dev/null
+++ b/cgi-bin~/data_symbols.py
@@ -0,0 +1,68 @@
+symalias = {
+'box_p' : 'CS105',
+'box_pressure' : 'CS105',
+'box_rh' : 'CS10162',
+'box_relative_humidity' : 'CS10162',
+'spd' : 'WSPD05305',
+'speed' : 'WSPD05305',
+'dir' : 'WDIR05305',
+'direction' : 'WDIR05305',
+'rh' : 'RH41372',
+'relative_humidity' : 'RH41372',
+'t' : 'TEMP41372',
+'temperature' : 'TEMP41372',
+'flux' : 'LI200X',
+'average_solar_radiation_flux' : 'LI200X',
+'precip' : 'RAIN380M',
+'precipitation' : 'RAIN380M',
+'accum_precip' : 'ACCURAIN',
+'accumulated_precipitation' : 'ACCURAIN',
+'box_t' : 'TEMP107_1',
+'box_temperature' : 'TEMP107_1',
+'outside_box_t' : 'TEMP107_2',
+'outside_box_temperature' : 'TEMP107_2',
+'t_6.3m' : 'TEMP107_3',
+'temperature_6.3_meter' : 'TEMP107_3',
+'t_14.5m' : 'TEMP107_4',
+'temperature_14.5_meter' : 'TEMP107_4',
+'t_unk' : 'TEMP107_5',
+'temperature_unknown' : 'TEMP107_5',
+'td' : 'DEWPOINT',
+'dewpoint' : 'DEWPOINT',
+'dew_point' : 'DEWPOINT',
+'p' : 'PAROSCI',
+'pres' : 'PAROSCI',
+'pressure' : 'PAROSCI',
+}
+
+
+temp_precision = '.1'
+rh_precision = '.0'
+press_precision = '.1'
+windspd_precision = '.1'
+winddir_precision = '.0'
+solarrad_precision = '.1'
+default_precision = '.3'
+rain_precision = '.2'
+ceil_precision = '.0'
+
+def precision(symbol):
+ if symbol in ('CS105', 'PAROSCI'):
+ return press_precision
+ elif symbol in ('CS10162', 'RH41372'):
+ return rh_precision
+ elif symbol in ('TEMP41372', 'TEMP107_1', 'TEMP107_2', 'TEMP107_3', 'TEMP107_4', 'TEMP107_5', 'DEWPOINT'):
+ return temp_precision
+ elif symbol == 'WSPD05305':
+ return windspd_precision
+ elif symbol == 'WDIR05305':
+ return winddir_precision
+ elif symbol == 'LI200X':
+ return solarrad_precision
+ elif symbol in ('RAIN380M', 'ACCURAIN'):
+ return rain_precision
+ else:
+ return default_precision
+
+
+
diff --git a/cgi-bin~/data_usage.py b/cgi-bin~/data_usage.py
new file mode 100644
index 0000000000000000000000000000000000000000..a36f9db7ce75e5cc056dacdfb5bef4de7be388d6
--- /dev/null
+++ b/cgi-bin~/data_usage.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python2
+# usage for data_data.py
+
+
+def usage():
+ print """
+<html><body>
+<em>
+Usage: data_data.py?symbols=sym_1:sym_2:sym_3:...:sym_n&begin=start_timestamp&end=end_timestamp&interval=hh:mm:ss
+</em><br>
+<br>
+ example invocation:<br>
+<br>
+ <a href=\"data_data.py?begin=2003-03-27+14:05:57&end=2003-03-27+15:05:57&symbols=t:p:speed:dir\">
+ <code>data_data.py?begin=2003-03-27+14:05:57&end=2003-03-27+15:05:57&symbols=t:p:speed:dir</code></a><br>
+<br>
+ if no end value provided, assume end = now. For example<br>
+(to use this, replace below timestamp with one nearer to the present date - else you'll get a lot of data):<br>
+<br>
+ <code>data_data.py?begin=2003-03-27+14:05:57&symbols=t:p:speed:dir</code><br>
+<br>
+ if no begin is provided, assume it is the same as end (return one value only). Example:<br>
+<br>
+ <a href=\"data_data.py?end=2003-03-27+15:05:57&symbols=t:p:dewpoint:accum_precip\">
+ <code>data_data.py?end=2003-03-27+15:05:57&symbols=t:p:dewpoint:accum_precip</code></a><br>
+<br>
+ thus, calling with no begin OR end gives the single most recent sample.<br>
+<br>
+<i>NEW as of 2006-07-26:</i>
+If no end is provided, but begin is of the form -hh:mm:ss, then the most recent hh hours, mm minutes
+and ss seconds of data are provided.
+<br>
+
+------------- Other Options --------------------------<br>
+
+To separate the fields in the timestamp with spaces (useful for some automated scripts),
+append <code>altts=1</code> to the CGI parameter list as follows:<br>
+<br>
+ <a href=\"data_data.py?begin=2003-04-08+23:05:57&end=2003-04-09+01:15:57&symbols=t:rp:dewpoint:accum_precip&altts=1\">
+ <code>data_data.py?begin=2003-04-08+23:05:57&end=2003-04-09+01:15:57&symbols=t:rp:dewpoint:accum_precip&altts=1</code></a><br>
+<br>
+<br>
+
+To separate the datafields with various characters (commas, spaces, etc) as opposed to constant-width
+column output,
+append <code>separator=,</code> (or <code>separator=+</code>, etc..) to the CGI parameter list as follows:<br>
+<br>
+ <a href=\"data_data.py?begin=2003-04-08+23:05:57&end=2003-04-09+01:15:57&symbols=t:rp:dewpoint:accum_precip&separator=,\">
+ <code>data_data.py?begin=2003-04-08+23:05:57&end=2003-04-09+01:15:57&symbols=t:rp:dewpoint:accum_precip&separator=,</code></a><br>
+<br>
+<br>
+It's also possible to get data at intervals greater than five seconds.
+To do this, specify the interval time as one of<br>
+<ul>
+ <li><code>interval=ss</code></li>
+ <li><code>interval=mm:ss</code></li>
+ <li><code>interval=hh:mm:ss</code></li>
+</ul><br>
+<br>
+<em>For correct operation, only specify intervals which are multiples of 5 seconds</em>. \
+For example:</br>
+<br>
+ <a href=\"data_data.py?begin=2003-03-27+14:05:57&end=2003-03-27+15:05:57&symbols=t:p:speed:dir&interval=1:00\">
+ <code>data_data.py?begin=2003-03-27+14:05:57&end=2003-03-27+15:05:57&symbols=t:p:speed:dir&interval=1:00</code></a><br>
+<br>
+will give you one sample every minute.
+<br>
+<br>
+
+<em>
+A list of requested symbols (click on symbol to get latest value):<br>
+</em>
+<br>
+------------- Standard Parameters --------------------<br>
+* Temperature [deg C] (
+ <a href="data_data.py?symbols=temperature">temperature</a> or
+ <a href="data_data.py?symbols=t">t</a>)<br>
+* Dew Point Temperature [deg C] (
+ <a href="data_data.py?symbols=dewpoint">dewpoint</a> or
+ <a href="data_data.py?symbols=dew_point">dew_point</a> or
+ <a href="data_data.py?symbols=td">td</a>)<br>
+* Wind Direction [degrees] (
+ <a href="data_data.py?symbols=direction">direction</a> or
+ <a href="data_data.py?symbols=dir">dir</a>)<br>
+* Wind Speed [mps] (
+ <a href="data_data.py?symbols=speed">speed</a> or
+ <a href="data_data.py?symbols=spd">spd</a>)<br>
+* Pressure [hPa] (
+ <a href="data_data.py?symbols=pressure">pressure</a> or
+ <a href="data_data.py?symbols=pres">pres</a> or
+ <a href="data_data.py?symbols=p">p</a>)<br>
+* Relative Humidity [percent] (
+ <a href="data_data.py?symbols=relative_humidity">relative_humidity</a> or
+ <a href="data_data.py?symbols=rh">rh</a>)<br>
+* Precipitation [inches accumulated in last 5 seconds - thus not really useful] (
+ <a href="data_data.py?symbols=precipitation">precipitation</a> or
+ <a href="data_data.py?symbols=precip">precip</a>)<br>
+* Accumlated Precipitation [inches accumulated since midnight] (
+ <a href="data_data.py?symbols=accumulated_precipitation">accumulated_precipitation</a> or
+ <a href="data_data.py?symbols=accum_precip">accum_precip</a>)<br>
+* Solar radiation [W / m^2] (
+ <a href="data_data.py?symbols=average_solar_radiation_flux">average_solar_radiation_flux</a> or
+ <a href="data_data.py?symbols=flux">flux</a>)<br>
+<br>
+---------------- Other temperatures ---------------------<br>
+* Temperature at 6.3m [deg C] (
+ <a href="data_data.py?symbols=temperature_6.3m">temperature_6.3m</a> or
+ <a href="data_data.py?symbols=t_6.3m">t_6.3m</a>)<br>
+* Temperature at 14.5m [deg C] (
+ <a href="data_data.py?symbols=temperature_14.5m">temperature_14.5m</a> or
+ <a href="data_data.py?symbols=t_14.5m">t_14.5m</a>)<br>
+* Temperature at (unknown) [deg C] (
+ <a href="data_data.py?symbols=temperature_unknown">temperature_unknown</a> or
+ <a href="data_data.py?symbols=t_unk">t_unk</a>)<br>
+<br>
+---------------- Box Values --------------------<br>
+* Temperature of instrument box [deg C] (
+ <a href="data_data.py?symbols=box_temperature">box_temperature</a> or
+ <a href="data_data.py?symbols=box_t">box_t</a>)<br>
+* pressure of the box [hPa] (
+ <a href="data_data.py?symbols=box_pressure">box_pressure</a> or
+ <a href="data_data.py?symbols=box_p">box_p</a>)<br>
+* Temperature outside box [deg C] (
+ <a href="data_data.py?symbols=outside_box_t">outside_box_t</a>)<br>
+* Box Relative Humidity [percent] (
+ <a href="data_data.py?symbols=box_relative_humidity">box_relative_humidity</a> or
+ <a href="data_data.py?symbols=box_rh">box_rh</a>)<br>
+<br>
+<br>
+</body></html>
+"""
diff --git a/cgi-bin~/dbget2.py b/cgi-bin~/dbget2.py
new file mode 100644
index 0000000000000000000000000000000000000000..45368fce4f9d4871c013f3b06d793dd83b25ccea
--- /dev/null
+++ b/cgi-bin~/dbget2.py
@@ -0,0 +1,222 @@
+# routines to access data from the penthouse database
+
+DB_SERVER = 'bora2.ssec.wisc.edu'
+DB_NAME = 'rig'
+DB_USER = 'rig'
+
+METEOROLOGY_GROUP_ID=-1
+ENGINEERING_GROUP_ID=-2
+
+import pgdb
+import re
+
+# make a connection to the database
+def connect():
+ return pgdb.connect('%s:%s:%s' % (DB_SERVER, DB_NAME, DB_USER))
+
+# close the connection
+def disconnect(con):
+ con.close()
+
+# get a dataset
+def get_dataset(symbol, res, start, end):
+
+ con = connect()
+ c = con.cursor()
+ c.execute("SELECT symbol_id FROM symbols WHERE name='%s'" % symbol)
+ symbol_id = c.fetchone()[0]
+ c.execute("SELECT stamp, value " +
+ "FROM data NATURAL JOIN samples " +
+ ("WHERE symbol_id=%d " % symbol_id) +
+ ("AND stamp >= '%s' AND stamp <= '%s' " % (start, end)) +
+ ("AND resolution=%d") % res)
+ tmp = c.fetchall()
+ disconnect(con)
+
+ return tmp
+
+# get a daraset with the specified units (not checked for errors)
+def get_dataset_with_units(symbol, res, start, end, units):
+
+ con = connect()
+ c = con.cursor()
+ c.execute("SELECT symbol_id FROM symbols WHERE name='%s'" % symbol)
+ symbol_id = c.fetchone()[0]
+ c.execute(("SELECT stamp, convert_units(value, '%s') " % units) +
+ "FROM data NATURAL JOIN samples " +
+ ("WHERE symbol_id=%d " % symbol_id) +
+ ("AND stamp >= '%s' AND stamp <= '%s' " % (start, end)) +
+ ("AND resolution=%d") % res)
+ tmp = c.fetchall()
+ disconnect(con)
+
+ return tmp
+
+
+# makes a text file from a dataset - ready for gnuplot
+def makefile(dataset, filename):
+
+ f = open(filename, 'w')
+
+ #dataset = map(lambda x: (x[0][0:x[0].index('.')], x[1]), dataset)
+ for row in dataset:
+ f.write(row[0] + ' ' + str(row[1]) + '\n')
+
+ return (dataset[0][0], dataset[-1][0])
+
+# returns data for the specified symbol
+def get_symbol_data(symbol):
+
+ sym_dict = {}
+
+ con = connect()
+ c = con.cursor()
+ c.execute("SELECT * FROM symbols WHERE name='%s'" % symbol)
+ vals = c.fetchone()
+ for i in range(len(c.description)):
+ sym_dict[c.description[i][0]] = vals[i]
+
+ disconnect(con)
+
+ return sym_dict
+
+# gets all symbol data in a dictionary keyed by name
+# dictionary entries are: (id, long name, units, description)
+def get_all_symbol_data():
+
+ sym_data = {}
+
+ con = connect()
+ c = con.cursor()
+ c.execute("SELECT name, symbol_id, long_name, units, description " +
+ "FROM symbols")
+ for row in c.fetchall():
+ sym_data[row[0]] = tuple(row[1:])
+
+ disconnect(con)
+
+ return sym_data
+
+# returns the names of the symbols
+def get_symbol_names():
+
+ sym_list = []
+
+ con = connect()
+ c = con.cursor()
+ c.execute("SELECT s.name FROM symbols s, group_items g " +
+ " WHERE g.symbol_id=s.symbol_id AND g.group_id > -3" +
+ " ORDER BY g.group_id DESC, g.list_order")
+ for l in c.fetchall():
+ if l[0] != "TIME":
+ sym_list.append(l[0])
+
+ disconnect(con)
+
+ return sym_list
+
+# returns long-name data
+def get_long_names():
+
+ names = {}
+
+ con = connect()
+ c = con.cursor()
+ c.execute("SELECT name, long_name FROM symbols")
+ for name, long_name in c.fetchall():
+ names[name] = long_name
+
+ disconnect(con)
+
+ return names
+
+# gets possible units for the given symbols
+def get_alternate_units(units):
+
+ con = connect()
+ c = con.cursor()
+ c.execute("SELECT type FROM units WHERE label='%s'" % units)
+ type = c.fetchone()[0]
+ c.execute("SELECT label FROM units WHERE type='%s'" % type)
+ tmp = map(lambda x: x[0], c.fetchall())
+
+ disconnect(con)
+
+ return tmp
+
+# returns the names of the symbols for meteorology
+def get_met_symbol_names():
+
+ return get_group_symbol_names(METEOROLOGY_GROUP_ID)
+
+# returns the names of the symbols for engineering sensors
+def get_eng_symbol_names():
+
+ return get_group_symbol_names(ENGINEERING_GROUP_ID)
+
+def get_group_symbol_names(gid):
+
+ sym_list = []
+
+ con = connect()
+ c = con.cursor()
+ c.execute("SELECT s.name FROM symbols s, group_items g " +
+ " WHERE g.symbol_id=s.symbol_id AND g.group_id=%d" % gid +
+ " ORDER BY g.list_order")
+ for l in c.fetchall():
+ if l[0] != "TIME":
+ sym_list.append(l[0])
+
+ disconnect(con)
+
+ return sym_list
+
+# returns units data
+def get_units():
+
+ units_dict = {}
+
+ con = connect()
+ c = con.cursor()
+ c.execute("SELECT name, units FROM symbols")
+ for name, units in c.fetchall():
+ units_dict[name] = units
+
+ disconnect(con)
+
+ return units_dict
+
+# returns current conditions tuple: (time_stamp, value_dict)
+def get_current_conds():
+
+ data = {}
+
+ con = connect()
+ c = con.cursor()
+ c.execute("SELECT sample_id, stamp FROM samples WHERE resolution=60 " +
+ "ORDER BY stamp DESC LIMIT 1")
+ sample_id, stamp = tuple(c.fetchone())
+ c.execute("SELECT symbol_id, value from data WHERE sample_id=%d" %
+ sample_id)
+ for row in c.fetchall():
+ data[row[0]] = row[1]
+
+ disconnect(con)
+
+ return (stamp, data)
+
+# returns a dictionary with global values
+def get_globals():
+
+ gd = {}
+
+ con = connect()
+ c = con.cursor()
+ c.execute('SELECT * FROM globals')
+ vals = c.fetchone()
+ for i in range(len(c.description)):
+ gd[c.description[i][0]] = vals[i]
+
+ disconnect(con)
+
+ return gd
diff --git a/cgi-bin~/dewpoint.py b/cgi-bin~/dewpoint.py
new file mode 100644
index 0000000000000000000000000000000000000000..a4ce2458845f729563ec69da0c24e9be8387807f
--- /dev/null
+++ b/cgi-bin~/dewpoint.py
@@ -0,0 +1,37 @@
+# Functions for calculating the dewpoint.
+# $Id: dewpoint.py,v 1.1 2003/04/16 19:38:48 maciek Exp $
+
+import math
+
+def dewpoint(tempC, relhum):
+ """ algorithm from Tom Whittaker
+tempC is the temperature in degrees Celsius,
+relhum is the relative humidity as a percentage"""
+ gasconst = 461.5
+ latheat = 2500800.0
+
+ dp = 1.0 / ( 1.0 / ( 273.15 + tempC ) - gasconst * math.log( (0.0 + relhum) / 100 ) / \
+ ( latheat - tempC * 2397.5 ))
+
+ return min(dp - 273.15, tempC)
+
+def awips_dewpoint(tempC, relhum):
+ """ algorithm taken from http://meted.ucar.edu/awips/validate/dewpnt.htm
+tempC is the temperature in degrees Celsius,
+relhum is the relative humidity as a percentage"""
+ C15 = 26.66082
+ C1 = 0.0091379024
+ C2 = 6106.396
+ C3 = 223.1986
+ C4 = 0.0182758048
+
+ t = tempC + 273.15 # convert temperature to Kelvin
+ rh = relhum / 100 # convert relative humidity to ratio
+
+ es = math.exp( C15 - C1 * t - C2 / t ) # saturation vapor pressure
+ e = rh * es
+ b = C15 - math.log(e)
+ td = ( b - math.sqrt( b * b - C3 ) ) / C4
+
+ return min(td - 273.15, tempC)
+
diff --git a/cgi-bin~/iqeye/ls.cgi b/cgi-bin~/iqeye/ls.cgi
new file mode 100755
index 0000000000000000000000000000000000000000..869b2470d3554155a9cc6cabd365cbb573691afa
--- /dev/null
+++ b/cgi-bin~/iqeye/ls.cgi
@@ -0,0 +1,123 @@
+#!/var/www/wsgi/python-env/metobs/bin/python
+"""List available EyeQ Camera images.
+
+See `print_usage()` for more information
+"""
+
+import cgi
+import cgitb; cgitb.enable()
+
+import os
+import sys
+from datetime import datetime
+from datetime import timedelta
+
+from metobs import mytime
+
+form = cgi.FieldStorage()
+
+def get(name, default=None):
+ if form.has_key(name):
+ return form[name].value
+ return default
+
+def print_usage():
+ print "ContentType: text/plain"
+ print
+ print """Provides image listings of SSEC RIG EyeQ Camera images.
+
+Because the images may not be exactly spaced, being that processing may increase
+the interval, images are listed by searching between the date provided and that
+date plus the image resolution.
+
+Parameters
+==========
+
+cam - (required) Camera name. West, East, etc ...
+d - Date of image in CST (YYYY-MM-DD HH:MM:SS). Defaults to current time.
+"""
+
+#: resolution at which images are generated
+image_rez = 10
+#: timezone for image filename times
+image_tz = mytime.OffsetTimezone(6)
+hostname = 'tahiti.ssec.wisc.edu'
+
+#: convert a file path to a server path`
+url_for = lambda fpth: "http://"+hostname+fpth.replace("/beach", "/pub")
+
+def image_path(cam, dt):
+ """Get a path for an image on disk.
+
+ :param cam: Name of the camera
+ :param dt: datetime of the image file used to parse the name
+ """
+ basedir = '/beach/incoming/Instrument_Data/METOBS/RIG/Cameras'
+ return os.path.join(basedir, cam + dt.strftime('/%Y/%m/%d/%H_%M_%S.trig+00.jpg'))
+
+
+
+#
+#
+# You shouldn't need to change anything below here
+#
+#
+
+
+def redirect_to(url):
+ print "Status: 302 Moved"
+ print "Location: " + url
+ print
+
+def file_not_found():
+ print "Status: 404 File Not Found"
+ print
+
+def headers(typ='plain'):
+ print "ContentType: text/" + typ
+ print
+
+def main():
+
+ # required parameter
+ camera = get('cam')
+ if not camera:
+ print_usage()
+ return 1
+
+ # parse data from parameter, defaulting to current time in the
+ # timezone specified
+ date = get('d')
+ if not date:
+ date = mytime.utc_now()
+ date = mytime.set_tz(date, tz=image_tz)
+ else:
+ date = mytime.parse_stamp(date)
+ date = mytime.set_tz(date, tz=image_tz)
+
+ # images may not be exactly the same interval appart, therefore we have
+ # to attempt to locate the image
+ url = ""
+ pths = []
+ for i in range(image_rez):
+ fpth = image_path(camera, date + timedelta(seconds=i))
+ pths.append(fpth)
+ if os.path.exists(fpth):
+ url = url_for(fpth)
+ break
+
+ # successfully found an image
+ if url:
+ redirect_to(url)
+
+ if form.has_key('debug'):
+ headers()
+ for pth in pths:
+ print pth
+ return 1
+
+ # we should only get here if the file was not found
+ file_not_found()
+ return 1
+
+sys.exit(main())
diff --git a/cgi-bin~/symbols.py b/cgi-bin~/symbols.py
new file mode 100644
index 0000000000000000000000000000000000000000..2e390ecc219f3dbf943753bed6f7b847cc62122c
--- /dev/null
+++ b/cgi-bin~/symbols.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python2
+#
+# Creates the SYMBOLS file which is used by the graphing applet
+
+import re
+import pgdb
+
+DB_SERVER = 'bora2.ssec.wisc.edu'
+DB_NAME = 'rig'
+DB_USER = 'rig'
+OUTPUT_FILE = '/home/brucef/public_html/rig_old/SYMBOLS'
+
+if __name__ == '__main__':
+
+ output = open(OUTPUT_FILE, 'w')
+ con = pgdb.connect('%s:%s:%s' % (DB_SERVER, DB_NAME, DB_USER))
+ c = con.cursor()
+ c.execute("SELECT name, long_name, units FROM symbol")
+ for l in c.fetchall():
+
+ # symbol name
+ output.write(l[0] + ' ')
+
+ # long name
+ output.write(re.sub(' ', '+', l[1]) + ' ')
+
+ # units
+ output.write(re.sub(' ', '+', l[2]) + '\n')
+
diff --git a/cgi-bin~/test b/cgi-bin~/test
new file mode 100755
index 0000000000000000000000000000000000000000..5d407129226dcf048cb0595518f214e728558e7d
--- /dev/null
+++ b/cgi-bin~/test
@@ -0,0 +1,3 @@
+#!/bin/ksh
+
+echo "hello world"
diff --git a/cgi-bin~/tower/mobile.cgi b/cgi-bin~/tower/mobile.cgi
new file mode 100755
index 0000000000000000000000000000000000000000..41f9f4a221dae97d9c676cae1b81a65f62e417d4
--- /dev/null
+++ b/cgi-bin~/tower/mobile.cgi
@@ -0,0 +1,88 @@
+#!/var/www/wsgi/python-env/metobs/bin/python
+
+import cgi
+# FOR DEBUG ONLY !!!!!
+#import cgitb; cgitb.enable()
+
+import sqlalchemy as sa
+from sqlalchemy import orm
+from metobs.data import *
+from metobs import db, mytime
+
+TXT = """ UW Lake Mendota Buoy
+ ====================
+ As of: %(stamp)s
+
+ Atmosphere
+ -----------
+ Wind Dir: %(dir)d deg (%(dir2)s)
+ Wind Spd: %(spd).1f m/s (%(spd2).1f knts)
+ Air Temp: %(temp).1f degC (%(temp2).1f degF)
+
+ Water Temperature
+ -----------------
+ Surface %(surface).1f degC (%(surface2).1f degF)
+ -1m %(wt1).1f degC (%(wt1-2).1f degF)
+ -5m %(wt5).1f degC (%(wt5-2).1f degF)
+ -10m %(wt10).1f degC (%(wt10-2).1f degF)
+ -15m %(wt15).1f degC (%(wt15-2).1f degF)
+ -20m %(wt20).1f degC (%(wt20-2).1f degF)
+"""
+
+ERR_TXT = """
+ An Error Occured: Request could not be completed
+"""
+
+symbols = [
+ 'WIND_DIRECTION_2.0',
+ 'WIND_SPEED_2.0',
+ 'AIR_TEMP',
+ 'WATER_TEMP_0.0',
+ 'WATER_TEMP_1.0',
+ 'WATER_TEMP_5.0',
+ 'WATER_TEMP_10.0',
+ 'WATER_TEMP_15.0',
+ 'WATER_TEMP_20.0']
+
+def main():
+ form = cgi.FieldStorage()
+
+ try:
+ eng = sa.create_engine('postgres://buoy:vyit@tahiti.ssec.wisc.edu/buoy')
+ session = orm.create_session(bind=eng)
+ db.init_model()
+
+ station = session.query(db.Station, db.Station.name=='Mendota Buoy').one()[0]
+ tmp_syms = dict((s.name, s) for s in station.symbols)
+ qry_syms = [tmp_syms[s] for s in symbols]
+ stamp = db.max_stamp(session, station)
+ print stamp
+
+ data = db.get_slice(session, station, stamp, symbols=qry_syms)[1][-1]
+ stamp = stamp.astimezone(mytime.OffsetTimezone(6))
+
+ txt = TXT % {
+ 'stamp':stamp.strftime('%Y-%m-%d %H:%M:%S CST'),
+ 'dir':data[0], 'dir2':dir2txt(data[0]),
+ 'spd':data[1], 'spd2':mps2knots(data[1]),
+ 'temp':data[2], 'temp2':c2f(data[2]),
+ 'surface':data[3], 'surface2':c2f(data[3]),
+ 'wt1':data[4], 'wt1-2':c2f(data[4]),
+ 'wt5':data[5], 'wt5-2':c2f(data[5]),
+ 'wt10':data[6], 'wt10-2':c2f(data[6]),
+ 'wt15':data[7], 'wt15-2':c2f(data[7]),
+ 'wt20':data[8], 'wt20-2':c2f(data[8])}
+ except Exception, e:
+ txt = ERR_TXT
+ raise e
+
+ print "Content-Type: text/plain; charset=UTF-8"
+ print "Refresh: 120"
+ print "Cache-control: private, no-cache"
+ print "Expires: Thu, 1 Aug 2002 12:23:00 GMT"
+ print "Pragma: no-cache"
+ print
+ print txt
+
+if __name__ == '__main__':
+ main()