diff options
Diffstat (limited to 'WebKitTools/QueueStatusServer')
11 files changed, 270 insertions, 99 deletions
diff --git a/WebKitTools/QueueStatusServer/handlers/dashboard.py b/WebKitTools/QueueStatusServer/handlers/dashboard.py index 80f30ec..bbb65b8 100644 --- a/WebKitTools/QueueStatusServer/handlers/dashboard.py +++ b/WebKitTools/QueueStatusServer/handlers/dashboard.py @@ -26,16 +26,55 @@ # (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 operator + from google.appengine.ext import webapp from google.appengine.ext.webapp import template from model.attachment import Attachment +from model.queues import queues + class Dashboard(webapp.RequestHandler): - def get(self): - attachments = Attachment.recent(limit=25) + # FIXME: This list probably belongs as part of a Queue object in queues.py + # Arrays are bubble_name, queue_name + # FIXME: Can this be unified with StatusBubble._queues_to_display? + _queues_to_display = [ + ["Style", "style-queue"], + ["Cr-Linux", "chromium-ews"], + ["Cr-Win", "cr-win-ews"], + ["Qt", "qt-ews"], + ["Gtk", "gtk-ews"], + ["Mac", "mac-ews"], + ["Win", "win-ews"], + ["Commit", "commit-queue"], + ] + # Split the zipped list into component parts + _header_names, _ordered_queue_names = zip(*_queues_to_display) + + # This asserts that all of the queues listed above are valid queue names. + assert(reduce(operator.and_, map(lambda name: name in queues, _ordered_queue_names))) + + def _build_bubble(self, attachment, queue_name): + queue_status = attachment.status_for_queue(queue_name) + bubble = { + "status_class": attachment.state_from_queue_status(queue_status) if queue_status else "none", + "status_date": queue_status.date if queue_status else None, + } + return bubble + + def _build_row(self, attachment): + row = { + "bug_id": attachment.bug_id(), + "attachment_id": attachment.id, + "bubbles": [self._build_bubble(attachment, queue_name) for queue_name in self._ordered_queue_names], + } + return row + + def get(self): template_values = { - "summaries" : [attachment.summary() for attachment in attachments], + "headers": self._header_names, + "rows": [self._build_row(attachment) for attachment in Attachment.recent(limit=25)], } self.response.out.write(template.render("templates/dashboard.html", template_values)) diff --git a/WebKitTools/QueueStatusServer/handlers/statusbubble.py b/WebKitTools/QueueStatusServer/handlers/statusbubble.py index d52509f..0e2b8de 100644 --- a/WebKitTools/QueueStatusServer/handlers/statusbubble.py +++ b/WebKitTools/QueueStatusServer/handlers/statusbubble.py @@ -26,17 +26,50 @@ # (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 operator + from google.appengine.ext import webapp from google.appengine.ext.webapp import template from model.attachment import Attachment +from model.workitems import WorkItems +from model.queues import queues, name_with_underscores class StatusBubble(webapp.RequestHandler): + # FIXME: This list probably belongs as part of a Queue object in queues.py + # Arrays are bubble_name, queue_name + _queues_to_display = [ + ["style", "style-queue"], + ["cr-linux", "chromium-ews"], + ["cr-win", "cr-win-ews"], + ["gtk", "gtk-ews"], + ["qt", "qt-ews"], + ["mac", "mac-ews"], + ["win", "win-ews"], + ] + + # This asserts that all of the queues listed above are valid queue names. + assert(reduce(operator.and_, map(lambda name_pair: name_pair[1] in queues, _queues_to_display))) + + def _build_bubble(self, queue_name_pair, attachment): + bubble_name = queue_name_pair[0] + queue_name = queue_name_pair[1] + + queue_status = attachment.status_for_queue(queue_name) + bubble = { + "name": bubble_name, + "attachment_id": attachment.id, + "queue_position": attachment.position_in_queue(queue_name), + "state": attachment.state_from_queue_status(queue_status) if queue_status else "none", + "status": queue_status, + } + return bubble + def get(self, attachment_id): attachment = Attachment(int(attachment_id)) - + bubbles = [self._build_bubble(name_pair, attachment) for name_pair in self._queues_to_display] template_values = { - "summary" : attachment.summary() + "bubbles": bubbles, } self.response.out.write(template.render("templates/statusbubble.html", template_values)) diff --git a/WebKitTools/QueueStatusServer/handlers/updatestatus.py b/WebKitTools/QueueStatusServer/handlers/updatestatus.py index 50d4b6e..5a93dbd 100644 --- a/WebKitTools/QueueStatusServer/handlers/updatestatus.py +++ b/WebKitTools/QueueStatusServer/handlers/updatestatus.py @@ -38,9 +38,10 @@ class UpdateStatus(UpdateBase): def get(self): self.response.out.write(template.render("templates/updatestatus.html", None)) - def post(self): + def _queue_status_from_request(self): queue_status = QueueStatus() + # FIXME: I think this can be removed, no one uses it. if users.get_current_user(): queue_status.author = users.get_current_user() @@ -53,6 +54,10 @@ class UpdateStatus(UpdateBase): queue_status.message = self.request.get("status") results_file = self.request.get("results_file") queue_status.results_file = db.Blob(str(results_file)) + return queue_status + + def post(self): + queue_status = self._queue_status_from_request() queue_status.put() - Attachment.dirty(patch_id) + Attachment.dirty(queue_status.active_patch_id) self.response.out.write(queue_status.key().id()) diff --git a/WebKitTools/QueueStatusServer/handlers/updateworkitems.py b/WebKitTools/QueueStatusServer/handlers/updateworkitems.py new file mode 100644 index 0000000..b58e743 --- /dev/null +++ b/WebKitTools/QueueStatusServer/handlers/updateworkitems.py @@ -0,0 +1,64 @@ +# Copyright (C) 2010 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. + +from google.appengine.ext import webapp, db +from google.appengine.ext.webapp import template + +from handlers.updatebase import UpdateBase +from model.queues import queues +from model.workitems import WorkItems + +from datetime import datetime + + +class UpdateWorkItems(UpdateBase): + def get(self): + self.response.out.write(template.render("templates/updateworkitems.html", None)) + + def _work_items_for_queue(self, queue_name): + if queue_name not in queues: + self.response.set_status(500) + return + work_items = WorkItems.all().filter("queue_name =", queue_name).get() + if not work_items: + work_items = WorkItems() + work_items.queue_name = queue_name + return work_items + + def _work_items_from_request(self): + queue_name = self.request.get("queue_name") + work_items = self._work_items_for_queue(queue_name) + items_string = self.request.get("work_items") + # Our parsing could be much more robust. + work_items.item_ids = map(int, items_string.split(" ")) + work_items.date = datetime.now() + return work_items + + def post(self): + work_items = self._work_items_from_request() + work_items.put() diff --git a/WebKitTools/QueueStatusServer/main.py b/WebKitTools/QueueStatusServer/main.py index 8bfa7e9..fb6fc4b 100644 --- a/WebKitTools/QueueStatusServer/main.py +++ b/WebKitTools/QueueStatusServer/main.py @@ -43,6 +43,8 @@ from handlers.statusbubble import StatusBubble from handlers.svnrevision import SVNRevision from handlers.updatestatus import UpdateStatus from handlers.updatesvnrevision import UpdateSVNRevision +from handlers.updateworkitems import UpdateWorkItems + webapp.template.register_template_library('filters.webkit_extras') @@ -57,6 +59,7 @@ routes = [ (r'/svn-revision/(.*)', SVNRevision), (r'/queue-status/(.*)', RecentStatus), ('/update-status', UpdateStatus), + ('/update-work-items', UpdateWorkItems), ('/update-svn-revision', UpdateSVNRevision), ] diff --git a/WebKitTools/QueueStatusServer/model/attachment.py b/WebKitTools/QueueStatusServer/model/attachment.py index 751f78e..9ae59e8 100644 --- a/WebKitTools/QueueStatusServer/model/attachment.py +++ b/WebKitTools/QueueStatusServer/model/attachment.py @@ -30,8 +30,9 @@ import re from google.appengine.api import memcache -from model.queues import queues +from model.queues import queues, name_with_underscores from model.queuestatus import QueueStatus +from model.workitems import WorkItems class Attachment(object): @@ -60,6 +61,7 @@ class Attachment(object): def __init__(self, attachment_id): self.id = attachment_id self._summary = None + self._cached_queue_positions = None def summary(self): if self._summary: @@ -71,11 +73,7 @@ class Attachment(object): memcache.set(str(self.id), self._summary, namespace="attachment-summary") return self._summary - def _dash_to_underscore(self, dashed_name): - regexp = re.compile("-") - return regexp.sub("_", dashed_name) - - def _state_from_status(self, status): + def state_from_queue_status(self, status): table = { "Pass" : "pass", "Fail" : "fail", @@ -89,6 +87,40 @@ class Attachment(object): return "pending" return None + def position_in_queue(self, queue_name): + return self._queue_positions().get(queue_name) + + def status_for_queue(self, queue_name): + underscore_queue_name = name_with_underscores(queue_name) + # summary() is a horrible API and should be killed. + queue_summary = self.summary().get(underscore_queue_name) + if not queue_summary: + return None + return queue_summary.get("status") + + def bug_id(self): + return self.summary().get("bug_id") + + def _queue_positions(self): + if self._cached_queue_positions: + return self._cached_queue_positions + # FIXME: Should we be mem-caching this? + self._cached_queue_positions = self._calculate_queue_positions() + return self._cached_queue_positions + + def _calculate_queue_positions(self): + queue_positions = {} + for work_items in WorkItems.all().fetch(limit=len(queues)): + queue_name = str(work_items.queue_name) + try: + position = work_items.item_ids.index(self.id) + # Display 1-based indecies to the user. + queue_positions[queue_name] = position + 1 + except ValueError, e: + queue_positions[queue_name] = None + return queue_positions + + # FIXME: This is controller/view code and does not belong in a model. def _fetch_summary(self): summary = { "attachment_id" : self.id } @@ -102,8 +134,8 @@ class Attachment(object): summary[queue] = None status = QueueStatus.all().filter('queue_name =', queue).filter('active_patch_id =', self.id).order('-date').get() if status: - summary[self._dash_to_underscore(queue)] = { - "state" : self._state_from_status(status), - "status" : status, + summary[name_with_underscores(queue)] = { + "state": self.state_from_queue_status(status), + "status": status, } return summary diff --git a/WebKitTools/QueueStatusServer/model/queues.py b/WebKitTools/QueueStatusServer/model/queues.py index 57463de..46f2f15 100644 --- a/WebKitTools/QueueStatusServer/model/queues.py +++ b/WebKitTools/QueueStatusServer/model/queues.py @@ -1,4 +1,4 @@ -# Copyright (C) 2009 Google Inc. All rights reserved. +# Copyright (C) 2010 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 @@ -26,6 +26,9 @@ # (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 re + + queues = [ "commit-queue", "style-queue", @@ -36,3 +39,8 @@ queues = [ "mac-ews", "win-ews", ] + + +def name_with_underscores(dashed_name): + regexp = re.compile("-") + return regexp.sub("_", dashed_name) diff --git a/WebKitTools/QueueStatusServer/model/workitems.py b/WebKitTools/QueueStatusServer/model/workitems.py new file mode 100644 index 0000000..3ea59cb --- /dev/null +++ b/WebKitTools/QueueStatusServer/model/workitems.py @@ -0,0 +1,35 @@ +# Copyright (C) 2010 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. + +from google.appengine.ext import db + + +class WorkItems(db.Model): + queue_name = db.StringProperty() + item_ids = db.ListProperty(int) + date = db.DateTimeProperty(auto_now_add=True) diff --git a/WebKitTools/QueueStatusServer/templates/dashboard.html b/WebKitTools/QueueStatusServer/templates/dashboard.html index 14b7ede..c5c2359 100644 --- a/WebKitTools/QueueStatusServer/templates/dashboard.html +++ b/WebKitTools/QueueStatusServer/templates/dashboard.html @@ -17,57 +17,27 @@ function statusDetail(patch_id) { <tr> <th>Bug</th> <th>Attachment</th> - <th>Style</th> - <th>Cr-Linux</th> - <th>Cr-Win</th> - <th>Qt</th> - <th>Mac</th> - <th>Win</th> - <th>Gtk</th> - <th>Commit</th> + {% for header in headers %} + <th>{{ header }}</th> + {% endfor %} </tr> </thead> - <tbody>{% for summary in summaries %} + <tbody>{% for row in rows %} <tr> <td class="status"> - {{ summary.bug_id|force_escape|webkit_bug_id|safe }} + {{ row.bug_id|force_escape|webkit_bug_id|safe }} </td> <td class="status"> - {{ summary.attachment_id|force_escape|webkit_attachment_id|safe }} + {{ row.attachment_id|force_escape|webkit_attachment_id|safe }} </td> - <!-- FIXME: Find some way to remove this copy-and-paste code! --> - <td class="status {{ summary.style_queue.state }}"{% if summary.style_queue.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.style_queue.status.date|timesince }}"{% endif %}> - </td> - <td class="status {{ summary.chromium_ews.state }}"{% if summary.chromium_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.chromium_ews.status.date|timesince }} ago"{% endif %}> - </td> - <td class="status {{ summary.cr_win_ews.state }}"{% if summary.cr_win_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.cr_win_ews.status.date|timesince }} ago"{% endif %}> - </td> - <td class="status {{ summary.qt_ews.state }}"{% if summary.qt_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.qt_ews.status.date|timesince }} ago"{% endif %}> - </td> - <td class="status {{ summary.mac_ews.state }}"{% if summary.mac_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.mac_ews.status.date|timesince }} ago"{% endif %}> - </td> - <td class="status {{ summary.win_ews.state }}"{% if summary.win_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.win_ews.status.date|timesince }} ago"{% endif %}> - </td> - <td class="status {{ summary.gtk_ews.state }}"{% if summary.gtk_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.gtk_ews.status.date|timesince }} ago"{% endif %}> - </td> - <td class="status {{ summary.commit_queue.state }}"{% if summary.commit_queue.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.commit_queue.status.date|timesince }} ago"{% endif %}> + {% for bubble in row.bubbles %} + <td class="status {{ bubble.status_class }}" + {% if bubble.status %} + onclick="statusDetail({{ row.attachment_id }})" + title="{{ bubble.status_date|timesince }}" + {% endif %}> </td> + {% endfor %} </tr>{% endfor %} </tbody> </table> diff --git a/WebKitTools/QueueStatusServer/templates/statusbubble.html b/WebKitTools/QueueStatusServer/templates/statusbubble.html index c6e2134..3102741 100644 --- a/WebKitTools/QueueStatusServer/templates/statusbubble.html +++ b/WebKitTools/QueueStatusServer/templates/statusbubble.html @@ -18,26 +18,26 @@ body { border: 1px solid #AAA; background-color: white; font-size: 11px; + cursor: pointer; +} +.none { + cursor: auto; } .pass { background-color: #8FDF5F; border: 1px solid #4F8530; - cursor: pointer; } .fail { background-color: #E98080; border: 1px solid #A77272; - cursor: pointer; } .pending { background-color: #FFFC6C; border: 1px solid #C5C56D; - cursor: pointer; } .error { background-color: #E0B0FF; border: 1px solid #ACA0B3; - cursor: pointer; } </style> <script> @@ -47,41 +47,15 @@ function statusDetail(patch_id) { </script> </head> <body> -<!-- FIXME: Find some way to remove this copy-and-paste code! --> -<div class="status {{ summary.style_queue.state }}"{% if summary.style_queue.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.style_queue.status.date|timesince }} ago"{% endif %}> - style -</div> -<div class="status {{ summary.chromium_ews.state }}"{% if summary.chromium_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.chromium_ews.status.date|timesince }} ago"{% endif %}> - cr-linux -</div> -<div class="status {{ summary.cr_win_ews.state }}"{% if summary.cr_win_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.cr_win_ews.status.date|timesince }} ago"{% endif %}> - cr-win -</div> -<div class="status {{ summary.qt_ews.state }}"{% if summary.qt_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.qt_ews.status.date|timesince }} ago"{% endif %}> - qt -</div> -<div class="status {{ summary.gtk_ews.state }}"{% if summary.gtk_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.gtk_ews.status.date|timesince }} ago"{% endif %}> - gtk -</div> -<div class="status {{ summary.mac_ews.state }}"{% if summary.mac_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.mac_ews.status.date|timesince }} ago"{% endif %}> - mac -</div> -<div class="status {{ summary.win_ews.state }}"{% if summary.win_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.win_ews.status.date|timesince }} ago"{% endif %}> - win +{% for bubble in bubbles %} +<div class="status {{ bubble.state }}"{% if bubble.status %} + onclick="statusDetail({{ bubble.attachment_id }})" + title="{{ bubble.status.date|timesince }} ago"{% endif %}> + {{ bubble.name }} + {% if bubble.queue_position %} + (#{{ bubble.queue_position }}) + {% endif %} </div> +{% endfor %} </body> </html> diff --git a/WebKitTools/QueueStatusServer/templates/updateworkitems.html b/WebKitTools/QueueStatusServer/templates/updateworkitems.html new file mode 100644 index 0000000..b086fc3 --- /dev/null +++ b/WebKitTools/QueueStatusServer/templates/updateworkitems.html @@ -0,0 +1,8 @@ +<form name="update_work_items" enctype="multipart/form-data" method="post"> +Update work items for a queue: <input name="queue_name"> + <div> + Work Items: + <input name="work_items"> + </div> + <div><input type="submit" value="Update Work Items"></div> +</form> |