Skip to content

Commit

Permalink
Notifo notifications (closes sampsyo#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
sampsyo committed Sep 7, 2011
1 parent 8a3d248 commit 4b6923f
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 23 deletions.
89 changes: 71 additions & 18 deletions loglet.py
Expand Up @@ -7,9 +7,11 @@
import time
import datetime
from werkzeug.contrib.atom import AtomFeed
import notifo


# Constants.
# Default configuration. These can be overridden using the LOGLET_CONFIG
# environment variable.

DB_NAME = 'loglet.db'
MIN_LEVEL = 0
Expand All @@ -18,7 +20,9 @@
LEVEL_WARN = 30
LEVEL_ERROR = 40
MAX_MESSAGES = 512
MAX_TITLE_LENGTH = 128
MAX_TITLE_LENGTH = 256
MAX_NOTIFO_LENGTH = 128
NOTIFICATION_THRESHOLD = 50
REFRESH_DELAY = 60 # seconds
TIME_ZONES = [
(-12.0, "Eniwetok, Kwajalein"),
Expand Down Expand Up @@ -53,6 +57,8 @@
(+11.0, "Magadan, Solomon Islands, New Caledonia"),
(+12.0, "Auckland, Wellington, Fiji, Kamchatka"),
]
NOTIFO_USER = ''
NOTIFO_SECRET = ''


# Utilities.
Expand All @@ -65,7 +71,8 @@ def random_string(length=16, chars=(string.ascii_letters + string.digits)):
# Application setup.

app = flask.Flask(__name__)
app.debug = False
app.config.from_object(__name__) # Use above constants as default.
app.config.from_envvar('LOGLET_CONFIG', True)

# Connection to SQLite database.
@app.before_request
Expand Down Expand Up @@ -143,7 +150,8 @@ def init_db():
id INTEGER PRIMARY KEY,
longid TEXT UNIQUE,
name TEXT,
twitternames TEXT
twitternames TEXT,
notifoname TEXT
);
CREATE INDEX IF NOT EXISTS loglongid ON logs (longid);
CREATE TABLE IF NOT EXISTS messages (
Expand All @@ -160,21 +168,22 @@ def init_db():
# Query helpers.

def _get_log(longid):
"""Returns the integer ID and title of a log given its long string
ID.
"""Returns the integer ID and extra information of for a log given
its long string ID.
"""
c = g.db.execute("SELECT id, name FROM logs WHERE longid = ?", (longid,))
c = g.db.execute("SELECT id, name, notifoname FROM logs "
"WHERE longid = ?", (longid,))
with closing(c):
row = c.fetchone()
if not row:
flask.abort(404)
return row[0], row[1]
return row[0], {'title': row[1], 'notifoname': row[2] or ''}

def _log_contents(longid):
"""Given a log's long ID, return a list log messages from it and
the log's title.
"""
logid, title = _get_log(longid)
logid, loginfo = _get_log(longid)
c = g.db.execute("SELECT message, time, level, id FROM messages "
"WHERE logid = ? ORDER BY time DESC, id DESC",
(logid,))
Expand All @@ -187,7 +196,7 @@ def _log_contents(longid):
'level': row[2],
'id': row[3]
})
return messages, title
return messages, loginfo


# Views.
Expand Down Expand Up @@ -228,7 +237,7 @@ def log(longid):
elif level > MAX_LEVEL:
level = MAX_LEVEL

logid, _ = _get_log(longid)
logid, loginfo = _get_log(longid)
with g.db:
# Add new message.
g.db.execute("INSERT INTO messages (logid, message, time, level) "
Expand All @@ -239,6 +248,25 @@ def log(longid):
"messages WHERE logid = ? ORDER BY time DESC, id DESC "
"LIMIT -1 OFFSET ?)",
(logid, MAX_MESSAGES))

# Send notifications.
if level >= app.config['NOTIFICATION_THRESHOLD']:
logname = loginfo['title'] or longid
logurl = flask.url_for('log', longid=longid, _external=True)

# Notifo.
if loginfo['notifoname']:
resp = notifo.send_notification(
app.config['NOTIFO_USER'],
app.config['NOTIFO_SECRET'],
loginfo['notifoname'],
title=logname,
msg=message,
uri=logurl
)
if resp['status'] != 'success':
log.warn('notifo notification failed: %s' %
resp)

return flask.jsonify(success=1)

Expand All @@ -249,10 +277,11 @@ def log(longid):
except (KeyError, ValueError):
tzoffset = 0.0

messages, title = _log_contents(longid)
messages, loginfo = _log_contents(longid)
return flask.render_template('log.html',
messages=messages,
title=title,
title=loginfo['title'],
notifoname=loginfo['notifoname'],
longid=longid,
tzoffset=tzoffset)

Expand All @@ -270,17 +299,17 @@ def logtxt(longid):
@app.route("/<longid>/json")
def logjson(longid):
"""JSON log representation."""
messages, title = _log_contents(longid)
messages, loginfo = _log_contents(longid)
return flask.jsonify(log=longid,
messages=messages,
title=title)
title=loginfo['title'])

@app.route("/<longid>/feed")
def logfeed(longid):
"""Atom feed for a log."""
logurl = flask.url_for('log', longid=longid, _external=True)
messages, title = _log_contents(longid)
feed = AtomFeed('Loglet: %s' % title,
messages, loginfo = _log_contents(longid)
feed = AtomFeed('Loglet: %s' % loginfo['title'],
feed_url=request.url,
url=logurl)
for message in messages:
Expand All @@ -298,14 +327,38 @@ def logfeed(longid):
@app.route("/<longid>/meta", methods=["POST"])
def logmeta(longid):
"""Change metadata for a log."""

if 'title' in request.form:
title = request.form['title'][:MAX_TITLE_LENGTH]
title = request.form['title'][:MAX_TITLE_LENGTH].strip()
logid, _ = _get_log(longid)
app.logger.debug("log %s title changed to %s" % (longid, repr(title)))
with g.db:
g.db.execute("UPDATE logs SET name=? WHERE id=?", (title, logid))

if 'notifoname' in request.form:
username = request.form['notifoname'][:MAX_NOTIFO_LENGTH].strip()

# Try confirming with Notifo.
if username:
resp = notifo.subscribe_user(app.config['NOTIFO_USER'],
app.config['NOTIFO_SECRET'],
username)
app.logger.debug('log %s notifo user changed; response: %s' %
(longid, repr(resp)))
if resp['status'] != 'success':
# Successful subscribe. Change user.
app.logger.warn('notifo subscribe failed; disabling')
username = ''

# Store either the successful username or a blank.
logid, _ = _get_log(longid)
with g.db:
g.db.execute("UPDATE logs SET notifoname=? "
"WHERE id=?", (username, logid))

return flask.redirect('/' + longid)


# Debug server.

if __name__ == '__main__':
Expand Down
10 changes: 5 additions & 5 deletions templates/index.html
Expand Up @@ -72,14 +72,14 @@ <h5>Open Source</h5>
Hosted by <a href="http://dreamhost.com/">Dreamhost</a>.
{%- endif %}
</p>
<h5>Mobile-Optimized</h5>
<h5>Notifications</h5>
<p>
Try viewing a log in your phone&rsquo;s browser.
Get log messages sent to your phone or desktop via your
<a href="http://notifo.com/">Notifo</a> account.
</p>
<h5>Coming Soon</h5>
<h5>Mobile-Optimized</h5>
<p>
I&rsquo;d like to add notification via Twitter DMs. Check back
soon.
Try viewing a log in your phone&rsquo;s browser.
</p>
<h5>Limits</h5>
<p>
Expand Down
15 changes: 15 additions & 0 deletions templates/log.html
Expand Up @@ -124,6 +124,21 @@ <h5>Auto-Refresh</h4>
Reload every {{ refresh_delay }} seconds.
</label>
</form>
<h5>Notifications</h5>
<p>
Enter your <a href="http://notifo.com/">Notifo</a> username
to send messages to your phone or desktop.
</p>
<form action="{{ url_for('logmeta', longid=longid) }}"
method="POST">
<input type="text" name="notifoname" id="notifobox"
value="{{ notifoname }}">
</form>
<p>
Notifications are sent for messages with level
{{ config.NOTIFICATION_THRESHOLD }}
or greater.
</p>
<h5>Log Title</h5>
<form action="{{ url_for('logmeta', longid=longid) }}"
method="POST">
Expand Down

0 comments on commit 4b6923f

Please sign in to comment.