<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[174087] trunk/Tools</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/174087">174087</a></dd>
<dt>Author</dt> <dd>ap@apple.com</dd>
<dt>Date</dt> <dd>2014-09-29 14:55:04 -0700 (Mon, 29 Sep 2014)</dd>
</dl>
<h3>Log Message</h3>
<pre>Improve Bugzilla status bubbles
https://bugs.webkit.org/show_bug.cgi?id=137232
Reviewed by Ryosuke Niwa.
* QueueStatusServer/app.yaml: Will update again with an actual version when landing.
* QueueStatusServer/handlers/statusbubble.py: Eliminated yellow color, added
blue and orange. Significantly extended tooltips. Made bubbles show up even for queues
that are stuck, as it was only confusing that they disappeared after 99.
* QueueStatusServer/model/attachment.py: Removed functionality that was only used
by old bubbles. We need a lot more information to determine color, so the implementation
can not be here.
* QueueStatusServer/templates/statusbubble.html: Updated colors in CSS, made bubbles
always have a link for consistency. Added code to convert timestamps in tooltips
to local time zone.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsQueueStatusServerappyaml">trunk/Tools/QueueStatusServer/app.yaml</a></li>
<li><a href="#trunkToolsQueueStatusServerhandlersstatusbubblepy">trunk/Tools/QueueStatusServer/handlers/statusbubble.py</a></li>
<li><a href="#trunkToolsQueueStatusServermodelattachmentpy">trunk/Tools/QueueStatusServer/model/attachment.py</a></li>
<li><a href="#trunkToolsQueueStatusServertemplatesstatusbubblehtml">trunk/Tools/QueueStatusServer/templates/statusbubble.html</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (174086 => 174087)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog        2014-09-29 21:26:06 UTC (rev 174086)
+++ trunk/Tools/ChangeLog        2014-09-29 21:55:04 UTC (rev 174087)
</span><span class="lines">@@ -1,3 +1,24 @@
</span><ins>+2014-09-29 Alexey Proskuryakov <ap@apple.com>
+
+ Improve Bugzilla status bubbles
+ https://bugs.webkit.org/show_bug.cgi?id=137232
+
+ Reviewed by Ryosuke Niwa.
+
+ * QueueStatusServer/app.yaml: Will update again with an actual version when landing.
+
+ * QueueStatusServer/handlers/statusbubble.py: Eliminated yellow color, added
+ blue and orange. Significantly extended tooltips. Made bubbles show up even for queues
+ that are stuck, as it was only confusing that they disappeared after 99.
+
+ * QueueStatusServer/model/attachment.py: Removed functionality that was only used
+ by old bubbles. We need a lot more information to determine color, so the implementation
+ can not be here.
+
+ * QueueStatusServer/templates/statusbubble.html: Updated colors in CSS, made bubbles
+ always have a link for consistency. Added code to convert timestamps in tooltips
+ to local time zone.
+
</ins><span class="cx"> 2014-09-29 Commit Queue <commit-queue@webkit.org>
</span><span class="cx">
</span><span class="cx"> Unreviewed, rolling out r174045.
</span></span></pre></div>
<a id="trunkToolsQueueStatusServerappyaml"></a>
<div class="modfile"><h4>Modified: trunk/Tools/QueueStatusServer/app.yaml (174086 => 174087)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/QueueStatusServer/app.yaml        2014-09-29 21:26:06 UTC (rev 174086)
+++ trunk/Tools/QueueStatusServer/app.yaml        2014-09-29 21:55:04 UTC (rev 174087)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> application: webkit-queues
</span><del>-version: 174015 # Bugzilla bug ID of last major change
</del><ins>+version: 174087 # Bugzilla bug ID of last major change
</ins><span class="cx"> runtime: python
</span><span class="cx"> api_version: 1
</span><span class="cx">
</span></span></pre></div>
<a id="trunkToolsQueueStatusServerhandlersstatusbubblepy"></a>
<div class="modfile"><h4>Modified: trunk/Tools/QueueStatusServer/handlers/statusbubble.py (174086 => 174087)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/QueueStatusServer/handlers/statusbubble.py        2014-09-29 21:26:06 UTC (rev 174086)
+++ trunk/Tools/QueueStatusServer/handlers/statusbubble.py        2014-09-29 21:55:04 UTC (rev 174087)
</span><span class="lines">@@ -32,41 +32,151 @@
</span><span class="cx"> from google.appengine.ext.webapp import template
</span><span class="cx">
</span><span class="cx"> from model.attachment import Attachment
</span><ins>+from model.activeworkitems import ActiveWorkItems
+from model.patchlog import PatchLog
+from model.queues import Queue
+from model.queuestatus import QueueStatus
</ins><span class="cx"> from model.workitems import WorkItems
</span><del>-from model.queues import Queue
</del><ins>+from sets import Set
</ins><span class="cx">
</span><ins>+progress_statuses = Set([
+ "Cleaned working directory",
+ "Updated working directory",
+ "Applied patch",
+ "Built patch",
+ "Watchlist applied",
+ "Style checked",
+ "ChangeLog validated",
+ "Built patch",
+ "Able to build without patch",
+ "Passed tests",
+ "Able to pass tests without patch",
+ "Landed patch"
+])
</ins><span class="cx">
</span><span class="cx"> class StatusBubble(webapp.RequestHandler):
</span><ins>+ def _iso_time(self, time):
+ return "[[" + time.isoformat() + "]]"
+
+ # queue_position includes items that are already active, so it's misleading.
+ # For a queue that has 8 bots, being #9 in the queue actually means being #1.
+ def _real_queue_position(self, queue, queue_position):
+ active_work_items = queue.active_work_items().item_ids
+ if active_work_items:
+ return queue_position - len(active_work_items)
+ else:
+ return queue_position
+
+ def _latest_resultative_status(self, statuses):
+ for status in statuses:
+ if not status.message in progress_statuses:
+ return status
+ return None
+
+ def _build_message_for_provisional_failure(self, queue, attachment, queue_position, statuses):
+ patch_log = PatchLog.lookup_if_exists(attachment.id, queue.name())
+ if not patch_log:
+ return "Internal error. No PatchLog entry in database."
+
+ is_active = attachment.id in queue.active_work_items().item_ids
+ try_count = patch_log.retry_count + (not is_active) # retry_count is updated when a new attempt starts.
+ latest_resultative_status = self._latest_resultative_status(statuses)
+ tree_is_red = latest_resultative_status.message == "Unable to pass tests without patch (tree is red?)" or latest_resultative_status.message == "Unable to build without patch"
+
+ message = latest_resultative_status.message + "."
+ if is_active:
+ if tree_is_red:
+ message += "\n\nTrying again now."
+ else:
+ message += "\n\nThis result is not final, as the issue could be a pre-existing one. Trying to determine that now."
+ if try_count == 1:
+ message += "\n\nPreviously completed a round of testing, but couldn't arrive to a definitive conclusion."
+ elif try_count > 1:
+ message += "\n\nPreviously completed " + str(try_count) + " rounds of testing, but couldn't arrive to a definitive conclusion."
+ else:
+ real_queue_position = self._real_queue_position(queue, queue_position)
+ if tree_is_red:
+ message += "\n\nWill try again, currently #" + str(real_queue_position) + " in queue."
+ else:
+ message += "\n\nThis result is not final, as the issue can be a pre-existing one. "
+ if try_count == 1:
+ message += "Completed one round "
+ else:
+ message += "Completed " + str(try_count) + " rounds "
+ message += "of testing trying to determine that, but couldn't arrive to a definitive conclusion yet.\n\nWill try again, currently #" + str(real_queue_position) + " in queue."
+ message += "\n\nPlease click the bubble for detailed results.\n\n" + self._iso_time(statuses[0].date)
+ return message
+
</ins><span class="cx"> def _build_bubble(self, queue, attachment, queue_position):
</span><del>- queue_status = attachment.status_for_queue(queue)
</del><span class="cx"> bubble = {
</span><span class="cx"> "name": queue.short_name().lower(),
</span><span class="cx"> "attachment_id": attachment.id,
</span><del>- "queue_position": queue_position,
- "state": attachment.state_from_queue_status(queue_status) if queue_status else "none",
- "status": queue_status,
</del><span class="cx"> }
</span><ins>+ # 10 recent statuses is enough to always include a resultative one, if there were any at all.
+ statuses = QueueStatus.all().filter('queue_name =', queue.name()).filter('active_patch_id =', attachment.id).order('-date').fetch(limit=10)
+ if not statuses:
+ if attachment.id in queue.active_work_items().item_ids:
+ bubble["state"] = "started"
+ bubble["details_message"] = "Started processing, no output yet.\n\n" + self._iso_time(queue.active_work_items().time_for_item(attachment.id))
+ else:
+ real_queue_position = self._real_queue_position(queue, queue_position)
+ bubble["state"] = "none"
+ bubble["details_message"] = "Waiting in queue, processing has not started yet.\n\nPosition in queue: " + str(real_queue_position)
+ bubble["queue_position"] = real_queue_position
+ else:
+ latest_resultative_status = self._latest_resultative_status(statuses)
+ if not latest_resultative_status:
+ bubble["state"] = "started"
+ bubble["details_message"] = ("Started processing.\n\nRecent messages:\n\n"
+ + "\n".join([status.message for status in statuses]) + "\n\n" + self._iso_time(statuses[0].date))
+ elif statuses[0].message == "Pass":
+ bubble["state"] = "pass"
+ bubble["details_message"] = "Pass\n\n" + self._iso_time(statuses[0].date)
+ elif statuses[0].message == "Fail":
+ bubble["state"] = "fail"
+ bubble["details_message"] = statuses[1].message + "\n\n" + self._iso_time(statuses[0].date)
+ elif statuses[0].message == "Error: " + queue.name() + " did not process patch.":
+ bubble["state"] = "none"
+ bubble["details_message"] = "The patch is no longer eligible for processing."
+ if len(statuses) > 1:
+ bubble["details_message"] += " Recent messages:\n\n" + "\n".join([status.message for status in statuses[1:]]) + "\n\n" + self._iso_time(statuses[0].date)
+ elif statuses[0].message == "Error: " + queue.name() + " unable to apply patch.":
+ bubble["state"] = "fail"
+ bubble["details_message"] = statuses[1].message + "\n\n" + self._iso_time(statuses[0].date)
+ elif statuses[0].message.startswith("Error: "):
+ bubble["state"] = "error"
+ bubble["details_message"] = "\n".join([status.message for status in statuses]) + "\n\n" + self._iso_time(statuses[0].date)
+ elif queue_position:
+ bubble["state"] = "provisional-fail"
+ bubble["details_message"] = self._build_message_for_provisional_failure(queue, attachment, queue_position, statuses)
+ else:
+ bubble["state"] = "error"
+ bubble["details_message"] = ("Internal error. Latest status implies that the patch should be in queue, but it is not. Recent messages:\n\n"
+ + "\n".join([status.message for status in statuses]) + "\n\n" + self._iso_time(statuses[0].date))
+
+ if "details_message" in bubble:
+ bubble["details_message"] = queue.display_name() + "\n\n" + bubble["details_message"]
+
</ins><span class="cx"> return bubble
</span><span class="cx">
</span><del>- def _have_status_for(self, attachment, queue):
- # Any pending queue is shown.
</del><ins>+ def _should_show_bubble_for(self, attachment, queue):
+ # Any pending queue is shown.
</ins><span class="cx"> if attachment.position_in_queue(queue):
</span><span class="cx"> return True
</span><del>- # Complete ewses are also shown.
</del><ins>+ # EWS queues are also shown when complete.
</ins><span class="cx"> return bool(queue.is_ews() and attachment.status_for_queue(queue))
</span><span class="cx">
</span><span class="cx"> def _build_bubbles_for_attachment(self, attachment):
</span><span class="cx"> show_submit_to_ews = True
</span><span class="cx"> bubbles = []
</span><span class="cx"> for queue in Queue.all():
</span><del>- if not self._have_status_for(attachment, queue):
</del><ins>+ if not self._should_show_bubble_for(attachment, queue):
</ins><span class="cx"> continue
</span><span class="cx"> queue_position = attachment.position_in_queue(queue)
</span><del>- if queue_position and queue_position >= 100:
- # This queue is so far behind it's not even worth showing.
- continue
- bubbles.append(self._build_bubble(queue, attachment, queue_position))
- # If even one ews has status, we don't show the submit-to-ews button.
</del><ins>+ bubble = self._build_bubble(queue, attachment, queue_position)
+ if bubble:
+ bubbles.append(bubble)
+ # If at least one EWS queue has status, we don't show the submit-to-ews button.
</ins><span class="cx"> if queue.is_ews():
</span><span class="cx"> show_submit_to_ews = False
</span><span class="cx">
</span></span></pre></div>
<a id="trunkToolsQueueStatusServermodelattachmentpy"></a>
<div class="modfile"><h4>Modified: trunk/Tools/QueueStatusServer/model/attachment.py (174086 => 174087)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/QueueStatusServer/model/attachment.py        2014-09-29 21:26:06 UTC (rev 174086)
+++ trunk/Tools/QueueStatusServer/model/attachment.py        2014-09-29 21:55:04 UTC (rev 174087)
</span><span class="lines">@@ -34,24 +34,6 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> class Attachment(object):
</span><del>- @classmethod
- def recent(cls, limit=1):
- statuses = QueueStatus.all().order("-date")
- # Notice that we use both a set and a list here to keep the -date ordering.
- ids = []
- visited_ids = set()
- for status in statuses:
- attachment_id = status.active_patch_id
- if not attachment_id:
- continue
- if attachment_id in visited_ids:
- continue
- visited_ids.add(attachment_id)
- ids.append(attachment_id)
- if len(visited_ids) >= limit:
- break
- return map(cls, ids)
-
</del><span class="cx"> def __init__(self, attachment_id):
</span><span class="cx"> self.id = attachment_id
</span><span class="cx"> self._summary = None
</span><span class="lines">@@ -63,20 +45,6 @@
</span><span class="cx"> self._summary = self._fetch_summary()
</span><span class="cx"> return self._summary
</span><span class="cx">
</span><del>- def state_from_queue_status(self, status):
- table = {
- "Pass" : "pass",
- "Fail" : "fail",
- }
- state = table.get(status.message)
- if state:
- return state
- if status.message.startswith("Error:"):
- return "error"
- if status:
- return "pending"
- return None
-
</del><span class="cx"> def position_in_queue(self, queue):
</span><span class="cx"> return self._queue_positions().get(queue.name())
</span><span class="cx">
</span><span class="lines">@@ -117,7 +85,6 @@
</span><span class="cx"> if status:
</span><span class="cx"> # summary() is a horrible API and should be killed.
</span><span class="cx"> summary[queue.name_with_underscores()] = {
</span><del>- "state": self.state_from_queue_status(status),
</del><span class="cx"> "status": status,
</span><span class="cx"> }
</span><span class="cx"> return summary
</span></span></pre></div>
<a id="trunkToolsQueueStatusServertemplatesstatusbubblehtml"></a>
<div class="modfile"><h4>Modified: trunk/Tools/QueueStatusServer/templates/statusbubble.html (174086 => 174087)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/QueueStatusServer/templates/statusbubble.html        2014-09-29 21:26:06 UTC (rev 174086)
+++ trunk/Tools/QueueStatusServer/templates/statusbubble.html        2014-09-29 21:55:04 UTC (rev 174087)
</span><span class="lines">@@ -19,7 +19,7 @@
</span><span class="cx"> -moz-border-radius: 5px;
</span><span class="cx"> -webkit-border-radius: 5px;
</span><span class="cx"> border-radius: 5px;
</span><del>- border: 1px solid #AAA;
</del><ins>+ border: 1px solid rgba(1, 1, 1, 0.3);
</ins><span class="cx"> background-color: white;
</span><span class="cx"> font-size: 11px;
</span><span class="cx"> cursor: pointer;
</span><span class="lines">@@ -27,29 +27,22 @@
</span><span class="cx"> color: black;
</span><span class="cx"> }
</span><span class="cx"> .status:hover {
</span><del>- border-color: #666;
</del><ins>+ border-color: rgba(1, 1, 1, 0.7);
</ins><span class="cx"> }
</span><del>-.none {
- cursor: auto;
-}
-.none:hover {
- border-color: #AAA;
-}
</del><span class="cx"> .pass {
</span><span class="cx"> background-color: #8FDF5F;
</span><del>- border: 1px solid #4F8530;
</del><span class="cx"> }
</span><span class="cx"> .fail {
</span><span class="cx"> background-color: #E98080;
</span><del>- border: 1px solid #A77272;
</del><span class="cx"> }
</span><del>-.pending {
- background-color: #FFFC6C;
- border: 1px solid #C5C56D;
</del><ins>+.started {
+ background-color: #D8FFFA;
</ins><span class="cx"> }
</span><ins>+.provisional-fail {
+ background-color: #FFAF05;
+}
</ins><span class="cx"> .error {
</span><span class="cx"> background-color: #E0B0FF;
</span><del>- border: 1px solid #ACA0B3;
</del><span class="cx"> }
</span><span class="cx"> .queue_position {
</span><span class="cx"> font-size: 9px;
</span><span class="lines">@@ -72,9 +65,9 @@
</span><span class="cx"> <div id="bubbleContainer">
</span><span class="cx"> {% for bubble in bubbles %}
</span><span class="cx"> <a class="status {{ bubble.state }}" target="_top"
</span><del>- {% if bubble.status %}
</del><span class="cx"> href="/patch/{{ bubble.attachment_id }}"
</span><del>- title="{{ bubble.status.date|timesince }} ago"
</del><ins>+ {% if bubble.details_message %}
+ title="{{ bubble.details_message }}"
</ins><span class="cx"> {% endif %}
</span><span class="cx"> >
</span><span class="cx"> {{ bubble.name }}
</span><span class="lines">@@ -91,6 +84,20 @@
</span><span class="cx"> <input class="status" type="submit" value="Submit for EWS analysis">
</span><span class="cx"> </form>
</span><span class="cx"> {% endif %}
</span><ins>+
+<script>
+// Convert from UTC dates to local.
+var bubbles = document.getElementsByClassName("status")
+for (var i = 0; i < bubbles.length; ++i) {
+ var bubble = bubbles[i];
+ if (bubble.hasAttribute("title")) {
+ var newTitle = bubble.getAttribute("title").replace(/\[\[(.+)\]\]/, function(match, isoDateString) {
+ return new Date(isoDateString).toString();
+ });
+ bubble.setAttribute("title", newTitle);
+ }
+}
+</script>
</ins><span class="cx"> </div>
</span><span class="cx"> </body>
</span><span class="cx"> </html>
</span></span></pre>
</div>
</div>
</body>
</html>