Changeset 174087 in webkit
- Timestamp:
- Sep 29, 2014 2:55:04 PM (10 years ago)
- Location:
- trunk/Tools
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Tools/ChangeLog
r174086 r174087 1 2014-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 1 22 2014-09-29 Commit Queue <commit-queue@webkit.org> 2 23 -
trunk/Tools/QueueStatusServer/app.yaml
r174015 r174087 1 1 application: webkit-queues 2 version: 1740 15# Bugzilla bug ID of last major change2 version: 174087 # Bugzilla bug ID of last major change 3 3 runtime: python 4 4 api_version: 1 -
trunk/Tools/QueueStatusServer/handlers/statusbubble.py
r97523 r174087 33 33 34 34 from model.attachment import Attachment 35 from model.activeworkitems import ActiveWorkItems 36 from model.patchlog import PatchLog 37 from model.queues import Queue 38 from model.queuestatus import QueueStatus 35 39 from model.workitems import WorkItems 36 from model.queues import Queue40 from sets import Set 37 41 42 progress_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 ]) 38 56 39 57 class 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 40 110 def _build_bubble(self, queue, attachment, queue_position): 41 queue_status = attachment.status_for_queue(queue)42 111 bubble = { 43 112 "name": queue.short_name().lower(), 44 113 "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,48 114 } 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 49 160 return bubble 50 161 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. 53 164 if attachment.position_in_queue(queue): 54 165 return True 55 # Complete ewses are also shown.166 # EWS queues are also shown when complete. 56 167 return bool(queue.is_ews() and attachment.status_for_queue(queue)) 57 168 … … 60 171 bubbles = [] 61 172 for queue in Queue.all(): 62 if not self._ have_status_for(attachment, queue):173 if not self._should_show_bubble_for(attachment, queue): 63 174 continue 64 175 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. 70 180 if queue.is_ews(): 71 181 show_submit_to_ews = False -
trunk/Tools/QueueStatusServer/model/attachment.py
r156803 r174087 35 35 36 36 class Attachment(object): 37 @classmethod38 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_id45 if not attachment_id:46 continue47 if attachment_id in visited_ids:48 continue49 visited_ids.add(attachment_id)50 ids.append(attachment_id)51 if len(visited_ids) >= limit:52 break53 return map(cls, ids)54 55 37 def __init__(self, attachment_id): 56 38 self.id = attachment_id … … 63 45 self._summary = self._fetch_summary() 64 46 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 state74 if status.message.startswith("Error:"):75 return "error"76 if status:77 return "pending"78 return None79 47 80 48 def position_in_queue(self, queue): … … 118 86 # summary() is a horrible API and should be killed. 119 87 summary[queue.name_with_underscores()] = { 120 "state": self.state_from_queue_status(status),121 88 "status": status, 122 89 } -
trunk/Tools/QueueStatusServer/templates/statusbubble.html
r148216 r174087 20 20 -webkit-border-radius: 5px; 21 21 border-radius: 5px; 22 border: 1px solid #AAA;22 border: 1px solid rgba(1, 1, 1, 0.3); 23 23 background-color: white; 24 24 font-size: 11px; … … 28 28 } 29 29 .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); 37 31 } 38 32 .pass { 39 33 background-color: #8FDF5F; 40 border: 1px solid #4F8530;41 34 } 42 35 .fail { 43 36 background-color: #E98080; 44 border: 1px solid #A77272;45 37 } 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; 49 43 } 50 44 .error { 51 45 background-color: #E0B0FF; 52 border: 1px solid #ACA0B3;53 46 } 54 47 .queue_position { … … 73 66 {% for bubble in bubbles %} 74 67 <a class="status {{ bubble.state }}" target="_top" 75 {% if bubble.status %}76 68 href="/patch/{{ bubble.attachment_id }}" 77 title="{{ bubble.status.date|timesince }} ago" 69 {% if bubble.details_message %} 70 title="{{ bubble.details_message }}" 78 71 {% endif %} 79 72 > … … 92 85 </form> 93 86 {% endif %} 87 88 <script> 89 // Convert from UTC dates to local. 90 var bubbles = document.getElementsByClassName("status") 91 for (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> 94 101 </div> 95 102 </body>
Note: See TracChangeset
for help on using the changeset viewer.