Changeset 174087 in webkit


Ignore:
Timestamp:
Sep 29, 2014 2:55:04 PM (10 years ago)
Author:
ap@apple.com
Message:

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.

Location:
trunk/Tools
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Tools/ChangeLog

    r174086 r174087  
     12014-09-29  Alexey Proskuryakov  <ap@apple.com>
     2
     3        Improve Bugzilla status bubbles
     4        https://bugs.webkit.org/show_bug.cgi?id=137232
     5
     6        Reviewed by Ryosuke Niwa.
     7
     8        * QueueStatusServer/app.yaml: Will update again with an actual version when landing.
     9
     10        * QueueStatusServer/handlers/statusbubble.py: Eliminated yellow color, added
     11        blue and orange. Significantly extended tooltips. Made bubbles show up even for queues
     12        that are stuck, as it was only confusing that they disappeared after 99.
     13
     14        * QueueStatusServer/model/attachment.py: Removed functionality that was only used
     15        by old bubbles. We need a lot more information to determine color, so the implementation
     16        can not be here.
     17
     18        * QueueStatusServer/templates/statusbubble.html: Updated colors in CSS, made bubbles
     19        always have a link for consistency. Added code to convert timestamps in tooltips
     20        to local time zone.
     21
    1222014-09-29  Commit Queue  <commit-queue@webkit.org>
    223
  • trunk/Tools/QueueStatusServer/app.yaml

    r174015 r174087  
    11application: webkit-queues
    2 version: 174015 # Bugzilla bug ID of last major change
     2version: 174087 # Bugzilla bug ID of last major change
    33runtime: python
    44api_version: 1
  • trunk/Tools/QueueStatusServer/handlers/statusbubble.py

    r97523 r174087  
    3333
    3434from model.attachment import Attachment
     35from model.activeworkitems import ActiveWorkItems
     36from model.patchlog import PatchLog
     37from model.queues import Queue
     38from model.queuestatus import QueueStatus
    3539from model.workitems import WorkItems
    36 from model.queues import Queue
     40from sets import Set
    3741
     42progress_statuses = Set([
     43    "Cleaned working directory",
     44    "Updated working directory",
     45    "Applied patch",
     46    "Built patch",
     47    "Watchlist applied",
     48    "Style checked",
     49    "ChangeLog validated",
     50    "Built patch",
     51    "Able to build without patch",
     52    "Passed tests",
     53    "Able to pass tests without patch",
     54    "Landed patch"
     55])
    3856
    3957class StatusBubble(webapp.RequestHandler):
     58    def _iso_time(self, time):
     59        return "[[" + time.isoformat() + "]]"
     60
     61    # queue_position includes items that are already active, so it's misleading.
     62    # For a queue that has 8 bots, being #9 in the queue actually means being #1.
     63    def _real_queue_position(self, queue, queue_position):
     64        active_work_items = queue.active_work_items().item_ids
     65        if active_work_items:
     66            return queue_position - len(active_work_items)
     67        else:
     68            return queue_position
     69
     70    def _latest_resultative_status(self, statuses):
     71        for status in statuses:
     72            if not status.message in progress_statuses:
     73                return status
     74        return None
     75
     76    def _build_message_for_provisional_failure(self, queue, attachment, queue_position, statuses):
     77        patch_log = PatchLog.lookup_if_exists(attachment.id, queue.name())
     78        if not patch_log:
     79            return "Internal error. No PatchLog entry in database."
     80
     81        is_active = attachment.id in queue.active_work_items().item_ids
     82        try_count = patch_log.retry_count + (not is_active)  # retry_count is updated when a new attempt starts.
     83        latest_resultative_status = self._latest_resultative_status(statuses)
     84        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"
     85
     86        message = latest_resultative_status.message + "."
     87        if is_active:
     88            if tree_is_red:
     89                message += "\n\nTrying again now."
     90            else:
     91                message += "\n\nThis result is not final, as the issue could be a pre-existing one. Trying to determine that now."
     92                if try_count == 1:
     93                    message += "\n\nPreviously completed a round of testing, but couldn't arrive to a definitive conclusion."
     94                elif try_count > 1:
     95                    message += "\n\nPreviously completed " + str(try_count) + " rounds of testing, but couldn't arrive to a definitive conclusion."
     96        else:
     97            real_queue_position = self._real_queue_position(queue, queue_position)
     98            if tree_is_red:
     99                message += "\n\nWill try again, currently #" + str(real_queue_position) + " in queue."
     100            else:
     101                message += "\n\nThis result is not final, as the issue can be a pre-existing one. "
     102                if try_count == 1:
     103                    message += "Completed one round "
     104                else:
     105                    message += "Completed " + str(try_count) + " rounds "
     106                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."
     107        message += "\n\nPlease click the bubble for detailed results.\n\n" + self._iso_time(statuses[0].date)
     108        return message
     109
    40110    def _build_bubble(self, queue, attachment, queue_position):
    41         queue_status = attachment.status_for_queue(queue)
    42111        bubble = {
    43112            "name": queue.short_name().lower(),
    44113            "attachment_id": attachment.id,
    45             "queue_position": queue_position,
    46             "state": attachment.state_from_queue_status(queue_status) if queue_status else "none",
    47             "status": queue_status,
    48114        }
     115        # 10 recent statuses is enough to always include a resultative one, if there were any at all.
     116        statuses = QueueStatus.all().filter('queue_name =', queue.name()).filter('active_patch_id =', attachment.id).order('-date').fetch(limit=10)
     117        if not statuses:
     118            if attachment.id in queue.active_work_items().item_ids:
     119                bubble["state"] = "started"
     120                bubble["details_message"] = "Started processing, no output yet.\n\n" + self._iso_time(queue.active_work_items().time_for_item(attachment.id))
     121            else:
     122                real_queue_position = self._real_queue_position(queue, queue_position)
     123                bubble["state"] = "none"
     124                bubble["details_message"] = "Waiting in queue, processing has not started yet.\n\nPosition in queue: " + str(real_queue_position)
     125                bubble["queue_position"] = real_queue_position
     126        else:
     127            latest_resultative_status = self._latest_resultative_status(statuses)
     128            if not latest_resultative_status:
     129                bubble["state"] = "started"
     130                bubble["details_message"] = ("Started processing.\n\nRecent messages:\n\n"
     131                    + "\n".join([status.message for status in statuses]) + "\n\n" + self._iso_time(statuses[0].date))
     132            elif statuses[0].message == "Pass":
     133                bubble["state"] = "pass"
     134                bubble["details_message"] = "Pass\n\n" + self._iso_time(statuses[0].date)
     135            elif statuses[0].message == "Fail":
     136                bubble["state"] = "fail"
     137                bubble["details_message"] = statuses[1].message + "\n\n" + self._iso_time(statuses[0].date)
     138            elif statuses[0].message == "Error: " + queue.name() + " did not process patch.":
     139                bubble["state"] = "none"
     140                bubble["details_message"] = "The patch is no longer eligible for processing."
     141                if len(statuses) > 1:
     142                    bubble["details_message"] += " Recent messages:\n\n" + "\n".join([status.message for status in statuses[1:]]) + "\n\n" + self._iso_time(statuses[0].date)
     143            elif statuses[0].message == "Error: " + queue.name() + " unable to apply patch.":
     144                bubble["state"] = "fail"
     145                bubble["details_message"] = statuses[1].message + "\n\n" + self._iso_time(statuses[0].date)
     146            elif statuses[0].message.startswith("Error: "):
     147                bubble["state"] = "error"
     148                bubble["details_message"] = "\n".join([status.message for status in statuses]) + "\n\n" + self._iso_time(statuses[0].date)
     149            elif queue_position:
     150                bubble["state"] = "provisional-fail"
     151                bubble["details_message"] = self._build_message_for_provisional_failure(queue, attachment, queue_position, statuses)
     152            else:
     153                bubble["state"] = "error"
     154                bubble["details_message"] = ("Internal error. Latest status implies that the patch should be in queue, but it is not. Recent messages:\n\n"
     155                    + "\n".join([status.message for status in statuses]) + "\n\n" + self._iso_time(statuses[0].date))
     156
     157        if "details_message" in bubble:
     158            bubble["details_message"] = queue.display_name() + "\n\n" + bubble["details_message"]
     159
    49160        return bubble
    50161
    51     def _have_status_for(self, attachment, queue):
    52         # Any pending queue is shown.
     162    def _should_show_bubble_for(self, attachment, queue):
     163         # Any pending queue is shown.
    53164        if attachment.position_in_queue(queue):
    54165            return True
    55         # Complete ewses are also shown.
     166        # EWS queues are also shown when complete.
    56167        return bool(queue.is_ews() and attachment.status_for_queue(queue))
    57168
     
    60171        bubbles = []
    61172        for queue in Queue.all():
    62             if not self._have_status_for(attachment, queue):
     173            if not self._should_show_bubble_for(attachment, queue):
    63174                continue
    64175            queue_position = attachment.position_in_queue(queue)
    65             if queue_position and queue_position >= 100:
    66                 # This queue is so far behind it's not even worth showing.
    67                 continue
    68             bubbles.append(self._build_bubble(queue, attachment, queue_position))
    69             # If even one ews has status, we don't show the submit-to-ews button.
     176            bubble = self._build_bubble(queue, attachment, queue_position)
     177            if bubble:
     178                bubbles.append(bubble)
     179            # If at least one EWS queue has status, we don't show the submit-to-ews button.
    70180            if queue.is_ews():
    71181                show_submit_to_ews = False
  • trunk/Tools/QueueStatusServer/model/attachment.py

    r156803 r174087  
    3535
    3636class Attachment(object):
    37     @classmethod
    38     def recent(cls, limit=1):
    39         statuses = QueueStatus.all().order("-date")
    40         # Notice that we use both a set and a list here to keep the -date ordering.
    41         ids = []
    42         visited_ids = set()
    43         for status in statuses:
    44             attachment_id = status.active_patch_id
    45             if not attachment_id:
    46                 continue
    47             if attachment_id in visited_ids:
    48                 continue
    49             visited_ids.add(attachment_id)
    50             ids.append(attachment_id)
    51             if len(visited_ids) >= limit:
    52                 break
    53         return map(cls, ids)
    54 
    5537    def __init__(self, attachment_id):
    5638        self.id = attachment_id
     
    6345        self._summary = self._fetch_summary()
    6446        return self._summary
    65 
    66     def state_from_queue_status(self, status):
    67         table = {
    68             "Pass" : "pass",
    69             "Fail" : "fail",
    70         }
    71         state = table.get(status.message)
    72         if state:
    73             return state
    74         if status.message.startswith("Error:"):
    75             return "error"
    76         if status:
    77             return "pending"
    78         return None
    7947
    8048    def position_in_queue(self, queue):
     
    11886                # summary() is a horrible API and should be killed.
    11987                summary[queue.name_with_underscores()] = {
    120                     "state": self.state_from_queue_status(status),
    12188                    "status": status,
    12289                }
  • trunk/Tools/QueueStatusServer/templates/statusbubble.html

    r148216 r174087  
    2020    -webkit-border-radius: 5px;
    2121    border-radius: 5px;
    22     border: 1px solid #AAA;
     22    border: 1px solid rgba(1, 1, 1, 0.3);
    2323    background-color: white;
    2424    font-size: 11px;
     
    2828}
    2929.status:hover {
    30     border-color: #666;
    31 }
    32 .none {
    33     cursor: auto;
    34 }
    35 .none:hover {
    36     border-color: #AAA;
     30    border-color: rgba(1, 1, 1, 0.7);
    3731}
    3832.pass {
    3933    background-color: #8FDF5F;
    40     border: 1px solid #4F8530;
    4134}
    4235.fail {
    4336    background-color: #E98080;
    44     border: 1px solid #A77272;
    4537}
    46 .pending {
    47     background-color: #FFFC6C;
    48     border: 1px solid #C5C56D;
     38.started {
     39    background-color: #D8FFFA;
     40}
     41.provisional-fail {
     42    background-color: #FFAF05;
    4943}
    5044.error {
    5145  background-color: #E0B0FF;
    52   border: 1px solid #ACA0B3;
    5346}
    5447.queue_position {
     
    7366  {% for bubble in bubbles %}
    7467  <a class="status {{ bubble.state }}" target="_top"
    75   {% if bubble.status %}
    7668      href="/patch/{{ bubble.attachment_id }}"
    77       title="{{ bubble.status.date|timesince }} ago"
     69  {% if bubble.details_message %}
     70      title="{{ bubble.details_message }}"
    7871  {% endif %}
    7972  >
     
    9285  </form>
    9386{% endif %}
     87
     88<script>
     89// Convert from UTC dates to local.
     90var bubbles = document.getElementsByClassName("status")
     91for (var i = 0; i < bubbles.length; ++i) {
     92    var bubble = bubbles[i];
     93    if (bubble.hasAttribute("title")) {
     94        var newTitle = bubble.getAttribute("title").replace(/\[\[(.+)\]\]/, function(match, isoDateString) {
     95            return new Date(isoDateString).toString();
     96        });
     97        bubble.setAttribute("title", newTitle);
     98    }
     99}
     100</script>
    94101</div>
    95102</body>
Note: See TracChangeset for help on using the changeset viewer.