/
huskyhunter.py
144 lines (116 loc) · 4.13 KB
/
huskyhunter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import tornado.ioloop
import tornado.web
import psycopg2
import functools
import json
import os
import sys
import uuid
import urlparse
import clues
from contextlib import contextmanager
DATABASE_URL = urlparse.urlparse(os.environ['DATABASE_URL'])
@contextmanager
def commit(connection):
yield
connection.commit()
def jsonp(name, body):
return "%s(%s)" % (name, body)
def generate_id():
base_id = uuid.uuid4()
return base_id.int % 100000000
def valid_team(fun):
@functools.wraps(fun)
def wrapped(self, team, *args, **kwargs):
self.cursor.execute("select count(*) from teams where id = %s", (team,))
team_count = self.cursor.fetchone()[0]
if not team_count > 0:
raise tornado.web.HTTPError(404)
fun(self, team, *args, **kwargs)
return wrapped
def existing_clue(fun):
@functools.wraps(fun)
def wrapped(self, team, clue, *args, **kwargs):
if clues.get(self.cursor, team, clue) is None:
raise tornado.web.HTTPError(404)
fun(self, team, clue, *args, **kwargs)
return wrapped
class BaseHandler(tornado.web.RequestHandler):
def prepare(self):
self.jsonp_callback = self.get_argument("callback", "")
self.has_jsonp_callback = self.jsonp_callback != ""
self.connection = psycopg2.connect(host=DATABASE_URL.hostname, database=DATABASE_URL.path[1:],
user=DATABASE_URL.username, password=DATABASE_URL.password)
self.cursor = self.connection.cursor()
def on_finish(self):
self.cursor.close()
self.connection.close()
def writeJsonp(self, body):
self.set_header('Access-Control-Allow-Origin', '*')
self.write(jsonp(self.jsonp_callback, body) if self.has_jsonp_callback else body)
def options(self, *args, **kwargs):
self.set_header('Access-Control-Allow-Origin', '*')
self.set_header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
self.set_header('Access-Control-Allow-Headers', 'Content-Type')
self.set_status(200)
class CluesHandler(BaseHandler):
@valid_team
def get(self, team):
self.writeJsonp(json.dumps(clues.get_all(self.cursor, team)))
class ClueHandler(BaseHandler):
@valid_team
@existing_clue
def get(self, team, clue_number):
self.writeJsonp(clues.get_json(self.cursor, team, clue_number))
@valid_team
def put(self, team, clue_number):
clue = clues.decode(self.request.body)
print clue
with commit(self.connection):
if clues.get(self.cursor, team, clue_number) is None:
clues.create(self.cursor, team, clue_number, clue)
else:
clue = clues.update(self.cursor, team, clue_number, clue)
self.writeJsonp(json.dumps(clue))
@valid_team
@existing_clue
def delete(self, team, clue_number):
with commit(self.connection):
clues.delete(self.cursor, team, clue_number)
class PhotosHandler(BaseHandler):
@valid_team
@existing_clue
def get(self, team, clue):
self.writeJsonp(json.dumps(clues.get(self.cursor, team, clue).get("photos", [])))
class TeamHandler(BaseHandler):
@valid_team
def get(self, team):
self.cursor.execute("select name, id from teams where id = %s", (team,))
team = self.cursor.fetchone()
self.writeJsonp(json.dumps({"name": team[0], "id": team[1]}))
class TeamsHandler(BaseHandler):
def makeTeam(self):
name = self.get_argument("name")
new_id = generate_id()
with commit(self.connection):
self.cursor.execute("insert into teams (id, name) values (%s, %s)", (new_id, name))
return {"name": name, "id": new_id}
def get(self):
name = self.get_argument("name")
self.writeJsonp(json.dumps(self.makeTeam()))
def post(self):
name = self.get_argument("name")
self.writeJsonp(json.dumps(self.makeTeam()))
application = tornado.web.Application([
(r"/teams/?", TeamsHandler),
(r"/teams/([^/]+)/?", TeamHandler),
(r"/teams/([^/]+)/clues/?", CluesHandler),
(r"/teams/([^/]+)/clues/([^/]+)/?", ClueHandler),
(r"/teams/([^/]+)/clues/([^/]+)/photos/?", PhotosHandler),
])
if __name__ == "__main__":
port = os.environ.get("PORT")
print "Listening on port %s" % port
sys.stdout.flush()
application.listen(port)
tornado.ioloop.IOLoop.instance().start()