-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
150 lines (132 loc) · 5.2 KB
/
main.py
File metadata and controls
150 lines (132 loc) · 5.2 KB
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
145
146
147
148
149
150
import webapp2
import json
import datetime
import logging
from utils.jsondataencoder import JSONDateEncoder
from model.conveyordata import ConveyorData
from model.conveyorreset import ConveyorReset
from google.appengine.api import channel
from google.appengine.ext import deferred
from google.appengine.api import memcache
from utils.templateloader import JinjaTemplate, JinjaTemplateNotFoundException
class DialogHandler(webapp2.RequestHandler):
"""
Class for keeping the clients updated and listening to the reset button
message
"""
@staticmethod
def _get_latest_data():
return {
'last_data': ConveyorData.get_most_recent() or {},
'last_reset': ConveyorReset.get_most_recent() or {},
'now': datetime.datetime.utcnow()
}
def get(self):
"""
Method called on HTTP GET /opened
Writes {"last_data": ..., "last_reset": ...}
"""
self.response.out.write(json.dumps(self._get_latest_data(),
cls=JSONDateEncoder))
def post(self):
"""
Method called on HTTP POST /opened
If 'reset' is passed it will add a reset mark.
If 'status' is passed then:
- It expects also 'timestamp' with the Conveyor Machine data.
- Expects 'current_total_weight'.
- The new data is scheduled to be sent via channel to all clients.
If 'new' is passed is a request for the old data (via channel).
"""
to = memcache.get('clients') or []
if self.request.get('reset'):
current_total_weight = 0.0
current_data = ConveyorData.get_most_recent()
if current_data:
current_total_weight = current_data.current_total_weight
reset = ConveyorReset(current_total_weight = current_total_weight)
reset.put()
#Exit the function not to send messages back to the clients
return
elif self.request.get('status'):
timestamp = datetime.datetime.strptime(
self.request.get('timestamp'),
'%Y-%m-%d %H:%M:%S.%f'
) if self.request.get('timestamp') else None
current_total_weight = \
float(self.request.get('current_total_weight'))
status = self.request.get('status')
if None in (timestamp, current_total_weight, status):
#TODO: Raise error!
pass
conveyor_data = ConveyorData(timestamp=timestamp,
current_total_weight=current_total_weight,
status=status)
conveyor_data.put()
message = self._get_latest_data()
if self.request.get('new'):
to = [{'user_token':self.request.get('user_token')}]
message['previous'] = ConveyorData.get_most_recent(30, 1)
# The response via channel to clients in the 'to' array
deferred.defer(notify_clients, message, to)
def notify_clients(message, clients):
"""
Function (usually deferred) to send the message to the clients via
Google App Engine's Channel API
Channels only last open for two hours so, here, all clients subscribed
more than two hours ago are deleted from the array.
"""
now = datetime.datetime.now()
to_remove = []
for i in clients:
if 'created' in i:
elapsed = now - i['created']
if elapsed.total_seconds() > 2 * 60 * 60:
to_remove.append(i)
continue
#Let every client know the server's datetime for syncing.
#Since this job can be deferred the time needs to be updated.
message['now'] = datetime.datetime.utcnow()
channel.send_message(i['user_token'], json.dumps(message,
cls=JSONDateEncoder))
c = memcache.get('clients')
for i in to_remove:
c.remove(i)
memcache.set('clients', c)
class MainPage(webapp2.RequestHandler):
"""
Handler from the main page
"""
def get(self):
"""
Handler for HTTP GET /
Creates the user token and writes back the templated data
with the information needed for the client to connect to the
Channel API.
"""
user_token = self.request.get('user_token')
if not user_token:
import uuid
user_token = uuid.uuid4().get_hex()
token = channel.create_channel(user_token)
template_values = {
'token': token,
'user_token': user_token,
'created': datetime.datetime.now()
}
ret = None
try:
ret = JinjaTemplate('main.html').render(template_values)
except JinjaTemplateNotFoundException as ex:
ret = '<h1>Error: {}</h1>'.format(ex.message)
except Exception as ex:
ret = '<h1>Generic error: {}</h1>'.format(repr(ex))
clients = memcache.get('clients') or []
clients.append(template_values)
memcache.set('clients', clients)
self.response.out.write(ret)
app = webapp2.WSGIApplication([
('/', MainPage),
('/opened', DialogHandler),
])
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4