Revision 45c4b73a
Added by dsorber about 13 years ago
| 525.743/code/web/README | ||
|---|---|---|
|
1. Install apache and mod_wsgi.
|
||
|
2. Add this line to your apache config file
|
||
|
|
||
|
WSGIScriptAlias /bcfs /path/to/web/folder/app.wsgi
|
||
|
|
||
|
3. Restart apache and you should be able to see BCFS web display at "/bcfs".
|
||
| 525.743/code/web/app.wsgi | ||
|---|---|---|
|
import sqlite3
|
||
|
|
||
|
from genshi.template import TemplateLoader
|
||
|
from genshi.template.base import Context
|
||
|
|
||
|
DB_PATH = '/home/dsorber/bcfs/beer_temps.db'
|
||
|
|
||
|
# It accepts two arguments:
|
||
|
# environ points to a dictionary containing CGI like environment variables
|
||
|
# which is filled by the server for each received request from the client
|
||
|
# start_response is a callback function supplied by the server
|
||
|
# which will be used to send the HTTP status and headers to the server
|
||
|
|
||
|
def application(environ, start_response):
|
||
|
|
||
|
loader = TemplateLoader('/home/dsorber/bcfs/templates',
|
||
|
auto_reload=True)
|
||
|
|
||
|
# Testing purposes only -- REMOVE BEFORE SHIPPING
|
||
|
response_body = 'The environ dict: %s' % environ['PATH_INFO']
|
||
|
|
||
|
if not environ['PATH_INFO']:
|
||
|
# Load the index template
|
||
|
template = loader.load('index.html')
|
||
|
|
||
|
# Create the response args
|
||
|
response_args = {'title': 'Beer Fermentation Control System'}
|
||
|
|
||
|
# Open up the connection to the database, execute the query,
|
||
|
# grab the results, then close the DB connection.
|
||
|
db_conn = sqlite3.connect(DB_PATH)
|
||
|
db = db_conn.cursor()
|
||
|
db.execute("select strftime('%m/%d/%Y', date(ts, 'unixepoch')), "
|
||
|
" count(ts), avg(temp1), max(temp1), min(temp1),"
|
||
|
" avg(temp2), max(temp2), min(temp2), avg(temp3),"
|
||
|
" max(temp3), min(temp3), avg(temp4), max(temp4),"
|
||
|
" min(temp4)"
|
||
|
"from temp_data "
|
||
|
"group by date(ts, 'unixepoch')")
|
||
|
response_args['results'] = db.fetchall()
|
||
|
db_conn.close()
|
||
|
|
||
|
# Generate the stream and then render it to HTML
|
||
|
stream = template.generate(**response_args)
|
||
|
response_body = stream.render('html', doctype='html')
|
||
|
|
||
|
# Set the appropriate response headers
|
||
|
response_headers = [('Content-Type', 'text/html'),
|
||
|
('Content-Length', str(len(response_body)))]
|
||
|
|
||
|
elif environ['PATH_INFO'].startswith('/day/'):
|
||
|
# Load the day template
|
||
|
template = loader.load('day.html')
|
||
|
|
||
|
day = environ['PATH_INFO'][5:7]
|
||
|
month = environ['PATH_INFO'][7:9]
|
||
|
year = environ['PATH_INFO'][9:13]
|
||
|
|
||
|
date = '%s/%s/%s' % (month, day, year)
|
||
|
|
||
|
# Create the response args
|
||
|
response_args = {'title': 'Sensor Readings for %s' % date}
|
||
|
|
||
|
# Open up the connection to the database, execute the query,
|
||
|
# grab the results, then close the DB connection.
|
||
|
db_conn = sqlite3.connect(DB_PATH)
|
||
|
db = db_conn.cursor()
|
||
|
db.execute("select strftime('%m/%d/%Y %H:%M:%S', ts, 'unixepoch'), "
|
||
|
" temp1, temp2, temp3, temp4, relay_status "
|
||
|
"from temp_data "
|
||
|
"where date(ts, 'unixepoch') is date('2013-04-22')"
|
||
|
"order by ts desc")
|
||
|
response_args['results'] = db.fetchall()
|
||
|
db_conn.close()
|
||
|
|
||
|
# Generate the stream and then render it to HTML
|
||
|
stream = template.generate(**response_args)
|
||
|
response_body = stream.render('html', doctype='html')
|
||
|
|
||
|
# Set the appropriate response headers
|
||
|
response_headers = [('Content-Type', 'text/html'),
|
||
|
('Content-Length', str(len(response_body)))]
|
||
|
else:
|
||
|
# User supplied an invalid URL
|
||
|
response_body = 'Invalid URL "%s"' % environ['PATH_INFO']
|
||
|
response_headers = [('Content-Type', 'text/plain'),
|
||
|
('Content-Length', str(len(response_body)))]
|
||
|
|
||
|
# HTTP response code and message
|
||
|
status = '200 OK'
|
||
|
|
||
|
# Send the status and response headers to the server
|
||
|
start_response(status, response_headers)
|
||
|
|
||
|
# Return the response body. (Notice it is wrapped in a list although it
|
||
|
# could be any iterable.)
|
||
|
return [response_body]
|
||
| 525.743/code/web/templates/day.html | ||
|---|---|---|
|
<html xmlns="http://www.w3.org/1999/xhtml"
|
||
|
xmlns:py="http://genshi.edgewall.org/">
|
||
|
<head>
|
||
|
<title>Beer Fermenation Control System</title>
|
||
|
<style type="text/css">
|
||
|
body {font-family: sans-serif;
|
||
|
font-size: 16px}
|
||
|
h3 {font-size: 24px;}
|
||
|
table {border: 1px solid #000;
|
||
|
border-spacing: 0px;}
|
||
|
table a:link {color: #000;
|
||
|
text-decoration: none;}
|
||
|
table a:visited {color: #000;
|
||
|
text-decoration: none;}
|
||
|
table a:hover {color: #000;
|
||
|
text-decoration: underline;}
|
||
|
tr.even {background-color: #FFF;}
|
||
|
tr.odd {background-color: #FFC;}
|
||
|
th {border: 1px solid #000;
|
||
|
padding: 10px;
|
||
|
border-spacing: 2px;}
|
||
|
td {border-top: 1px solid #000;
|
||
|
padding: 2px 5px;}
|
||
|
.center {text-align: center;}
|
||
|
.sensor1 {background-color: #FC0;}
|
||
|
.sensor2 {background-color: #C3F;}
|
||
|
.sensor3 {background-color: #0C0;}
|
||
|
.sensor4 {background-color: #F60;}
|
||
|
.avg {background-color: #AAA;}
|
||
|
.max {background-color: #CCC;}
|
||
|
.min {background-color: #EEE;}
|
||
|
.relay_on {font-weight: bold;
|
||
|
color: #0C0;}
|
||
|
</style>
|
||
|
</head>
|
||
|
<body>
|
||
|
<h1>${title}</h1>
|
||
|
<table>
|
||
|
<tr>
|
||
|
<th>Datetime</th>
|
||
|
<th class="sensor1">Sensor 1</th>
|
||
|
<th class="sensor2">Sensor 2</th>
|
||
|
<th class="sensor3">Sensor 3</th>
|
||
|
<th class="sensor4">Sensor 4</th>
|
||
|
<th class="max">Relay Status</th>
|
||
|
</tr>
|
||
|
<tr py:for="ctr, row in enumerate(results)" class="${ctr % 2 and 'odd' or 'even'}">
|
||
|
<td><a href="bcfs/day/${row[0][3:5] + row[0][0:2] + row[0][6:10]}">${row[0]}</a></td>
|
||
|
<td class="center">${row[1]}</td>
|
||
|
<td class="center">${row[2]}</td>
|
||
|
<td class="center">${row[3]}</td>
|
||
|
<td class="center">${row[4]}</td>
|
||
|
<td class="center ${row[5] == 1 and 'relay_on' or ''}">${int(row[5]) and 'on' or 'off'}</td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
<br />
|
||
|
<a href="/bcfs">Back</a>
|
||
|
</body>
|
||
|
</html>
|
||
| 525.743/code/web/templates/index.html | ||
|---|---|---|
|
<html xmlns="http://www.w3.org/1999/xhtml"
|
||
|
xmlns:py="http://genshi.edgewall.org/">
|
||
|
<head>
|
||
|
<title>Beer Fermenation Control System</title>
|
||
|
<style type="text/css">
|
||
|
body {font-family: sans-serif;
|
||
|
font-size: 16px}
|
||
|
h3 {font-size: 24px;}
|
||
|
table {border: 1px solid #000;
|
||
|
border-spacing: 0px;}
|
||
|
table a:link {color: #000;
|
||
|
text-decoration: none;}
|
||
|
table a:visited {color: #000;
|
||
|
text-decoration: none;}
|
||
|
table a:hover {color: #000;
|
||
|
text-decoration: underline;}
|
||
|
tr.even {background-color: #FFF;}
|
||
|
tr.odd {background-color: #FFC;}
|
||
|
th {border: 1px solid #000;
|
||
|
padding: 10px;
|
||
|
border-spacing: 2px;}
|
||
|
td {border-top: 1px solid #000;
|
||
|
padding: 2px 5px;}
|
||
|
.center {text-align: center;}
|
||
|
.sensor1 {background-color: #FC0;}
|
||
|
.sensor2 {background-color: #C3F;}
|
||
|
.sensor3 {background-color: #0C0;}
|
||
|
.sensor4 {background-color: #F60;}
|
||
|
.avg {background-color: #AAA;}
|
||
|
.max {background-color: #CCC;}
|
||
|
.min {background-color: #EEE;}
|
||
|
</style>
|
||
|
</head>
|
||
|
<body>
|
||
|
<h1>${title}</h1>
|
||
|
<h3>Sensor Reading Summary</h3>
|
||
|
<table>
|
||
|
<tr>
|
||
|
<th colspan="2"> </th>
|
||
|
<th colspan="3" class="sensor1">Sensor 1</th>
|
||
|
<th colspan="3" class="sensor2">Sensor 2</th>
|
||
|
<th colspan="3" class="sensor3">Sensor 3</th>
|
||
|
<th colspan="3" class="sensor4">Sensor 4</th>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<th>Date</th>
|
||
|
<th>Readings</th>
|
||
|
<th class="avg">Avg</th>
|
||
|
<th class="max">Max</th>
|
||
|
<th class="min">Min</th>
|
||
|
<th class="avg">Avg</th>
|
||
|
<th class="max">Max</th>
|
||
|
<th class="min">Min</th>
|
||
|
<th class="avg">Avg</th>
|
||
|
<th class="max">Max</th>
|
||
|
<th class="min">Min</th>
|
||
|
<th class="avg">Avg</th>
|
||
|
<th class="max">Max</th>
|
||
|
<th class="min">Min</th>
|
||
|
</tr>
|
||
|
<tr py:for="ctr, row in enumerate(results)" class="${ctr % 2 and 'odd' or 'even'}">
|
||
|
<td><a href="bcfs/day/${row[0][3:5] + row[0][0:2] + row[0][6:10]}">${row[0]}</a></td>
|
||
|
<td class="center">${row[1]}</td>
|
||
|
<td class="center">${round(float(row[2]), 3)}</td>
|
||
|
<td class="center">${row[3]}</td>
|
||
|
<td class="center">${row[4]}</td>
|
||
|
|
||
|
<td class="center">${round(float(row[5]), 3)}</td>
|
||
|
<td class="center">${row[6]}</td>
|
||
|
<td class="center">${row[7]}</td>
|
||
|
|
||
|
<td class="center">${round(float(row[8]), 3)}</td>
|
||
|
<td class="center">${row[9]}</td>
|
||
|
<td class="center">${row[10]}</td>
|
||
|
|
||
|
<td class="center">${round(float(row[11]), 3)}</td>
|
||
|
<td class="center">${row[12]}</td>
|
||
|
<td class="center">${row[13]}</td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
|
||
|
</body>
|
||
|
</html>
|
||
Adding web remote display code. All the basic functionality is complete but I'd like to play around with adding some graphs for S&G.