Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4)

Side by Side Diff: Tools/GardeningServer/alerts.py

Issue 643613002: Revert of Implemented retrieval of alerts history (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | Tools/GardeningServer/alerts_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import calendar 5 import calendar
6 import datetime 6 import datetime
7 import json 7 import json
8 import logging 8 import logging
9 import webapp2 9 import webapp2
10 import zlib 10 import zlib
11 11
12 from google.appengine.api import memcache 12 from google.appengine.api import memcache
13 from google.appengine.api import users
14 from google.appengine.datastore import datastore_query
15 from google.appengine.ext import ndb 13 from google.appengine.ext import ndb
16 14
17 LOGGER = logging.getLogger(__name__) 15 LOGGER = logging.getLogger(__name__)
18 16
19 17
20 class DateTimeEncoder(json.JSONEncoder): 18 class DateTimeEncoder(json.JSONEncoder):
21 def default(self, obj): 19 def default(self, obj):
22 if isinstance(obj, datetime.datetime): 20 if isinstance(obj, datetime.datetime):
23 return calendar.timegm(obj.timetuple()) 21 return calendar.timegm(obj.timetuple())
24 # Let the base class default method raise the TypeError. 22 # Let the base class default method raise the TypeError.
25 return json.JSONEncoder.default(self, obj) 23 return json.JSONEncoder.default(self, obj)
26 24
27 25
28 class AlertsJSON(ndb.Model): 26 class AlertsJSON(ndb.Model):
29 type = ndb.StringProperty()
30 json = ndb.BlobProperty(compressed=True) 27 json = ndb.BlobProperty(compressed=True)
31 date = ndb.DateTimeProperty(auto_now_add=True) 28 date = ndb.DateTimeProperty(auto_now_add=True)
32 29
33 30
34 class AlertsHandler(webapp2.RequestHandler): 31 class AlertsHandler(webapp2.RequestHandler):
35 ALERTS_TYPE = 'alerts' 32 MEMCACHE_ALERTS_KEY = 'alerts'
36 33
37 # Has no 'response' member. 34 # Has no 'response' member.
38 # pylint: disable=E1101 35 # pylint: disable=E1101
39 def send_json_headers(self): 36 def send_json_headers(self):
40 self.response.headers.add_header('Access-Control-Allow-Origin', '*') 37 self.response.headers.add_header('Access-Control-Allow-Origin', '*')
41 self.response.headers['Content-Type'] = 'application/json' 38 self.response.headers['Content-Type'] = 'application/json'
42 39
43 # Has no 'response' member. 40 # Has no 'response' member.
44 # pylint: disable=E1101 41 # pylint: disable=E1101
45 def send_json_data(self, data): 42 def send_json_data(self, data):
46 self.send_json_headers() 43 self.send_json_headers()
47 self.response.write(data) 44 self.response.write(data)
48 45
49 def generate_json_dump(self, alerts): 46 def generate_json_dump(self, alerts):
50 return json.dumps(alerts, cls=DateTimeEncoder, indent=1) 47 return json.dumps(alerts, cls=DateTimeEncoder, indent=1)
51 48
52 def get_from_memcache(self, memcache_key): 49 def get_from_memcache(self, memcache_key):
53 compressed = memcache.get(memcache_key) 50 compressed = memcache.get(memcache_key)
54 if not compressed: 51 if not compressed:
55 self.send_json_headers() 52 self.send_json_headers()
56 return 53 return
57 uncompressed = zlib.decompress(compressed) 54 uncompressed = zlib.decompress(compressed)
58 self.send_json_data(uncompressed) 55 self.send_json_data(uncompressed)
59 56
60 def get(self): 57 def get(self):
61 self.get_from_memcache(AlertsHandler.ALERTS_TYPE) 58 self.get_from_memcache(AlertsHandler.MEMCACHE_ALERTS_KEY)
62 59
63 def post_to_history(self, alerts_type, alerts): 60 def save_alerts_to_history(self, alerts):
64 last_query = AlertsJSON.query().filter(AlertsJSON.type == alerts_type) 61 last_entry = AlertsJSON.query().order(-AlertsJSON.date).get()
65 last_entry = last_query.order(-AlertsJSON.date).get()
66 last_alerts = json.loads(last_entry.json) if last_entry else {} 62 last_alerts = json.loads(last_entry.json) if last_entry else {}
67 63
68 # Only changes to the fields with 'alerts' in the name should cause a 64 # Only changes to the fields with 'alerts' in the name should cause a
69 # new history entry to be saved. 65 # new history entry to be saved.
70 def alert_fields(alerts_json): 66 def alert_fields(alerts_json):
71 filtered_json = {} 67 filtered_json = {}
72 for key, value in alerts_json.iteritems(): 68 for key, value in alerts_json.iteritems():
73 if 'alerts' in key: 69 if 'alerts' in key:
74 filtered_json[key] = value 70 filtered_json[key] = value
75 return filtered_json 71 return filtered_json
76 72
77 if alert_fields(last_alerts) != alert_fields(alerts): 73 if alert_fields(last_alerts) != alert_fields(alerts):
78 new_entry = AlertsJSON( 74 new_entry = AlertsJSON(json=self.generate_json_dump(alerts))
79 json=self.generate_json_dump(alerts),
80 type=alerts_type)
81 new_entry.put() 75 new_entry.put()
82 76
83 # Has no 'response' member. 77 # Has no 'response' member.
84 # pylint: disable=E1101 78 # pylint: disable=E1101
85 def post_to_memcache(self, memcache_key, alerts): 79 def post_to_memcache(self, memcache_key, alerts):
86 uncompressed = self.generate_json_dump(alerts) 80 uncompressed = self.generate_json_dump(alerts)
87 compression_level = 1 81 compression_level = 1
88 compressed = zlib.compress(uncompressed, compression_level) 82 compressed = zlib.compress(uncompressed, compression_level)
89 memcache.set(memcache_key, compressed) 83 memcache.set(memcache_key, compressed)
90 84
91 def parse_alerts(self, alerts_json): 85 def parse_alerts(self, alerts_json):
92 try: 86 try:
93 alerts = json.loads(alerts_json) 87 alerts = json.loads(alerts_json)
94 except ValueError: 88 except ValueError:
95 warning = 'content field was not JSON' 89 warning = 'content field was not JSON'
96 self.response.set_status(400, warning) 90 self.response.set_status(400, warning)
97 LOGGER.warn(warning) 91 LOGGER.warn(warning)
98 return 92 return
99 93
100 alerts.update({'date': datetime.datetime.utcnow()}) 94 alerts.update({'date': datetime.datetime.utcnow()})
101 95
102 return alerts 96 return alerts
103 97
104 def update_alerts(self, alerts_type): 98 def update_alerts(self, memcache_key):
105 alerts = self.parse_alerts(self.request.get('content')) 99 alerts = self.parse_alerts(self.request.get('content'))
106 if alerts: 100 if alerts:
107 self.post_to_memcache(alerts_type, alerts) 101 self.post_to_memcache(memcache_key, alerts)
108 self.post_to_history(alerts_type, alerts) 102 self.save_alerts_to_history(alerts)
109 103
110 def post(self): 104 def post(self):
111 self.update_alerts(AlertsHandler.ALERTS_TYPE) 105 self.update_alerts(AlertsHandler.MEMCACHE_ALERTS_KEY)
112
113
114 class AlertsHistory(webapp2.RequestHandler):
115 MAX_LIMIT_PER_PAGE = 100
116
117 def get(self):
118 alerts_query = AlertsJSON.query().order(-AlertsJSON.date)
119 result_json = {}
120
121 user = users.get_current_user()
122 if not user:
123 result_json['redirect-url'] = users.create_login_url(
124 self.request.uri)
125
126 # Return only public alerts for non-internal users.
127 if not user or not user.email().endswith('@google.com'):
128 alerts_query = alerts_query.filter(
129 AlertsJSON.type == AlertsHandler.ALERTS_TYPE)
130
131 cursor = self.request.get('cursor')
132 if cursor:
133 cursor = datastore_query.Cursor(urlsafe=cursor)
134
135 limit = int(self.request.get('limit', self.MAX_LIMIT_PER_PAGE))
136 limit = min(self.MAX_LIMIT_PER_PAGE, limit)
137
138 if cursor:
139 alerts, next_cursor, has_more = alerts_query.fetch_page(
140 limit, start_cursor=cursor)
141 else:
142 alerts, next_cursor, has_more = alerts_query.fetch_page(limit)
143
144 result_json.update({
145 'has_more': has_more,
146 'cursor': next_cursor.urlsafe() if next_cursor else '',
147 'history': [json.loads(alert.json) for alert in alerts]
148 })
149
150 self.response.headers['Content-Type'] = 'application/json'
151 self.response.out.write(json.dumps(result_json))
152 106
153 107
154 app = webapp2.WSGIApplication([ 108 app = webapp2.WSGIApplication([
155 ('/alerts', AlertsHandler), 109 ('/alerts', AlertsHandler)
156 ('/alerts-history', AlertsHistory)
157 ]) 110 ])
OLDNEW
« no previous file with comments | « no previous file | Tools/GardeningServer/alerts_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698