• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (C) 2013 Google Inc. All rights reserved.
2#
3# Redistribution and use in source and binary forms, with or without
4# modification, are permitted provided that the following conditions are
5# met:
6#
7#     * Redistributions of source code must retain the above copyright
8# notice, this list of conditions and the following disclaimer.
9#     * Redistributions in binary form must reproduce the above
10# copyright notice, this list of conditions and the following disclaimer
11# in the documentation and/or other materials provided with the
12# distribution.
13#     * Neither the name of Google Inc. nor the names of its
14# contributors may be used to endorse or promote products derived from
15# this software without specific prior written permission.
16#
17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29import datetime
30import logging
31import webapp2
32
33from google.appengine.ext import ndb
34from google.appengine.ext.webapp import template
35from google.appengine.ext.db import BadRequestError
36
37# A simple log server for rebaseline-o-matic.
38#
39# Accepts updates to the same log entry and shows a simple status page.
40# Has a special state for the case where there are no NeedsRebaseline
41# lines in TestExpectations to avoid cluttering the log with useless
42# entries every 30 seconds.
43#
44# Other than that, new updatelog calls append to the most recent log
45# entry until they have the newentry parameter, in which case, it
46# starts a new log entry.
47
48LOG_PARAM = "log"
49NEW_ENTRY_PARAM = "newentry"
50# FIXME: no_needs_rebaseline is never used anymore. Remove support for it.
51# Instead, add UI to logs.html to collapse short entries.
52NO_NEEDS_REBASELINE_PARAM = "noneedsrebaseline"
53NUM_LOGS_PARAM = "numlogs"
54BEFORE_PARAM = "before"
55
56
57class LogEntry(ndb.Model):
58    content = ndb.TextProperty()
59    date = ndb.DateTimeProperty(auto_now_add=True)
60    is_no_needs_rebaseline = ndb.BooleanProperty()
61
62
63def logs_query():
64    return LogEntry.query().order(-LogEntry.date)
65
66
67class UpdateLog(webapp2.RequestHandler):
68    def post(self):
69        new_log_data = self.request.POST.get(LOG_PARAM)
70        # This entry is set to on whenever a new auto-rebaseline run is going to
71        # start logging entries. If this is not on, then the log will get appended
72        # to the most recent log entry.
73        new_entry = self.request.POST.get(NEW_ENTRY_PARAM) == "on"
74        # The case of no NeedsRebaseline lines in TestExpectations is special-cased
75        # to always overwrite the previous noneedsrebaseline entry in the log to
76        # avoid cluttering the log with useless empty posts. It just updates the
77        # date of the entry so that users can see that rebaseline-o-matic is still
78        # running.
79        # FIXME: no_needs_rebaseline is never used anymore. Remove support for it.
80        no_needs_rebaseline = self.request.POST.get(NO_NEEDS_REBASELINE_PARAM) == "on"
81
82        out = "Wrote new log entry."
83        if not new_entry or no_needs_rebaseline:
84            log_entries = logs_query().fetch(1)
85            if log_entries:
86                log_entry = log_entries[0]
87                log_entry.date = datetime.datetime.now()
88                if no_needs_rebaseline:
89                    # Don't write out a new log entry for repeated no_needs_rebaseline cases.
90                    # The repeated entries just add noise to the logs.
91                    if log_entry.is_no_needs_rebaseline:
92                        out = "Overwrote existing no needs rebaseline log."
93                    else:
94                        out = "Wrote new no needs rebaseline log."
95                        new_entry = True
96                        new_log_data = ""
97                elif log_entry.is_no_needs_rebaseline:
98                    out = "Previous entry was a no need rebaseline log. Writing a new log."
99                    new_entry = True
100                else:
101                    out = "Added to existing log entry."
102                    log_entry.content = log_entry.content + "\n" + new_log_data
103
104        if new_entry or not log_entries:
105            log_entry = LogEntry(content=new_log_data, is_no_needs_rebaseline=no_needs_rebaseline)
106
107        try:
108            log_entry.put()
109        except BadRequestError:
110            out = "Created new log entry because the previous one exceeded the max length."
111            LogEntry(content=new_log_data, is_no_needs_rebaseline=no_needs_rebaseline).put()
112
113        self.response.out.write(out)
114
115
116class UploadForm(webapp2.RequestHandler):
117    def get(self):
118        self.response.out.write(template.render("uploadform.html", {
119            "update_log_url": "/updatelog",
120            "set_no_needs_rebaseline_url": "/noneedsrebaselines",
121            "log_param": LOG_PARAM,
122            "new_entry_param": NEW_ENTRY_PARAM,
123            "no_needs_rebaseline_param": NO_NEEDS_REBASELINE_PARAM,
124        }))
125
126
127class ShowLatest(webapp2.RequestHandler):
128    def get(self):
129        query = logs_query()
130
131        before = self.request.get(BEFORE_PARAM)
132        if before:
133            date = datetime.datetime.strptime(before, "%Y-%m-%dT%H:%M:%SZ")
134            query = query.filter(LogEntry.date < date)
135
136        num_logs = self.request.get(NUM_LOGS_PARAM)
137        logs = query.fetch(int(num_logs) if num_logs else 3)
138
139        self.response.out.write(template.render("logs.html", {
140            "logs": logs,
141            "num_logs_param": NUM_LOGS_PARAM,
142            "before_param": BEFORE_PARAM,
143        }))
144
145
146routes = [
147    ('/uploadform', UploadForm),
148    ('/updatelog', UpdateLog),
149    ('/', ShowLatest),
150]
151
152app = webapp2.WSGIApplication(routes, debug=True)
153