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
|
# Copyright (C) 2013 Google Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import datetime
import logging
import webapp2
from google.appengine.ext import ndb
from google.appengine.ext.webapp import template
# A simple log server for rebaseline-o-matic.
#
# Accepts updates to the same log entry and shows a simple status page.
# Has a special state for the case where there are no NeedsRebaseline
# lines in TestExpectations to avoid cluttering the log with useless
# entries every 30 seconds.
#
# Other than that, new updatelog calls append to the most recent log
# entry until they have the newentry parameter, in which case, it
# starts a new log entry.
LOG_PARAM = "log"
NEW_ENTRY_PARAM = "newentry"
NO_NEEDS_REBASELINE_PARAM = "noneedsrebaseline"
NUM_LOGS_PARAM = "numlogs"
BEFORE_PARAM = "before"
class LogEntry(ndb.Model):
content = ndb.TextProperty()
date = ndb.DateTimeProperty(auto_now_add=True)
is_no_needs_rebaseline = ndb.BooleanProperty()
def logs_query():
return LogEntry.query().order(-LogEntry.date)
class UpdateLog(webapp2.RequestHandler):
def post(self):
new_log_data = self.request.POST.get(LOG_PARAM)
# This entry is set to on whenever a new auto-rebaseline run is going to
# start logging entries. If this is not on, then the log will get appended
# to the most recent log entry.
new_entry = self.request.POST.get(NEW_ENTRY_PARAM) == "on"
# The case of no NeedsRebaseline lines in TestExpectations is special-cased
# to always overwrite the previous noneedsrebaseline entry in the log to
# avoid cluttering the log with useless empty posts. It just updates the
# date of the entry so that users can see that rebaseline-o-matic is still
# running.
no_needs_rebaseline = self.request.POST.get(NO_NEEDS_REBASELINE_PARAM) == "on"
out = "Wrote new log entry."
if not new_entry or no_needs_rebaseline:
log_entries = logs_query().fetch(1)
if log_entries:
log_entry = log_entries[0]
log_entry.date = datetime.datetime.now()
if no_needs_rebaseline:
# Don't write out a new log entry for repeated no_needs_rebaseline cases.
# The repeated entries just add noise to the logs.
if log_entry.is_no_needs_rebaseline:
out = "Overwrote existing no needs rebaseline log."
else:
out = "Wrote new no needs rebaseline log."
new_entry = True
new_log_data = ""
elif log_entry.is_no_needs_rebaseline:
out = "Previous entry was a no need rebaseline log. Writing a new log."
new_entry = True
else:
out = "Added to existing log entry."
log_entry.content = log_entry.content + "\n" + new_log_data
if new_entry or not log_entries:
log_entry = LogEntry(content=new_log_data, is_no_needs_rebaseline=no_needs_rebaseline)
log_entry.put()
self.response.out.write(out)
class UploadForm(webapp2.RequestHandler):
def get(self):
self.response.out.write(template.render("uploadform.html", {
"update_log_url": "/updatelog",
"set_no_needs_rebaseline_url": "/noneedsrebaselines",
"log_param": LOG_PARAM,
"new_entry_param": NEW_ENTRY_PARAM,
"no_needs_rebaseline_param": NO_NEEDS_REBASELINE_PARAM,
}))
class ShowLatest(webapp2.RequestHandler):
def get(self):
query = logs_query()
before = self.request.get(BEFORE_PARAM)
if before:
date = datetime.datetime.strptime(before, "%Y-%m-%dT%H:%M:%SZ")
query = query.filter(LogEntry.date < date)
num_logs = self.request.get(NUM_LOGS_PARAM)
logs = query.fetch(int(num_logs) if num_logs else 3)
self.response.out.write(template.render("logs.html", {
"logs": logs,
"num_logs_param": NUM_LOGS_PARAM,
"before_param": BEFORE_PARAM,
}))
routes = [
('/uploadform', UploadForm),
('/updatelog', UpdateLog),
('/', ShowLatest),
]
app = webapp2.WSGIApplication(routes, debug=True)
|