Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(29)

Unified Diff: appengine/swarming/elements/res/imp/botpage/bot-page-summary.html

Issue 2381853003: Add bot-page summary with utilization stats (Closed) Base URL: git@github.com:luci/luci-py@page-everywhere
Patch Set: address spaces Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « appengine/swarming/elements/res/imp/botpage/bot-page-demo.html ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: appengine/swarming/elements/res/imp/botpage/bot-page-summary.html
diff --git a/appengine/swarming/elements/res/imp/botpage/bot-page-summary.html b/appengine/swarming/elements/res/imp/botpage/bot-page-summary.html
new file mode 100644
index 0000000000000000000000000000000000000000..e3de264429f27f738f6e7fa557c5a67e0720a2c6
--- /dev/null
+++ b/appengine/swarming/elements/res/imp/botpage/bot-page-summary.html
@@ -0,0 +1,378 @@
+<!--
+ This in an HTML Import-able file that contains the definition
+ of the following elements:
+
+ <bot-page-summary>
+
+ Usage:
+
+ <bot-page-summary></bot-page-summary>
+
+ Properties:
+ None.
+
+ Methods:
+ None.
+
+ Events:
+ None.
+-->
+<link rel="import" href="/res/imp/bower_components/paper-checkbox/paper-checkbox.html">
+
+<link rel="import" href="/res/imp/common/single-page-style.html">
+<link rel="import" href="/res/imp/common/sort-toggle.html">
+<link rel="import" href="/res/imp/common/url-param.html">
+
+<link rel="import" href="bot-page-shared-behavior.html">
+
+<dom-module id="bot-page-summary">
+ <template>
+ <style include="single-page-style">
+ .wrapper {
+ display: table;
+ margin-left: auto;
+ margin-bottom: 10px;
+ margin-right: 5px;
+ }
+
+ paper-checkbox {
+ margin-left: 5px;
+ }
+
+ .thick {
+ border-top-style: solid;
+ }
+ </style>
+
+ <url-param name="show_full_names"
+ value="{{_show_full_names}}">
+ </url-param>
+ <url-param name="show_all_tasks"
+ value="{{_show_all_tasks}}">
+ </url-param>
+ <url-param name="sort_stats"
+ value="{{_sortstr}}"
+ default_value="total:desc">
+ </url-param>
+
+ <div class="wrapper">
+ <table>
+ <thead on-sort_change="_sortChange">
+ <tr>
+ <th>
+ <span>Name</span>
+ <sort-toggle
+ name="full_name"
+ current="[[_sort]]">
+ </sort-toggle>
+ </th>
+ <th>
+ <span>Total</span>
+ <sort-toggle
+ name="total"
+ current="[[_sort]]">
+ </sort-toggle>
+ </th>
+ <th>
+ <span>Success</span>
+ <sort-toggle
+ name="success"
+ current="[[_sort]]">
+ </sort-toggle>
+ </th>
+ <th>
+ <span>Failed</span>
+ <sort-toggle
+ name="failed"
+ current="[[_sort]]">
+ </sort-toggle>
+ </th>
+ <th>
+ <span>Died</span>
+ <sort-toggle
+ name="bot_died"
+ current="[[_sort]]">
+ </sort-toggle>
+ </th>
+ <th>
+ <span>Average Duration</span>
+ <sort-toggle
+ name="avg_duration"
+ current="[[_sort]]">
+ </sort-toggle>
+ </th>
+ <th>
+ <span>Total Duration</span>
+ <sort-toggle
+ name="total_time"
+ current="[[_sort]]">
+ </sort-toggle>
+ </th>
+ <th>Percent of Total</th>
+ </tr>
+ </thead>
+ <tbody>
+ <template is="dom-repeat" items="[[_tasksToShow]]" as="task">
+ <tr>
+ <td hidden$="[[_truthy(_show_full_names)]]" title="[[task.full_name]]">[[task.name]]</td>
+ <td hidden$="[[_not(_show_full_names)]]" title="[[task.full_name]]">[[task.full_name]]</td>
+ <td>[[task.total]]</td>
+ <td>[[task.success]]</td>
+ <td>[[task.failed]]</td>
+ <td>[[task.bot_died]]</td>
+ <td>[[_humanDuration(task.avg_duration)]]</td>
+ <td>[[_humanDuration(task.total_time)]]</td>
+ <td>[[task.total_time_percent]]%</td>
+ </tr>
+ </template>
+ </tbody>
+ <tr class="thick">
+ <td>Total</td>
+ <td>[[_totalStats.total]]</td>
+ <td>[[_totalStats.success]]</td>
+ <td>[[_totalStats.failed]]</td>
+ <td>[[_totalStats.bot_died]]</td>
+ <td>[[_humanDuration(_totalStats.avg_duration)]]</td>
+ <td>[[_humanDuration(_totalStats.total_time)]]</td>
+ <td>100.0%</td>
+ </tr>
+ </table>
+
+ <div>
+ <table>
+ <thead>
+ <tr>
+ <th title="How much time passed between the oldest task fetched and now.">
+ Total Wall Time
+ </th>
+ <th title="How much of the wall time this bot was busy with a task.">
+ Wall Time Utilization
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>[[_humanDuration(_totalStats.wall_time)]]</td>
+ <td>[[_totalStats.wall_time_utilization]]%</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <paper-checkbox
+ checked="{{_show_full_names}}">
+ Show Full Names
+ </paper-checkbox>
+ <paper-checkbox
+ hidden$="[[_cannotExpand]]"
+ checked="{{_show_all_tasks}}">
+ Show All Tasks
+ </paper-checkbox>
+ </div>
+ </div>
+
+ </template>
+ <script>
+ (function(){
+ var SHOW_LIMIT = 15;
+ Polymer({
+ is: 'bot-page-summary',
+
+ behaviors: [
+ SwarmingBehaviors.BotPageBehavior,
+ ],
+
+ properties: {
+ // input
+ tasks: {
+ type: Array,
+ },
+
+ _cannotExpand: {
+ type: Boolean,
+ computed: "_countTasks(_taskStats.*)"
+ },
+ _show_all_tasks: {
+ type: Boolean
+ },
+ _show_full_names: {
+ type: Boolean
+ },
+ _sortstr: {
+ type: String
+ },
+ _sort: {
+ type: Object,
+ computed: "_makeSortObject(_sortstr)",
+ },
+ // _taskStats in an Array<Object> where each object represents one
+ // type of task (e.g. test_windows_release) and aggregate stats
+ // about it (e.g. average duration)
+ _taskStats: {
+ type: Array
+ },
+ // _tasksToShow is a sorted subset of _taskStats. This allows us to
+ // hide some of the tasks (if there are many)
+ _tasksToShow: {
+ type: Array,
+ computed: "_sortAndLimitTasks(_taskStats.*,_sort.*,_show_all_tasks)"
+ },
+ // _totalStats contains the aggregate stats for all tasks.
+ _totalStats: {
+ type: Object
+ }
+ },
+
+ // We define this down here to listen to all array events (e.g. splices)
+ // otherwise we don't update when more tasks are added.
+ observers: ["_aggregate(tasks.*)"],
+
+ _aggregate: function() {
+ if (!this.tasks || !this.tasks.length) {
+ return;
+ }
+ // TODO(kjlubick): Fix sk.now() to be less awkward to use.
+ var now = new Date(sk.now() * 1000);
+ var taskNames = [];
+ var taskAgg = {};
+ var totalStats = {
+ total: this.tasks.length,
+ success: 0,
+ failed: 0,
+ bot_died: 0,
+ avg_duration: 0,
+ total_time: 0,
+ // to compute wall_time, we find the latest task (and assume tasks
+ // come to us chronologically) and find the difference between then
+ // and now.
+ wall_time: (now - this.tasks[this.tasks.length - 1].started_ts) / 1000,
+ };
+ this.tasks.forEach(function(t) {
+ var n = t.name.trim();
+ var pieces = n.split('/');
+ if (pieces.length === 5) {
+ // this appears to be a buildbot name
+ // piece 0 is tag "name", piece 3 is "buildername"
+ // We throw the rest away (OS, commit hash, build number) so we
+ // can identify the "true name".
+ n = pieces[0] + "/" + pieces[3];
+ }
+
+ if (!taskAgg[n]) {
+ taskAgg[n] = {
+ full_name: n,
+ name: n,
+ total: 0,
+ success: 0,
+ failed: 0,
+ bot_died: 0,
+ avg_duration: 0,
+ total_time: 0,
+ }
+ }
+
+ taskAgg[n].total++;
+ if (t.failure) {
+ totalStats.failed++;
+ taskAgg[n].failed++;
+ } else if (t.internal_failure) {
+ totalStats.bot_died++;
+ taskAgg[n].bot_died++;
+ } else {
+ totalStats.success++;
+ taskAgg[n].success++;
+ }
+ totalStats.total_time += t.duration;
+ taskAgg[n].total_time += t.duration;
+ });
+
+ totalStats.avg_duration = totalStats.total_time / totalStats.total;
+ totalStats.wall_time_utilization = (totalStats.total_time * 100 / totalStats.wall_time).toFixed(1);
+ this.set("_totalStats", totalStats);
+
+ // Turn the map into the array and compute total time percent.
+ var names = Object.keys(taskAgg);
+ var taskStats = [];
+ names.forEach(function(n) {
+ taskAgg[n].avg_duration = taskAgg[n].total_time / taskAgg[n].total;
+ taskAgg[n].total_time_percent = (taskAgg[n].total_time * 100 /totalStats.total_time).toFixed(1);
+ taskStats.push(taskAgg[n]);
+ });
+
+ // Shorten names if possible by finding the longest common substring
+ // of at least n-1 of the tasks. These parameters can be tweaked; see
+ // https://www.npmjs.com/package/common-substrings for documentation
+ // n-1 seems to be good to avoid not finding decent matches if there
+ // is an oddball task.
+ var substrings = new Substrings({
+ minOccurrence: Math.max(2, names.length-1),
+ minLength: 6,
+ });
+ substrings.build(names);
+ var result = substrings.weighByAverage() || [];
+ // result is an Array<{name:String, source:Array<int>} where the
+ // ints in source are the indices of names that have the substring.
+ // result is sorted with the "best" results first.
+ if (result.length) {
+ result[0].source.forEach(function(idx){
+ var name = taskStats[idx].full_name;
+ taskStats[idx].name = name.replace(result[0].name, "...");
+ });
+ }
+
+ this.set("_taskStats", taskStats);
+ },
+
+ _compare: function(a,b) {
+ if (!this._sort) {
+ return 0;
+ }
+ var dir = 1;
+ if (this._sort.direction === "desc") {
+ dir = -1;
+ }
+ return dir * swarming.naturalCompare(a[this._sort.name], b[this._sort.name]);
+ },
+
+ _countTasks: function(){
+ return this._taskStats.length <= SHOW_LIMIT;
+ },
+
+ _makeSortObject: function(sortstr){
+ if (!sortstr) {
+ return undefined;
+ }
+ var pieces = sortstr.split(":");
+ if (pieces.length != 2) {
+ // fail safe
+ return {name: "full_name", direction: "asc"};
+ }
+ return {
+ name: pieces[0],
+ direction: pieces[1],
+ }
+ },
+
+ _sortAndLimitTasks: function() {
+ swarming.stableSort(this._taskStats, this._compare.bind(this));
+ var limit = this._taskStats.length;
+ if (!this._show_all_tasks && this._taskStats.length > SHOW_LIMIT) {
+ limit = SHOW_LIMIT;
+ }
+ return this._taskStats.slice(0, limit);
+ },
+
+ _sortChange: function(e) {
+ // The event we get from sort-toggle tells us the name of what needs
+ // to be sorting and how to sort it.
+ if (!(e && e.detail && e.detail.name)) {
+ return;
+ }
+ e.preventDefault();
+ e.stopPropagation();
+ this.set("_sortstr", e.detail.name + ":" + e.detail.direction);
+ },
+
+ });
+ })();
+ </script>
+</dom-module>
« no previous file with comments | « appengine/swarming/elements/res/imp/botpage/bot-page-demo.html ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698