Milimetric has submitted this change and it was merged. ( 
https://gerrit.wikimedia.org/r/348227 )

Change subject: Improve annotations
......................................................................


Improve annotations

This patch:
- Adds parsing and display of annotations to tabs layout
- Gives annotations a new (hopefully not ugly) look
- Gives annot. distinct letters (A, B, C... instead of A, A, A...)
- Fixes bug: Annotations being snapped to same date would not show

Bug: T162482
Change-Id: I5d0c9cab72aa713c7f0e6cd7b1f1d832c7aca5b8
---
M src/app/apis/aqs-api.js
M src/components/layouts/tabs/tabs.js
M src/components/visualizers/dygraphs-timeseries/bindings.js
M src/components/visualizers/dygraphs-timeseries/dygraphs-timeseries.html
M src/components/visualizers/visualizer/visualizer.js
5 files changed, 112 insertions(+), 27 deletions(-)

Approvals:
  Milimetric: Verified; Looks good to me, approved



diff --git a/src/app/apis/aqs-api.js b/src/app/apis/aqs-api.js
index 8f339d2..280dbf9 100644
--- a/src/app/apis/aqs-api.js
+++ b/src/app/apis/aqs-api.js
@@ -92,7 +92,7 @@
 
                 // Datasets are disjoint but we need to add them so that
                 // the timeseries object makes sense.
-                var ts = TimeseriesData.mergeAll(timeseries)
+                var ts = TimeseriesData.mergeAll(timeseries);
                 deferred.resolve(ts);
 
             }, function (reason) {
diff --git a/src/components/layouts/tabs/tabs.js 
b/src/components/layouts/tabs/tabs.js
index 6866983..3f0edc8 100644
--- a/src/components/layouts/tabs/tabs.js
+++ b/src/components/layouts/tabs/tabs.js
@@ -7,7 +7,8 @@
     var ko = require('knockout'),
         _ = require('lodash'),
         templateMarkup = require('text!./tabs.html'),
-        configApi = require('apis.config');
+        configApi = require('apis.config'),
+        annotationsApi = require('apis.annotations');
 
     require('twix');
 
@@ -64,6 +65,15 @@
                         g.type === 'stacked-bars' ? 'bar' :
                         g.type === 'dygraphs-timeseries' ? 'line' :
                         g.type === 'table-timeseries' ? 'table' : '';
+
+                    // Fetch annotations if present
+                    var annotationsInfo = g.annotations;
+                    g.annotations = ko.observable();
+                    if (annotationsInfo) {
+                        annotationsApi.getTimeseriesData(
+                            {annotations: annotationsInfo}
+                        ).then(g.annotations);
+                    }
                 });
 
             }, this);
diff --git a/src/components/visualizers/dygraphs-timeseries/bindings.js 
b/src/components/visualizers/dygraphs-timeseries/bindings.js
index 78d5692..38322a4 100644
--- a/src/components/visualizers/dygraphs-timeseries/bindings.js
+++ b/src/components/visualizers/dygraphs-timeseries/bindings.js
@@ -2,7 +2,8 @@
 define(function (require) {
 
     var ko = require('knockout'),
-        moment = require('moment');
+        moment = require('moment'),
+        _ = require("lodash");
 
     require('dygraphs');
     require('./dygraphs.patch');
@@ -73,44 +74,77 @@
 
                 if (annotations) {
                     dygraphChart.ready(function () {
-                        var i = 0,
-                            $roller = $(dygraphChart.roller_),
-                            $rollerHolder = $('<span 
class="dygraph-roller">Averaging: </span>');
+                        var $roller = $(dygraphChart.roller_),
+                            $rollerHolder = $('<span 
class="dygraph-roller">Averaging: </span>'),
+                            charCode = 65,
+                            snapIndex = -1;
 
-                        
dygraphChart.setAnnotations(annotations.rowData().map(function (a) {
-                            // find the closest date in the data that fits
-                            var closestDate = null;
-                            var lastDistance = Math.pow(2, 53) - 1;
-                            for (; i < rows.length; i++) {
-                                var date = rows[i][0];
-                                var thisDistance = Math.min(lastDistance, 
Math.abs(date.getTime() - a[0]));
-                                // both arrays are sorted so the distance will 
only get further now
-                                if (thisDistance === lastDistance) {
-                                    i--;
-                                    break;
-                                }
-                                lastDistance = thisDistance;
-                                closestDate = rows[i][0];
-                                // if we're at the end, and no date matched, 
prefix annotation with date
-                                //   Also, handle other future dates the same 
by decrementing i
-                                if (i === rows.length - 1) {
-                                    a[1] = 
moment(a[0]).utc().format('YYYY-MM-DD ') + a[1];
-                                    i--;
+                        // Snap annotation dates to the closest data point
+                        var snappedAnnotations = 
annotations.rowData().map(function (a) {
+
+                            // Efficiently iterate data points to find closest 
one
+                            for (var i = snapIndex + 1; i < rows.length; i++) {
+                                if (a[0] >= rows[i][0].getTime()) {
+                                    snapIndex = i;
+                                } else {
                                     break;
                                 }
                             }
+
+                            // Format annotation
+                            var snappedDate, message;
+                            if (snapIndex === -1) {
+                                // The annotation is set before the first data 
point.
+                                // Attach it to the first data point, prefixed 
with its date.
+                                snappedDate = rows[0][0].getTime();
+                                message = 
moment(a[0]).utc().format('YYYY-MM-DD ') + a[1];
+                            } else if (snapIndex === rows.length - 1) {
+                                // The annotation is set after the last data 
point.
+                                // Attach it to the last data point, prefixed 
with its date.
+                                snappedDate = rows[rows.length - 
1][0].getTime();
+                                message = 
moment(a[0]).utc().format('YYYY-MM-DD ') + a[1];
+                            } else {
+                                // The annotation is set in between two data 
points.
+                                // Attach it to the earlier data point, no 
prefix.
+                                snappedDate = rows[snapIndex][0].getTime();
+                                message = a[1];
+                            }
+
+                            return [snappedDate, message];
+                        });
+
+                        // Merge annotations that got snapped to the same date
+                        var mergedAnnotations = _
+                            .chain(snappedAnnotations)
+                            .groupBy(function (a) { return a[0]; })
+                            .toPairs()
+                            .sortBy(function (g) { return g[0]; })
+                            .map(function (g) {
+                                return [
+                                    g[0],
+                                    g[1].map(function (a) {
+                                        return a[1];
+                                    }).join("\n\n")
+                                ];
+                            })
+                            .value();
+
+                        // Format annotations for dygraphs to parse
+                        
dygraphChart.setAnnotations(mergedAnnotations.map(function (a) {
                             return {
                                 // just attach to the first series and show on 
X axis
                                 series: options.labels[1],
                                 attachAtBottom: true,
-                                shortText: 'A',
+                                tickHeight: 0,
+                                shortText: String.fromCharCode(charCode++),
                                 // annoying thing to learn through 
experimentation:
                                 //   Dygraphs requires Date instances for the 
data, but
                                 //   Dygraphs requires milliseconds since 
epoch for the annotations
-                                x: closestDate.getTime(),
+                                x: a[0],
                                 text: a[1],
                             };
                         }));
+
                         // move the roller to the top and add a label
                         $rollerHolder.append($roller.detach()).append(' 
day(s)');
                         // remove the previous roller if any
diff --git 
a/src/components/visualizers/dygraphs-timeseries/dygraphs-timeseries.html 
b/src/components/visualizers/dygraphs-timeseries/dygraphs-timeseries.html
index 0252c03..f445727 100644
--- a/src/components/visualizers/dygraphs-timeseries/dygraphs-timeseries.html
+++ b/src/components/visualizers/dygraphs-timeseries/dygraphs-timeseries.html
@@ -8,6 +8,8 @@
         margin-left: -120px;
         pointer-events: none;
         width: auto!important;
+        z-index: 15!important;
+        top: 1.5em!important;
     }
 
     div.dygraph-legend > span {
@@ -30,6 +32,44 @@
         min-width: 300px;
         background-color: white;
     }
+    /* Patches appearance of annotations */
+    .dygraphDefaultAnnotation {
+        top: 0!important;
+        height: calc(100% - 1.6em)!important;
+        border: none!important;
+        border-left: 0.05em dashed!important;
+        background-color: transparent!important;
+        margin-left: 0.53em!important;
+        padding-left: 0.15em!important;
+        overflow: visible!important;
+        user-select: none;
+        width: 18px!important;
+        text-align: left;
+    }
+    .graph:hover .dygraphDefaultAnnotation {
+        opacity: 0.5;
+    }
+    .graph:hover .dygraphDefaultAnnotation:hover {
+        opacity: 1;
+        font-weight: bold;
+    }
+    .dygraphDefaultAnnotation:hover::before {
+        content: " ";
+        position: absolute;
+        padding: 1px;
+        top: 0px;
+        left: -1px;
+        border: 1px solid;
+        width: 18px;
+        height: 18px;
+    }
+    /* Presence of annotations messes with the averaging days control */
+    .dygraph-roller input {
+        position: relative!important;
+        left: auto!important;
+        top: auto!important;
+        display: inline!important;
+    }
 </style>
 
 <div class="columnar flexer flexed" data-bind="
diff --git a/src/components/visualizers/visualizer/visualizer.js 
b/src/components/visualizers/visualizer/visualizer.js
index 19002a9..e28204b 100644
--- a/src/components/visualizers/visualizer/visualizer.js
+++ b/src/components/visualizers/visualizer/visualizer.js
@@ -141,6 +141,7 @@
             format: numberUtils.numberFormatter(graph.format),
             height: 500,
             id: _.kebabCase([graph.metric, graph.submetric].join('-')),
+            annotations: graph.annotations,
         };
         this.params.downloadLink = graph.downloadLink;
         this.legendHeight = this.params.height - 80 + 'px';

-- 
To view, visit https://gerrit.wikimedia.org/r/348227
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I5d0c9cab72aa713c7f0e6cd7b1f1d832c7aca5b8
Gerrit-PatchSet: 4
Gerrit-Project: analytics/dashiki
Gerrit-Branch: master
Gerrit-Owner: Mforns <[email protected]>
Gerrit-Reviewer: Mforns <[email protected]>
Gerrit-Reviewer: Milimetric <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to