| Index: Tools/GardeningServer/alerts.py
|
| diff --git a/Tools/GardeningServer/alerts.py b/Tools/GardeningServer/alerts.py
|
| deleted file mode 100644
|
| index 380443ac37ccfa6e19e4adad287b143c006d09e9..0000000000000000000000000000000000000000
|
| --- a/Tools/GardeningServer/alerts.py
|
| +++ /dev/null
|
| @@ -1,176 +0,0 @@
|
| -# Copyright 2014 The Chromium Authors. All rights reserved.
|
| -# Use of this source code is governed by a BSD-style license that can be
|
| -# found in the LICENSE file.
|
| -
|
| -import calendar
|
| -import datetime
|
| -import json
|
| -import logging
|
| -import webapp2
|
| -import zlib
|
| -
|
| -from google.appengine.api import memcache
|
| -from google.appengine.api import users
|
| -from google.appengine.datastore import datastore_query
|
| -from google.appengine.ext import ndb
|
| -
|
| -LOGGER = logging.getLogger(__name__)
|
| -
|
| -
|
| -class DateTimeEncoder(json.JSONEncoder):
|
| - def default(self, obj):
|
| - if isinstance(obj, datetime.datetime):
|
| - return calendar.timegm(obj.timetuple())
|
| - # Let the base class default method raise the TypeError.
|
| - return json.JSONEncoder.default(self, obj)
|
| -
|
| -
|
| -class AlertsJSON(ndb.Model):
|
| - type = ndb.StringProperty()
|
| - json = ndb.BlobProperty(compressed=True)
|
| - date = ndb.DateTimeProperty(auto_now_add=True)
|
| -
|
| -
|
| -class AlertsHandler(webapp2.RequestHandler):
|
| - ALERTS_TYPE = 'alerts'
|
| -
|
| - # Has no 'response' member.
|
| - # pylint: disable=E1101
|
| - def send_json_headers(self):
|
| - self.response.headers.add_header('Access-Control-Allow-Origin', '*')
|
| - self.response.headers['Content-Type'] = 'application/json'
|
| -
|
| - # Has no 'response' member.
|
| - # pylint: disable=E1101
|
| - def send_json_data(self, data):
|
| - self.send_json_headers()
|
| - self.response.write(data)
|
| -
|
| - def generate_json_dump(self, alerts):
|
| - return json.dumps(alerts, cls=DateTimeEncoder, indent=1)
|
| -
|
| - def get_from_memcache(self, memcache_key):
|
| - compressed = memcache.get(memcache_key)
|
| - if not compressed:
|
| - self.send_json_headers()
|
| - return
|
| - uncompressed = zlib.decompress(compressed)
|
| - self.send_json_data(uncompressed)
|
| -
|
| - def get(self):
|
| - self.get_from_memcache(AlertsHandler.ALERTS_TYPE)
|
| -
|
| - def post_to_history(self, alerts_type, alerts):
|
| - last_query = AlertsJSON.query().filter(AlertsJSON.type == alerts_type)
|
| - last_entry = last_query.order(-AlertsJSON.date).get()
|
| - last_alerts = json.loads(last_entry.json) if last_entry else {}
|
| -
|
| - # Only changes to the fields with 'alerts' in the name should cause a
|
| - # new history entry to be saved.
|
| - def alert_fields(alerts_json):
|
| - filtered_json = {}
|
| - for key, value in alerts_json.iteritems():
|
| - if 'alerts' in key:
|
| - filtered_json[key] = value
|
| - return filtered_json
|
| -
|
| - if alert_fields(last_alerts) != alert_fields(alerts):
|
| - new_entry = AlertsJSON(
|
| - json=self.generate_json_dump(alerts),
|
| - type=alerts_type)
|
| - new_entry.put()
|
| -
|
| - # Has no 'response' member.
|
| - # pylint: disable=E1101
|
| - def post_to_memcache(self, memcache_key, alerts):
|
| - uncompressed = self.generate_json_dump(alerts)
|
| - compression_level = 1
|
| - compressed = zlib.compress(uncompressed, compression_level)
|
| - memcache.set(memcache_key, compressed)
|
| -
|
| - def parse_alerts(self, alerts_json):
|
| - try:
|
| - alerts = json.loads(alerts_json)
|
| - except ValueError:
|
| - warning = 'content field was not JSON'
|
| - self.response.set_status(400, warning)
|
| - LOGGER.warn(warning)
|
| - return
|
| -
|
| - alerts.update({'date': datetime.datetime.utcnow()})
|
| -
|
| - return alerts
|
| -
|
| - def update_alerts(self, alerts_type):
|
| - alerts = self.parse_alerts(self.request.get('content'))
|
| - if alerts:
|
| - self.post_to_memcache(alerts_type, alerts)
|
| - self.post_to_history(alerts_type, alerts)
|
| -
|
| - def post(self):
|
| - self.update_alerts(AlertsHandler.ALERTS_TYPE)
|
| -
|
| -
|
| -class AlertsHistory(webapp2.RequestHandler):
|
| - MAX_LIMIT_PER_PAGE = 100
|
| -
|
| - def get_entry(self, query, key):
|
| - try:
|
| - key = int(key)
|
| - except ValueError:
|
| - self.response.set_status(400, 'Invalid key format')
|
| - return {}
|
| -
|
| - ndb_key = ndb.Key(AlertsJSON, key)
|
| - result = query.filter(AlertsJSON.key == ndb_key).get()
|
| - if result:
|
| - return json.loads(result.json)
|
| - else:
|
| - self.response.set_status(404, 'Failed to find key %s' % key)
|
| - return {}
|
| -
|
| - def get_list(self, query):
|
| - cursor = self.request.get('cursor')
|
| - if cursor:
|
| - cursor = datastore_query.Cursor(urlsafe=cursor)
|
| -
|
| - limit = int(self.request.get('limit', self.MAX_LIMIT_PER_PAGE))
|
| - limit = min(self.MAX_LIMIT_PER_PAGE, limit)
|
| -
|
| - if cursor:
|
| - alerts, next_cursor, has_more = query.fetch_page(limit,
|
| - start_cursor=cursor)
|
| - else:
|
| - alerts, next_cursor, has_more = query.fetch_page(limit)
|
| -
|
| - return {
|
| - 'has_more': has_more,
|
| - 'cursor': next_cursor.urlsafe() if next_cursor else '',
|
| - 'history': [alert.key.integer_id() for alert in alerts]
|
| - }
|
| -
|
| - def get(self, key=None):
|
| - query = AlertsJSON.query().order(-AlertsJSON.date)
|
| - result_json = {}
|
| -
|
| - user = users.get_current_user()
|
| - result_json['login-url'] = users.create_login_url(self.request.uri)
|
| -
|
| - # Return only public alerts for non-internal users.
|
| - if not user or not user.email().endswith('@google.com'):
|
| - query = query.filter(AlertsJSON.type == AlertsHandler.ALERTS_TYPE)
|
| -
|
| - if key:
|
| - result_json.update(self.get_entry(query, key))
|
| - else:
|
| - result_json.update(self.get_list(query))
|
| -
|
| - self.response.headers['Content-Type'] = 'application/json'
|
| - self.response.out.write(json.dumps(result_json))
|
| -
|
| -
|
| -app = webapp2.WSGIApplication([
|
| - ('/alerts', AlertsHandler),
|
| - ('/alerts-history', AlertsHistory),
|
| - ('/alerts-history/(.*)', AlertsHistory),
|
| -])
|
|
|