Skip to content

Instantly share code, notes, and snippets.

@piotrmaslanka
Created July 22, 2013 13:33
Show Gist options
  • Save piotrmaslanka/6053838 to your computer and use it in GitHub Desktop.
Save piotrmaslanka/6053838 to your computer and use it in GitHub Desktop.
A sample service for eunike. Accepts messages via HTTP POST request. Provides statistical info upon GET requests.
# coding=UTF-8
from __future__ import division
import json, sys, os.path
import datetime
import sqlite3
import BaseHTTPServer, cgi
import time
import hashlib
PRESENTPATH = u'''<!DOCTYPE html>
<html>
<head><title>Raport SMS</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">
* { font-family: Helvetica, arial, freesans, clean, sans-serif; }
h1 { color: lightblue; margin-bottom: 0; }
.lb { margin-right: 20px; float: left; }
h2 { color: lightblue; }
.prefooter { clear: both; }
.footer { font-size: 0.8em; margin-top: 20px; }
a { color: black; }
</style></head><body>
<h1>Bramka SMS</h1>
<div class="lb">
<h2>Liczniki</h2>
<em>w ciągu ostatnich... </em><br>
Tygodnia: %(s_week)s <br>
48 godzin %(s_yesterday)s <br>
24 godzin: %(s_today)s <br>
12 godzin: %(s_12hrs)s <br>
5 godzin: %(s_5hrs)s <br>
1 godziny: %(s_hour)s <br>
15 minut: %(s_15mins)s <br><br>
TOTAL: %(s_total)s <br>
<a href="/statistics/">Statystyki</a><br>
</div>
<div class="lb">
<h2>Stan bramki</h2>
Status: %(fail_status)s <br><br>
Resetów modułu: %(gate_hard_resets)s <br>
Błędów RS232: %(gate_soft_resets)s <br>
<em>powyższe są z ostatnich 24 godzin</em> <br><br>
Przeciętna prędkość wysyłki SMS: %(avg_sms_speed)s <br>
<em>z ostatnich 20 SMS-ów</em> <br>
</div>
<div class="prefooter"></div>
<div class="footer">powered by <a href="https://github.com/henrietta/eunike">eunike</a></div>
</body></html>'''
STATSPATH = u'''<!DOCTYPE html>
<html>
<head><title>Statystyki SMS</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">
* { font-family: Helvetica, arial, freesans, clean, sans-serif; }
h1 { color: lightblue; margin-bottom: 0; }
.lb { margin-right: 20px; float: left; }
h2 { color: lightblue; }
.prefooter { clear: both; }
.footer { font-size: 0.8em; margin-top: 20px; }
.footer a { color: black; }
</style></head><body>
<h1>Bramka SMS</h1>
<div class="lb">
<h2>Procentowe udziały</h2>
%(gen_particles)s
</div>
<div class="prefooter"></div>
<div class="footer">powered by <a href="https://github.com/henrietta/eunike">eunike</a></div>
</body></html>
'''
# Load configuration
with open(sys.argv[1], 'rb') as json_config:
cnf = json.load(json_config)
json_at = cnf['instrumentation']['save_json_to']
sql_at = cnf['osm']['path']
class ReqHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def log_request(self, *args, **kwargs):
pass
def _do_POST(self):
self.query_string = self.rfile.read( \
int(self.headers['Content-Length']))
self.args = dict(cgi.parse_qsl(self.query_string))
target = self.args['nr']
content = self.args['sms']
hsh = self.args['hash']
PASSWORD = 'SOME PASSWORD'
hhsh = hashlib.sha1(target + content + PASSWORD).hexdigest()
if hhsh != hsh:
self.send_error(403, 'Invalid password')
return
condict = {
'content': content,
'target': target,
'msgclass': 'sms'
}
try:
condict['tag'] = self.args['tag']
except KeyError:
pass
genname = os.path.join(cnf['oams'][0]['directory'], str(time.time()))
with open(genname, 'wb') as x:
json.dump(condict, x)
os.rename(genname, genname+'.msg')
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
self.wfile.write('{"status": "ok"}')
def do_POST(self):
try:
self._do_POST()
except:
raise
self.send_error(500, 'Try again later')
def do_GET(self):
try:
if 'statistics' in self.path:
k = self._handle_statistics()
else:
k = self._handle_root()
except Exception as e:
self.send_error(500, 'Try again later')
else:
self.send_response(200)
self.send_header('Content-Type', 'text/html')
self.end_headers()
self.wfile.write(k.encode('utf8'))
def inq(self, qu, atr):
self.sqc.execute(qu, atr)
f = self.sqc.fetchall()[0][0]
return f
def _handle_statistics(self):
# SQLite
try:
self.sq = sqlite3.connect(sql_at)
self.sqc = self.sq.cursor()
self.sqc.execute('SELECT tag, COUNT(*) FROM sent GROUP BY tag')
data = self.sqc.fetchall()
finally:
self.sqc.close()
self.sq.close()
total = sum([x[1] for x in data])
td = [u'%s: %.2f%%<br>' % (tag if tag != None else '<em>Nieoznaczony</em>',
100*count/total) \
for tag, count in data]
return STATSPATH % {
'gen_particles': u' '.join(td)
}
def _handle_root(self):
def cx(d):
"""Convert datetime to format understood by sqlite"""
return time.mktime(d.timetuple())
def sent_last_hours(hrs):
return self.inq('SELECT COUNT(rowid) FROM sent WHERE sent_on > ?', (
cx(datetime.datetime.now() - datetime.timedelta(0, 3600*hrs)), ))
with open(json_at, 'rb') as jin:
instr = json.load(jin)
# calculate average sms speed
smss = instr['x-sms1']['send_time']
del smss['value']
try:
avg = sum(smss.values()) / len(smss.values())
avg = '%.2f s' % avg
except:
avg = 'N/A'
# fail status?
fail_status = '<span style="color: green;">OK</span>' \
if not instr['x-sms1']['is_failed'] else \
'<span style="color: red;">AWARIA</span>'
# SQLite
self.sq = sqlite3.connect(sql_at)
self.sqc = self.sq.cursor()
try:
return PRESENTPATH % {
's_total': self.inq('SELECT COUNT(rowid) FROM sent', ()),
's_today': sent_last_hours(12),
's_yesterday': sent_last_hours(48),
's_week': sent_last_hours(12*7),
's_5hrs': sent_last_hours(5),
's_12hrs': sent_last_hours(12),
's_15mins': sent_last_hours(0.25),
's_hour': sent_last_hours(1),
'gate_hard_resets': instr['x-sms1']['hard_resets'],
'gate_soft_resets': instr['x-sms1']['soft_resets'],
'avg_sms_speed': avg,
'fail_status': fail_status
}
finally:
self.sqc.close()
self.sq.close()
if len(sys.argv) != 4:
print 'How to invoke:'
print 'run.py EUNIKE_CONFIG_FILE HTTP_IP HTTP_PORT'
sys.exit()
sys.stdout = open('/dev/null', 'w')
sys.stderr = open('/dev/null', 'w')
BaseHTTPServer.HTTPServer((sys.argv[2], int(sys.argv[3])), ReqHandler).serve_forever()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment