Bearloga has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/237298

Change subject: Adds smoothing to dashboard + Smoothing options: moving 
average, weekly median, monthly median. + Smoothing can be specified globally 
or on a per-plot basis.
......................................................................

Adds smoothing to dashboard
+ Smoothing options: moving average, weekly median, monthly median.
+ Smoothing can be specified globally or on a per-plot basis.

Additional changes:
+ make_dygraph now outputs a dygraph that is renderDygraph'd in server.R
+ Updated the content in the Markdown docs to include correct contact/bug 
report info.

Bug: T107202
Change-Id: I5b749ff426019bb218fddf1b1bfeb3d369273572
---
M assets/content/app_events.md
M assets/content/app_load.md
M assets/content/build_a_plot.md
M assets/content/desktop_events.md
M assets/content/desktop_load.md
M assets/content/failure_breakdown.md
M assets/content/failure_rate.md
M assets/content/failure_suggests.md
M assets/content/fulltext_basic.md
M assets/content/geo_basic.md
M assets/content/kpi_api_usage.md
M assets/content/kpi_load_time.md
M assets/content/kpi_zero_results.md
M assets/content/kpis_summary.md
M assets/content/language_basic.md
M assets/content/mobile_events.md
M assets/content/mobile_load.md
M assets/content/open_basic.md
M assets/content/prefix_basic.md
M server.R
M ui.R
M utils.R
22 files changed, 285 insertions(+), 240 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/wikimedia/discovery/rainbow 
refs/changes/98/237298/1

diff --git a/assets/content/app_events.md b/assets/content/app_events.md
index 89cfc6b..ab62d52 100644
--- a/assets/content/app_events.md
+++ b/assets/content/app_events.md
@@ -21,7 +21,7 @@
 
 Questions, bug reports, and feature suggestions
 ------
-For technical, non-bug questions, [email 
Oliver](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong, [create an issue on 
GitHub](https://github.com/Ironholds/rainbow/issues). If you have a suggestion, 
[open a ticket in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/) in the 
Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
+For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong or have a suggestion, [open a ticket 
in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/?projects=Discovery)
 in the Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
 
 <hr style="border-color: gray;">
 <p style="font-size: small; color: gray;">
diff --git a/assets/content/app_load.md b/assets/content/app_load.md
index edf0958..6f624fe 100644
--- a/assets/content/app_load.md
+++ b/assets/content/app_load.md
@@ -20,7 +20,7 @@
 
 Questions, bug reports, and feature suggestions
 ------
-For technical, non-bug questions, [email 
Oliver](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong, [create an issue on 
GitHub](https://github.com/Ironholds/rainbow/issues). If you have a suggestion, 
[open a ticket in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/) in the 
Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
+For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong or have a suggestion, [open a ticket 
in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/?projects=Discovery)
 in the Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
 
 <hr style="border-color: gray;">
 <p style="font-size: small; color: gray;">
diff --git a/assets/content/build_a_plot.md b/assets/content/build_a_plot.md
index 546aa98..3477cb7 100644
--- a/assets/content/build_a_plot.md
+++ b/assets/content/build_a_plot.md
@@ -10,7 +10,7 @@
 
 Questions, bug reports, and feature suggestions
 ------
-For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong, [create an issue on 
GitHub](https://github.com/Ironholds/rainbow/issues). If you have a suggestion, 
[open a ticket in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/) in the 
Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
+For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong or have a suggestion, [open a ticket 
in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/?projects=Discovery)
 in the Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
 
 <hr style="border-color: gray;">
 <p style="font-size: small; color: gray;">
diff --git a/assets/content/desktop_events.md b/assets/content/desktop_events.md
index a44ae8d..c4b5695 100644
--- a/assets/content/desktop_events.md
+++ b/assets/content/desktop_events.md
@@ -25,7 +25,7 @@
 
 Questions, bug reports, and feature suggestions
 ------
-For technical, non-bug questions, [email 
Oliver](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong, [create an issue on 
GitHub](https://github.com/Ironholds/rainbow/issues). If you have a suggestion, 
[open a ticket in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/) in the 
Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
+For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong or have a suggestion, [open a ticket 
in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/?projects=Discovery)
 in the Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
 
 <hr style="border-color: gray;">
 <p style="font-size: small; color: gray;">
diff --git a/assets/content/desktop_load.md b/assets/content/desktop_load.md
index 33d7aa1..eecb56c 100644
--- a/assets/content/desktop_load.md
+++ b/assets/content/desktop_load.md
@@ -18,7 +18,7 @@
 
 Questions, bug reports, and feature suggestions
 ------
-For technical, non-bug questions, [email 
Oliver](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong, [create an issue on 
GitHub](https://github.com/Ironholds/rainbow/issues). If you have a suggestion, 
[open a ticket in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/) in the 
Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
+For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong or have a suggestion, [open a ticket 
in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/?projects=Discovery)
 in the Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
 
 <hr style="border-color: gray;">
 <p style="font-size: small; color: gray;">
diff --git a/assets/content/failure_breakdown.md 
b/assets/content/failure_breakdown.md
index f525d0d..484b2d2 100644
--- a/assets/content/failure_breakdown.md
+++ b/assets/content/failure_breakdown.md
@@ -15,7 +15,7 @@
 
 Questions, bug reports, and feature suggestions
 ------
-For technical, non-bug questions, [email 
Oliver](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong, [create an issue on 
GitHub](https://github.com/Ironholds/rainbow/issues). If you have a suggestion, 
[open a ticket in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/) in the 
Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
+For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong or have a suggestion, [open a ticket 
in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/?projects=Discovery)
 in the Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
 
 <hr style="border-color: gray;">
 <p style="font-size: small; color: gray;">
diff --git a/assets/content/failure_rate.md b/assets/content/failure_rate.md
index c8d9037..7c77ac6 100644
--- a/assets/content/failure_rate.md
+++ b/assets/content/failure_rate.md
@@ -16,7 +16,7 @@
 
 Questions, bug reports, and feature suggestions
 ------
-For technical, non-bug questions, [email 
Oliver](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong, [create an issue on 
GitHub](https://github.com/Ironholds/rainbow/issues). If you have a suggestion, 
[open a ticket in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/) in the 
Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
+For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong or have a suggestion, [open a ticket 
in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/?projects=Discovery)
 in the Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
 
 <hr style="border-color: gray;">
 <p style="font-size: small; color: gray;">
diff --git a/assets/content/failure_suggests.md 
b/assets/content/failure_suggests.md
index dbf35eb..408f334 100644
--- a/assets/content/failure_suggests.md
+++ b/assets/content/failure_suggests.md
@@ -14,7 +14,7 @@
 
 Questions, bug reports, and feature suggestions
 ------
-For technical, non-bug questions, [email 
Oliver](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong, [create an issue on 
GitHub](https://github.com/Ironholds/rainbow/issues). If you have a suggestion, 
[open a ticket in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/) in the 
Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
+For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong or have a suggestion, [open a ticket 
in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/?projects=Discovery)
 in the Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
 
 <hr style="border-color: gray;">
 <p style="font-size: small; color: gray;">
diff --git a/assets/content/fulltext_basic.md b/assets/content/fulltext_basic.md
index d83ce02..9012e14 100644
--- a/assets/content/fulltext_basic.md
+++ b/assets/content/fulltext_basic.md
@@ -16,7 +16,7 @@
 
 Questions, bug reports, and feature suggestions
 ------
-For technical, non-bug questions, [email 
Oliver](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong, [create an issue on 
GitHub](https://github.com/Ironholds/rainbow/issues). If you have a suggestion, 
[open a ticket in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/) in the 
Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
+For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong or have a suggestion, [open a ticket 
in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/?projects=Discovery)
 in the Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
 
 <hr style="border-color: gray;">
 <p style="font-size: small; color: gray;">
diff --git a/assets/content/geo_basic.md b/assets/content/geo_basic.md
index f88dddd..4a95483 100644
--- a/assets/content/geo_basic.md
+++ b/assets/content/geo_basic.md
@@ -19,7 +19,7 @@
 
 Questions, bug reports, and feature suggestions
 ------
-For technical, non-bug questions, [email 
Oliver](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong, [create an issue on 
GitHub](https://github.com/Ironholds/rainbow/issues). If you have a suggestion, 
[open a ticket in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/) in the 
Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
+For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong or have a suggestion, [open a ticket 
in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/?projects=Discovery)
 in the Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
 
 <hr style="border-color: gray;">
 <p style="font-size: small; color: gray;">
diff --git a/assets/content/kpi_api_usage.md b/assets/content/kpi_api_usage.md
index 79cf169..c34d26b 100644
--- a/assets/content/kpi_api_usage.md
+++ b/assets/content/kpi_api_usage.md
@@ -10,7 +10,7 @@
 
 Questions, bug reports, and feature suggestions
 ------
-For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong, [create an issue on 
GitHub](https://github.com/Ironholds/rainbow/issues). If you have a suggestion, 
[open a ticket in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/) in the 
Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
+For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong or have a suggestion, [open a ticket 
in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/?projects=Discovery)
 in the Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
 
 <hr style="border-color: gray;">
 <p style="font-size: small; color: gray;">
diff --git a/assets/content/kpi_load_time.md b/assets/content/kpi_load_time.md
index 30cebe2..f3553d8 100644
--- a/assets/content/kpi_load_time.md
+++ b/assets/content/kpi_load_time.md
@@ -10,7 +10,7 @@
 
 Questions, bug reports, and feature suggestions
 ------
-For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong, [create an issue on 
GitHub](https://github.com/Ironholds/rainbow/issues). If you have a suggestion, 
[open a ticket in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/) in the 
Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
+For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong or have a suggestion, [open a ticket 
in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/?projects=Discovery)
 in the Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
 
 <hr style="border-color: gray;">
 <p style="font-size: small; color: gray;">
diff --git a/assets/content/kpi_zero_results.md 
b/assets/content/kpi_zero_results.md
index 2a1a724..4c48125 100644
--- a/assets/content/kpi_zero_results.md
+++ b/assets/content/kpi_zero_results.md
@@ -10,7 +10,7 @@
 
 Questions, bug reports, and feature suggestions
 ------
-For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong, [create an issue on 
GitHub](https://github.com/Ironholds/rainbow/issues). If you have a suggestion, 
[open a ticket in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/) in the 
Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
+For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong or have a suggestion, [open a ticket 
in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/?projects=Discovery)
 in the Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
 
 <hr style="border-color: gray;">
 <p style="font-size: small; color: gray;">
diff --git a/assets/content/kpis_summary.md b/assets/content/kpis_summary.md
index 3d54f6d..40d2ab6 100644
--- a/assets/content/kpis_summary.md
+++ b/assets/content/kpis_summary.md
@@ -13,7 +13,7 @@
 
 Questions, bug reports, and feature suggestions
 ------
-For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong, [create an issue on 
GitHub](https://github.com/Ironholds/rainbow/issues). If you have a suggestion, 
[open a ticket in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/) in the 
Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
+For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong or have a suggestion, [open a ticket 
in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/?projects=Discovery)
 in the Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
 
 <hr style="border-color: gray;">
 <p style="font-size: small; color: gray;">
diff --git a/assets/content/language_basic.md b/assets/content/language_basic.md
index 583cb05..b5111e4 100644
--- a/assets/content/language_basic.md
+++ b/assets/content/language_basic.md
@@ -15,7 +15,7 @@
 
 Questions, bug reports, and feature suggestions
 ------
-For technical, non-bug questions, [email 
Oliver](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong, [create an issue on 
GitHub](https://github.com/Ironholds/rainbow/issues). If you have a suggestion, 
[open a ticket in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/) in the 
Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
+For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong or have a suggestion, [open a ticket 
in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/?projects=Discovery)
 in the Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
 
 <hr style="border-color: gray;">
 <p style="font-size: small; color: gray;">
diff --git a/assets/content/mobile_events.md b/assets/content/mobile_events.md
index 2ce2966..1841718 100644
--- a/assets/content/mobile_events.md
+++ b/assets/content/mobile_events.md
@@ -22,7 +22,7 @@
 
 Questions, bug reports, and feature suggestions
 ------
-For technical, non-bug questions, [email 
Oliver](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong, [create an issue on 
GitHub](https://github.com/Ironholds/rainbow/issues). If you have a suggestion, 
[open a ticket in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/) in the 
Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
+For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong or have a suggestion, [open a ticket 
in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/?projects=Discovery)
 in the Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
 
 <hr style="border-color: gray;">
 <p style="font-size: small; color: gray;">
diff --git a/assets/content/mobile_load.md b/assets/content/mobile_load.md
index 0145205..e3bca08 100644
--- a/assets/content/mobile_load.md
+++ b/assets/content/mobile_load.md
@@ -20,7 +20,7 @@
 
 Questions, bug reports, and feature suggestions
 ------
-For technical, non-bug questions, [email 
Oliver](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong, [create an issue on 
GitHub](https://github.com/Ironholds/rainbow/issues). If you have a suggestion, 
[open a ticket in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/) in the 
Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
+For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong or have a suggestion, [open a ticket 
in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/?projects=Discovery)
 in the Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
 
 <hr style="border-color: gray;">
 <p style="font-size: small; color: gray;">
diff --git a/assets/content/open_basic.md b/assets/content/open_basic.md
index 447f0e1..fb8d228 100644
--- a/assets/content/open_basic.md
+++ b/assets/content/open_basic.md
@@ -18,7 +18,7 @@
 
 Questions, bug reports, and feature suggestions
 ------
-For technical, non-bug questions, [email 
Oliver](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong, [create an issue on 
GitHub](https://github.com/Ironholds/rainbow/issues). If you have a suggestion, 
[open a ticket in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/) in the 
Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
+For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong or have a suggestion, [open a ticket 
in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/?projects=Discovery)
 in the Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
 
 <hr style="border-color: gray;">
 <p style="font-size: small; color: gray;">
diff --git a/assets/content/prefix_basic.md b/assets/content/prefix_basic.md
index ad2891a..ccd3276 100644
--- a/assets/content/prefix_basic.md
+++ b/assets/content/prefix_basic.md
@@ -15,7 +15,7 @@
 
 Questions, bug reports, and feature suggestions
 ------
-For technical, non-bug questions, [email 
Oliver](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong, [create an issue on 
GitHub](https://github.com/Ironholds/rainbow/issues). If you have a suggestion, 
[open a ticket in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/) in the 
Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
+For technical, non-bug questions, [email 
Mikhail](mailto:[email protected]?subject=Dashboard%20Question). If you 
experience a bug or notice something wrong or have a suggestion, [open a ticket 
in 
Phabricator](https://phabricator.wikimedia.org/maniphest/task/create/?projects=Discovery)
 in the Discovery board or [email 
Dan](mailto:[email protected]?subject=Dashboard%20Question).
 
 <hr style="border-color: gray;">
 <p style="font-size: small; color: gray;">
diff --git a/server.R b/server.R
index 29c14a8..932ea65 100644
--- a/server.R
+++ b/server.R
@@ -1,10 +1,10 @@
-#Version 0.2.0
+## Version 0.2.0
 source("utils.R")
 
 existing_date <- (Sys.Date()-1)
 
-#Read in desktop data and generate means for the value boxes, along with a 
time-series appropriate form for
-#dygraphs.
+## Read in desktop data and generate means for the value boxes, along with a 
time-series appropriate form for
+## dygraphs.
 read_desktop <- function(){
   data <- download_set("desktop_event_counts.tsv")
   interim <- reshape2::dcast(data, formula = timestamp ~ action, fun.aggregate 
= sum)
@@ -40,8 +40,8 @@
   android_dygraph_means <<- round(colMeans(android[,2:4]))
 
   app_load_data <- download_set("app_load_times.tsv")
-  ios_load_data <<- app_load_data[app_load_data$platform == "iOS",]
-  android_load_data <<- app_load_data[app_load_data$platform == "Android",]
+  ios_load_data <<- app_load_data[app_load_data$platform == "iOS", 
names(app_load_data) != "platform"]
+  android_load_data <<- app_load_data[app_load_data$platform == "Android", 
names(app_load_data) != "platform"]
 
   return(invisible())
 }
@@ -93,7 +93,7 @@
     existing_date <<- Sys.Date()
   }
 
-  #Desktop value boxes
+  ## Desktop value boxes
   output$desktop_event_searches <- renderValueBox(
     valueBox(
       value = desktop_dygraph_means["search sessions"],
@@ -121,17 +121,22 @@
     )
   )
 
-  #The dynamic graphs of events on desktop
-  output$desktop_event_plot <- make_dygraph(
-    desktop_dygraph_set, "Date", "Events",
-    "Desktop search events, by day"
-  )
-  output$desktop_load_plot <- make_dygraph(
-    desktop_load_data, "Date", "Load time (ms)",
-    "Desktop result load times, by day", use_si = FALSE
-  )
+  ## The dynamic graphs of events on desktop
+  output$desktop_event_plot <- renderDygraph({
+    smooth_level <- input$smoothing_desktop_event
+    make_dygraph(desktop_dygraph_set,
+                 "Date", "Events", "Desktop search events, by day",
+                 smoothing = ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level))
+  })
+  output$desktop_load_plot <- renderDygraph({
+    smooth_level <- input$smoothing_desktop_load
+    make_dygraph(desktop_load_data,
+                 "Date", "Load time (ms)", "Desktop result load times, by day",
+                 use_si = FALSE,
+                 smoothing = ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level))
+  })
 
-  #Mobile value boxes
+  ## Mobile value boxes
   output$mobile_event_searches <- renderValueBox(
     valueBox(
       value = mobile_dygraph_means["search sessions"],
@@ -159,17 +164,19 @@
     )
   )
 
-  #Mobile plots
-  output$mobile_event_plot <- make_dygraph(
-    mobile_dygraph_set, "Date", "Events",
-    "Mobile search events, by day"
-  )
-  output$mobile_load_plot <- make_dygraph(
-    mobile_load_data, "Date", "Load time (ms)",
-    "Mobile result load times, by day", use_si = FALSE
-  )
+  ## Mobile plots
+  output$mobile_event_plot <- renderDygraph({
+    smooth_level <- input$smoothing_mobile_event
+    make_dygraph(mobile_dygraph_set, "Date", "Events", "Mobile search events, 
by day",
+                 smoothing = ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level))
+  })
+  output$mobile_load_plot <- renderDygraph({
+    smooth_level <- input$smoothing_mobile_load
+    make_dygraph(mobile_load_data, "Date", "Load time (ms)", "Mobile result 
load times, by day",
+                 use_si = FALSE, smoothing = ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level))
+  })
 
-  #App value boxes
+  ## App value boxes
   output$app_event_searches <- renderValueBox(
     valueBox(
       value = android_dygraph_means[3],
@@ -197,66 +204,78 @@
     )
   )
 
-  #App plots
-  output$android_event_plot <- make_dygraph(
-    android_dygraph_set, "Date", "Events",
-    "Android mobile app search events, by day"
-  )
-  output$android_load_plot <- make_dygraph(
-    android_load_data, "Date", "Load time (ms)",
-    "Android result load times, by day", use_si = FALSE
-  )
-  output$ios_event_plot <- make_dygraph(
-    ios_dygraph_set, "Date", "Events",
-    "iOS mobile app search events, by day"
-  )
-  output$ios_load_plot <- make_dygraph(
-    ios_load_data, "Date", "Load time (ms)",
-    "iOS result load times, by day", use_si = FALSE
-  )
+  ## App plots
+  output$android_event_plot <- renderDygraph({
+    smooth_level <- input$smoothing_app_event
+    make_dygraph(android_dygraph_set, "Date", "Events", "Android mobile app 
search events, by day",
+                 smoothing = ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level))
+  })
+  output$android_load_plot <- renderDygraph({
+    smooth_level <- input$smoothing_app_load
+    make_dygraph(android_load_data, "Date", "Load time (ms)","Android result 
load times, by day", use_si = FALSE,
+                 smoothing = ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level))
+  })
+  output$ios_event_plot <- renderDygraph({
+    smooth_level <- input$smoothing_app_event
+    make_dygraph(ios_dygraph_set, "Date", "Events","iOS mobile app search 
events, by day",
+                 smoothing = ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level))
+  })
+  output$ios_load_plot <- renderDygraph({
+    smooth_level <- input$smoothing_app_load
+    make_dygraph(ios_load_data, "Date", "Load time (ms)","iOS result load 
times, by day", use_si = FALSE,
+                 smoothing = ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level))
+  })
 
-  #API plots
-  output$cirrus_aggregate <- make_dygraph(
-    split_dataset$cirrus, "Date", "Events",
-    "Full-text via API usage by day", TRUE
-  )
-  output$open_aggregate <- make_dygraph(
-    split_dataset$open, "Date", "Events",
-    "OpenSearch API usage by day", TRUE
-  )
-  output$geo_aggregate <- make_dygraph(
-    split_dataset$geo, "Date", "Events",
-    "Geo Search API usage by day", TRUE
-  )
-  output$language_aggregate <- make_dygraph(
-    split_dataset$language, "Date", "Events",
-    "Language Search API usage by day", TRUE
-  )
-  output$prefix_aggregate <- make_dygraph(
-    split_dataset$prefix, "Date", "Events",
-    "Prefix Search API usage by day", TRUE
-  )
+  ## API plots
+  output$cirrus_aggregate <- renderDygraph({
+    smooth_level <- input$smoothing_fulltext_search
+    make_dygraph(split_dataset$cirrus, "Date", "Events", "Full-text via API 
usage by day", TRUE,
+                 smoothing = ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level))
+  })
+  output$open_aggregate <- renderDygraph({
+    smooth_level <- input$smoothing_open_search
+    make_dygraph(split_dataset$open, "Date", "Events", "OpenSearch API usage 
by day", TRUE,
+                 smoothing = ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level))
+  })
+  output$geo_aggregate <- renderDygraph({
+    smooth_level <- input$smoothing_geo_search
+    make_dygraph(split_dataset$geo, "Date", "Events", "Geo Search API usage by 
day", TRUE,
+                 smoothing = ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level))
+  })
+  output$language_aggregate <- renderDygraph({
+    smooth_level <- input$smoothing_language_search
+    make_dygraph(split_dataset$language, "Date", "Events", "Language Search 
API usage by day", TRUE,
+                 smoothing = ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level))
+  })
+  output$prefix_aggregate <- renderDygraph({
+    smooth_level <- input$smoothing_prefix_search
+    make_dygraph(split_dataset$prefix, "Date", "Events", "Prefix Search API 
usage by day", TRUE,
+                 smoothing = ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level))
+  })
 
-  #Failure plots
-  output$failure_rate_plot <- make_dygraph(
-    failure_dygraph_set, "Date", "Queries",
-    "Search Queries with Zero Results, by day"
-  )
-  output$failure_rate_change_plot <- make_dygraph(
-    failure_roc_dygraph_set, "Date", "Change (%)",
-    "Zero result rate change, by day", TRUE,
-    "Rate of Change"
-  )
-  output$failure_breakdown_plot <- make_dygraph(
-    failure_breakdown_dygraph_set, "Date", "Zero Results Rate (%)",
-    "Zero result rate by search type"
-  )
-  output$suggestion_dygraph_plot <- make_dygraph(
-    suggestion_dygraph_set, "Date", "Zero Results Rate (%)",
-    "Zero Result Rates with Search Suggestions"
-  )
+  ## Failure plots
+  output$failure_rate_plot <- renderDygraph({
+    smooth_level <- input$smoothing_failure_rate
+    make_dygraph(failure_dygraph_set, "Date", "Queries", "Search Queries with 
Zero Results, by day",
+                 smoothing = ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level))
+  })
+  output$failure_rate_change_plot <- renderDygraph({
+    smooth_level <- input$smoothing_failure_rate
+    make_dygraph(failure_roc_dygraph_set, "Date", "Change (%)", "Zero result 
rate change, by day", TRUE, "Rate of Change",
+                 smoothing = ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level))
+  })
+  output$failure_breakdown_plot <- renderDygraph({
+    smooth_level <- input$smoothing_failure_breakdown
+    make_dygraph(failure_breakdown_dygraph_set, "Date", "Zero Results Rate 
(%)", "Zero result rate by search type",
+                 smoothing = ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level))
+  })
+  output$suggestion_dygraph_plot <- renderDygraph({
+    smooth_level <- input$smoothing_failure_suggestions
+    make_dygraph(suggestion_dygraph_set, "Date", "Zero Results Rate (%)", 
"Zero Result Rates with Search Suggestions",
+                 smoothing = ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level))
+  })
 
-  # KPI module
+  ## KPI module
   output$kpi_summary_date_range <- renderUI({
     date_range <- input$kpi_summary_date_range_selector
     switch(date_range,
@@ -295,7 +314,7 @@
                           sapply(as.numeric(as.character(.[date_range_index], 
"%e")), toOrdinal)))
              } %>% paste0(collapse = "-")
              return(HTML("<h3 class='kpi_date'>KPI summary for ", temp, 
":</h3>"))
-    })
+           })
     return(HTML("<h3 class='kpi_date'>KPI summary for ", temp[2], ", and % 
change from ", temp[1], ":</h3>"))
   })
   output$kpi_summary_box_load_time <- renderValueBox({
@@ -330,8 +349,7 @@
                               android_load_data, ios_load_data),
                          safe_tail, n = 90) %>%
                lapply(function(data_tail) return(data_tail$Median))
-             suppressWarnings(y <- median(apply(do.call(cbind, x), 1, median)))
-             # ^ will soon be unnecessary. warning arises from iOS data being 
88 days as of 2015-09-07
+             y <- median(apply(do.call(cbind, x), 1, median))
              return(valueBox(subtitle = "Load time", value = sprintf("%.0fms", 
y), color = "orange"))
            })
     y1 <- median(half(x, "top")); y2 <- median(half(x, "bottom")); z <- 100 * 
(y2 - y1) / y1
@@ -390,10 +408,10 @@
   })
   output$kpi_summary_api_usage_proportions <- renderPlot({
     switch (input$kpi_summary_date_range_selector,
-      daily = { n <- 1 },
-      weekly = { n <- 7 },
-      monthly = { n <- 30 },
-      quarterly = { n <- 90 }
+            daily = { n <- 1 },
+            weekly = { n <- 7 },
+            monthly = { n <- 30 },
+            quarterly = { n <- 90 }
     )
     api_latest <- cbind("Full-text via API" = safe_tail(split_dataset$cirrus, 
n)$events,
                         "Geo Search" = safe_tail(split_dataset$geo, n)$events,
@@ -405,7 +423,6 @@
                              Events = api_latest,
                              Prop = api_latest/sum(api_latest))
     api_latest <- api_latest[api_latest$Prop > 0.01, ]
-    # api_latest$text_pos <- cumsum(api_latest$Prop) + (c(0, 
cumsum(api_latest$Prop)[-nrow(api_latest)]) - cumsum(api_latest$Prop))/2
     api_latest$Label <- sprintf("%s (%.0f%%)", api_latest$API, 
100*api_latest$Prop)
     i <- which(api_latest$Prop > 0.5) # Majority API usage type gets 
additional text (for clarity)
     if ( length(i) == 1 )
@@ -414,6 +431,7 @@
     gg_prop_bar(api_latest, cols = list(item = "API", prop = "Prop", label = 
"Label"))
   })
   output$kpi_load_time_series <- renderDygraph({
+    smooth_level <- input$smoothing_kpi_load_time
     num_of_days_in_common <- min(sapply(list(desktop_load_data$Median, 
mobile_load_data$Median, android_load_data$Median, ios_load_data$Median), 
length))
     load_times <- list(desktop_load_data, mobile_load_data, android_load_data, 
ios_load_data) %>%
       lapply(safe_tail, num_of_days_in_common) %>%
@@ -422,76 +440,77 @@
       { colnames(.) <- c("Desktop", "Mobile Web", "Android", "iOS"); . } %>%
       {
         Median = apply(., 1, median)
-        # Median_change = percent_change(Median)
-        cbind(Median = Median[-1], .[-1, ])# , "Median % change" = 
Median_change[-1])
+        cbind(Median = Median, .)
       } %>%
-      xts(order.by = mobile_load_data$timestamp[-1]) # need to make dynamic
+      cbind(timestamp = safe_tail(desktop_load_data, 
num_of_days_in_common)$timestamp, .) %>%
+      smoother(smooth_level = ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level), rename = FALSE) %>%
+      { xts(.[, -1], order.by = .[, 1]) }
     return(dygraph(load_times,
-            main = "Load times over time",
-            xlab = "Date",
-            ylab = "Load time (ms)") %>%
-      dySeries("Median", axis = 'y2', strokeWidth = 4, label = "Cross-platform 
Median") %>%
-      dyAxis("y2", label = "Day-to-day % change in median load time",
-             independentTicks = TRUE, drawGrid = FALSE) %>%
-      dyLegend(width = 500, show = "always") %>%
-      dyOptions(strokeWidth = 2, colors = brewer.pal(5, "Set2")[5:1],
-                drawPoints = FALSE, pointSize = 3, labelsKMB = TRUE,
-                includeZero = TRUE) %>%
-      dyCSS(css = "./assets/css/custom.css"))
+                   main = "Load times over time",
+                   xlab = "Date",
+                   ylab = "Load time (ms)") %>%
+             dySeries("Median", axis = 'y2', strokeWidth = 4, label = 
"Cross-platform Median") %>%
+             dyAxis("y2", label = "Day-to-day % change in median load time",
+                    independentTicks = TRUE, drawGrid = FALSE) %>%
+             dyLegend(width = 500, show = "always") %>%
+             dyOptions(strokeWidth = 2, colors = brewer.pal(5, "Set2")[5:1],
+                       drawPoints = FALSE, pointSize = 3, labelsKMB = TRUE,
+                       includeZero = TRUE) %>%
+             dyCSS(css = "./assets/css/custom.css"))
   })
   output$kpi_zero_results_series <- renderDygraph({
+    smooth_level <- input$smoothing_kpi_zero_results
     zrr <- 100 * failure_dygraph_set$`Zero Result Queries` / 
failure_dygraph_set$`Search Queries`
     zrr_change <- 100 * (zrr[2:length(zrr)] - 
zrr[1:(length(zrr)-1)])/zrr[1:(length(zrr)-1)]
-    zrr <- xts(zrr, failure_dygraph_set$date)
-    colnames(zrr) <- "rate"
-    zrr_change <- xts(zrr_change, failure_dygraph_set$date[-1])
-    colnames(zrr_change) <- "change"
-    zrr <- cbind(zrr[-1, ], zrr_change)
+    zrr <- data.frame(date = failure_dygraph_set$date[-1], rate = zrr[-1], 
change = zrr_change)
+    zrr %<>% smoother(ifelse(smooth_level == "global", input$smoothing_global, 
smooth_level), rename = FALSE)
+    zrr <- xts(zrr[, -1], zrr[, 1])
     return(dygraph(zrr,
-            main = "Zero results rate over time",
-            xlab = "Date",
-            ylab = "% of search queries that yield zero results") %>%
-      dySeries("change", axis = 'y2', label = "day-to-day % change", 
strokeWidth = 1) %>%
-      dyLimit(limit = 12.50, label = "Goal: 12.50% zero results rate",
-              color = brewer.pal(3, "Set2")[3]) %>%
-      dyAxis("y2", label = "Day-to-day % change",
-             valueRange = c(-1, 1) * max(max(abs(as.numeric(zrr$change))), 10),
-             axisLineColor = brewer.pal(3, "Set2")[2],
-             axisLabelColor = brewer.pal(3, "Set2")[2],
-             independentTicks = TRUE, drawGrid = FALSE) %>%
-      dyAxis("y", drawGrid = FALSE,
-             axisLineColor = brewer.pal(3, "Set2")[1],
-             axisLabelColor = brewer.pal(3, "Set2")[1]) %>%
-      dyLimit(limit = 0, color = brewer.pal(3, "Set2")[2], strokePattern = 
"dashed") %>%
-      dyLegend(width = 400, show = "always") %>%
-      dyOptions(strokeWidth = 3, colors = brewer.pal(3, "Set2"),
-                drawPoints = FALSE, pointSize = 3, labelsKMB = TRUE,
-                includeZero = TRUE) %>%
-      dyCSS(css = "./assets/css/custom.css"))
+                   main = "Zero results rate over time",
+                   xlab = "Date",
+                   ylab = "% of search queries that yield zero results") %>%
+             dySeries("change", axis = 'y2', label = "day-to-day % change", 
strokeWidth = 1) %>%
+             dyLimit(limit = 12.50, label = "Goal: 12.50% zero results rate",
+                     color = brewer.pal(3, "Set2")[3]) %>%
+             dyAxis("y2", label = "Day-to-day % change",
+                    valueRange = c(-1, 1) * 
max(max(abs(as.numeric(zrr$change))), 10),
+                    axisLineColor = brewer.pal(3, "Set2")[2],
+                    axisLabelColor = brewer.pal(3, "Set2")[2],
+                    independentTicks = TRUE, drawGrid = FALSE) %>%
+             dyAxis("y", drawGrid = FALSE,
+                    axisLineColor = brewer.pal(3, "Set2")[1],
+                    axisLabelColor = brewer.pal(3, "Set2")[1]) %>%
+             dyLimit(limit = 0, color = brewer.pal(3, "Set2")[2], 
strokePattern = "dashed") %>%
+             dyLegend(width = 400, show = "always") %>%
+             dyOptions(strokeWidth = 3, colors = brewer.pal(3, "Set2"),
+                       drawPoints = FALSE, pointSize = 3, labelsKMB = TRUE,
+                       includeZero = TRUE) %>%
+             dyCSS(css = "./assets/css/custom.css"))
   })
   output$kpi_api_usage_series <- renderDygraph({
+    smooth_level <- input$smoothing_kpi_api_usage
     api_usage <- cbind(timestamp = split_dataset$cirrus$timestamp, 
as.data.frame(lapply(split_dataset, function(x) x$events)))
-    # api_usage <- api_usage[order(api_usage$timestamp, decreasing = FALSE), ]
     if ( input$kpi_api_usage_series_include_open ) {
       api_usage <- transform(api_usage, all = cirrus + geo + language + open + 
prefix)
     } else {
       api_usage <- transform(api_usage, all = cirrus + geo + language + prefix)
     }
     if ( input$kpi_api_usage_series_data == "raw" ) {
+      api_usage %<>% smoother(ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level), rename = FALSE)
       api_usage <- xts(api_usage[, -1], api_usage[, 1])
       if (!input$kpi_api_usage_series_include_open) {
         colnames(api_usage)[6] <- "all except open"
       }
       return(dygraph(api_usage, main = "Calls over time", xlab = "Date",
-              ylab = ifelse(input$kpi_api_usage_series_log_scale, "Calls 
(log10 scale)", "Calls")) %>%
-        dySeries("cirrus", label = "full-text via API") %>%
-        dyLegend(width = 400, show = "always") %>%
-        dyOptions(strokeWidth = 3, colors = brewer.pal(6, "Set2")[6:1],
-                  drawPoints = TRUE, pointSize = 3, labelsKMB = TRUE,
-                  includeZero = input$kpi_api_usage_series_log_scale,
-                  logscale = input$kpi_api_usage_series_log_scale
-        ) %>%
-        dyCSS(css = "./assets/css/custom.css"))
+                     ylab = ifelse(input$kpi_api_usage_series_log_scale, 
"Calls (log10 scale)", "Calls")) %>%
+               dySeries("cirrus", label = "full-text via API") %>%
+               dyLegend(width = 400, show = "always") %>%
+               dyOptions(strokeWidth = 3, colors = brewer.pal(6, "Set2")[6:1],
+                         drawPoints = FALSE, pointSize = 3, labelsKMB = TRUE,
+                         includeZero = input$kpi_api_usage_series_log_scale,
+                         logscale = input$kpi_api_usage_series_log_scale
+               ) %>%
+               dyCSS(css = "./assets/css/custom.css"))
     }
     api_usage_change <- transform(api_usage,
                                   cirrus = percent_change(cirrus),
@@ -501,16 +520,17 @@
                                   prefix = percent_change(prefix),
                                   all = percent_change(all)) %>%
                                   { .[-1, ] }
+    api_usage_change %<>% smoother(ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level), rename = FALSE)
     api_usage_change <- xts(api_usage_change[, -1], api_usage_change[, 1])
     if (!input$kpi_api_usage_series_include_open) 
colnames(api_usage_change)[6] <- "all except open"
     return(dygraph(api_usage_change,
-            main = "Day-to-day % change over time",
-            xlab = "Date", ylab = "% change") %>%
-      dyLegend(width = 400, show = "always") %>%
-      dyOptions(strokeWidth = 3, colors = brewer.pal(6, "Set2"),
-                drawPoints = TRUE, pointSize = 3, labelsKMB = TRUE,
-                includeZero = TRUE) %>%
-      dyCSS(css = "./assets/css/custom.css"))
+                   main = "Day-to-day % change over time",
+                   xlab = "Date", ylab = "% change") %>%
+             dyLegend(width = 400, show = "always") %>%
+             dyOptions(strokeWidth = 3, colors = brewer.pal(6, "Set2"),
+                       drawPoints = FALSE, pointSize = 3, labelsKMB = TRUE,
+                       includeZero = TRUE) %>%
+             dyCSS(css = "./assets/css/custom.css"))
   })
 
 })
diff --git a/ui.R b/ui.R
index cddf8c4..df544c7 100644
--- a/ui.R
+++ b/ui.R
@@ -39,9 +39,20 @@
              menuSubItem(text = "Summary", tabName = "failure_rate"),
              menuSubItem(text = "Search Type Breakdown", tabName = 
"failure_breakdown"),
              menuSubItem(text = "Search Suggestions", tabName = 
"failure_suggestions")
-    )
+    ),
+    selectInput(inputId = "smoothing_global", label = "Smoothing (Global 
Setting)", selectize = TRUE, selected = "day",
+                choices = c("No Smoothing" = "day", "Moving Average" = 
"moving_avg",
+                            "Weekly Median" = "week", "Monthly Median" = 
"month"))
   )
 )
+
+# Standardised input selector for smoothing
+smooth_select <- function(input_id, label = "Smoothing") {
+  return(selectInput(inputId = input_id, label = label, selectize = TRUE,
+                     selected = "global", choices = c("Use Global Setting" = 
"global",
+                     "No Smoothing" = "day", "Moving Average" = "moving_avg",
+                     "Weekly Median" = "week", "Monthly Median" = "month")))
+}
 
 #Body elements for the search visualisations.
 body <- dashboardBody(
@@ -59,9 +70,11 @@
             includeMarkdown("./assets/content/kpis_summary.md")
             ),
     tabItem(tabName = "kpi_load_time",
+            smooth_select("smoothing_kpi_load_time"),
             dygraphOutput("kpi_load_time_series"),
             includeMarkdown("./assets/content/kpi_load_time.md")),
     tabItem(tabName = "kpi_zero_results",
+            smooth_select("smoothing_kpi_zero_results"),
             dygraphOutput("kpi_zero_results_series"),
             includeMarkdown("./assets/content/kpi_zero_results.md")),
     tabItem(tabName = "kpi_api_usage",
@@ -70,15 +83,16 @@
                                          choices = list("Calls" = "raw",
                                                         "Day-to-day % change" 
= "change"),
                                          inline = TRUE),
-                            width = 5),
+                            width = 4),
                      column(checkboxInput("kpi_api_usage_series_log_scale",
                                           label = "Log10 Scale",
                                           value = FALSE),
-                            width = 3),
+                            width = 2),
                      column(checkboxInput("kpi_api_usage_series_include_open",
                                           label = "Include OpenSearch in 
total",
                                           value = TRUE),
-                            width = 4)),
+                            width = 3),
+                     column(smooth_select("smoothing_kpi_api_usage"), width = 
3)),
             dygraphOutput("kpi_api_usage_series"),
             includeMarkdown("./assets/content/kpi_api_usage.md")),
     tabItem(tabName = "desktop_events",
@@ -87,9 +101,11 @@
               valueBoxOutput("desktop_event_resultsets"),
               valueBoxOutput("desktop_event_clickthroughs")
             ),
+            smooth_select("smoothing_desktop_event"),
             dygraphOutput("desktop_event_plot"),
             includeMarkdown("./assets/content/desktop_events.md")),
     tabItem(tabName = "desktop_load",
+            smooth_select("smoothing_desktop_load"),
             dygraphOutput("desktop_load_plot"),
             includeMarkdown("./assets/content/desktop_load.md")),
     tabItem(tabName = "mobile_events",
@@ -98,10 +114,12 @@
               valueBoxOutput("mobile_event_resultsets"),
               valueBoxOutput("mobile_event_clickthroughs")
             ),
+            smooth_select("smoothing_mobile_event"),
             dygraphOutput("mobile_event_plot"),
             includeMarkdown("./assets/content/mobile_events.md")
     ),
     tabItem(tabName = "mobile_load",
+            smooth_select("smoothing_mobile_load"),
             dygraphOutput("mobile_load_plot"),
             includeMarkdown("./assets/content/mobile_load.md")
     ),
@@ -111,45 +129,55 @@
               valueBoxOutput("app_event_resultsets"),
               valueBoxOutput("app_event_clickthroughs")
             ),
+            smooth_select("smoothing_app_event"),
             dygraphOutput("android_event_plot"),
             dygraphOutput("ios_event_plot"),
             includeMarkdown("./assets/content/app_events.md")
     ),
     tabItem(tabName = "app_load",
+            smooth_select("smoothing_app_load"),
             dygraphOutput("android_load_plot"),
             dygraphOutput("ios_load_plot"),
             includeMarkdown("./assets/content/app_load.md")
     ),
     tabItem(tabName = "fulltext_search",
+            smooth_select("smoothing_fulltext_search"),
             dygraphOutput("cirrus_aggregate"),
             includeMarkdown("./assets/content/fulltext_basic.md")
     ),
     tabItem(tabName = "open_search",
+            smooth_select("smoothing_open_search"),
             dygraphOutput("open_aggregate"),
             includeMarkdown("./assets/content/open_basic.md")
     ),
     tabItem(tabName = "geo_search",
+            smooth_select("smoothing_geo_search"),
             dygraphOutput("geo_aggregate"),
             includeMarkdown("./assets/content/geo_basic.md")
     ),
     tabItem(tabName = "prefix_search",
+            smooth_select("smoothing_prefix_search"),
             dygraphOutput("prefix_aggregate"),
             includeMarkdown("./assets/content/prefix_basic.md")
     ),
     tabItem(tabName = "language_search",
+            smooth_select("smoothing_language_search"),
             dygraphOutput("language_aggregate"),
             includeMarkdown("./assets/content/language_basic.md")
     ),
     tabItem(tabName = "failure_rate",
+            smooth_select("smoothing_failure_rate"),
             dygraphOutput("failure_rate_plot"),
             dygraphOutput("failure_rate_change_plot"),
             includeMarkdown("./assets/content/failure_rate.md")
     ),
     tabItem(tabName = "failure_breakdown",
+            smooth_select("smoothing_failure_breakdown"),
             dygraphOutput("failure_breakdown_plot"),
             includeMarkdown("./assets/content/failure_breakdown.md")
     ),
     tabItem(tabName = "failure_suggestions",
+            smooth_select("smoothing_failure_suggestions"),
             dygraphOutput("suggestion_dygraph_plot"),
             includeMarkdown("./assets/content/failure_suggests.md")
     )
diff --git a/utils.R b/utils.R
index 9dbaeb9..882dad5 100644
--- a/utils.R
+++ b/utils.R
@@ -17,33 +17,81 @@
   return(readr::read_delim(con, delim = "\t"))
 }
 
+# Takes an untidy (read: dygraph-appropriate) dataset and adds
+# columns for each variable consisting of the smoothed, averaged mean
+smoother <- function(dataset, smooth_level = "day", rename = TRUE) {
+
+  # Determine the names and levels of aggregation. By default
+  # a smoothing level of "day" is assumed, which is no smoothing
+  # whatsoever, and so the original dataset is returned.
+  switch(smooth_level,
+         moving_avg = {
+           df <- apply(dataset[, -1, drop = FALSE], 2, function(x) {
+             y <- xts(x, dataset[, 1])
+             return(as.numeric(zoo::rollmean(x, k = 17, fill = NA)))
+           }) %>% as.data.frame %>% cbind(timestamp = dataset[, 1], .)
+           names(df) <- names(dataset)
+           if (rename) names(df)[-1] <- paste(names(df)[-1], " (Moving 
average)")
+           return(df)
+         },
+         week = {
+           dataset$filter_1 <- lubridate::week(dataset[, 1])
+           dataset$filter_2 <- lubridate::year(dataset[, 1])
+           name_append <- ifelse(rename, " (Weekly average)", "")
+         },
+         month = {
+           dataset$filter_1 <- lubridate::month(dataset[, 1])
+           dataset$filter_2 <- lubridate::year(dataset[, 1])
+           name_append <- ifelse(rename, " (Monthly average)", "")
+         },
+         {
+           return(dataset)
+         }
+  )
+
+  # If we're still here it was weekly or monthly. Calculate
+  # the average for each unique permutation of filters
+
+  result <- ddply(.data = dataset,
+                  .variables = c("filter_1", "filter_2"),
+                  .fun = function(df, name_append){
+
+                    # Construct output names for the averages, compute those 
averages, and
+                    # apply said names.
+                    output_names <- paste0(names(df)[2:(ncol(df) - 2)], 
name_append)
+                    holding <- apply(df[, 2:(ncol(df) - 2), drop = FALSE], 2, 
FUN = median) %>%
+                      round %>% t %>% as.data.frame
+                    names(holding) <- output_names
+
+                    # Return the bound original values and averaged values
+                    return(cbind(df[, 1, drop = FALSE], holding))
+                  }, name_append = name_append)
+
+  return(result[, !(names(result) %in% c("filter_1","filter_2"))])
+}
+
 #Create a dygraph using our standard format.
-make_dygraph <- function(data, x, y, title, is_single = FALSE, legend_name = 
NULL, use_si = TRUE){
+make_dygraph <- function(data, x, y, title, is_single = FALSE, legend_name = 
NULL, use_si = TRUE, smoothing = "day") {
   # cat("Making dygraph:", title, "\n"); # Debugging
-  if(is_single){
-    data <- xts(data[,3], data[[1]])
-    if(is.null(legend_name)){
+  if (is_single) {
+    data <- smoother(as.data.frame(data[, c(1, 3)]), smooth_level = smoothing)
+    data <- xts(data[, 2], data[, 1])
+    if (is.null(legend_name)) {
       names(data) <- "events"
     } else {
       names(data) <- legend_name
     }
   } else {
-    data <- xts(data[,-1], order.by = data[[1]])
+    data %<>% smoother(smooth_level = smoothing)
+    data <- xts(data[, -1], order.by = data[, 1])
   }
-  renderDygraph({
-    dyCSS(
-      dyOptions(
-        dyLegend(
-          dygraph(data,
-                  main = title,
-                  xlab = x, ylab = y),
-          width = 400, show = "always"
-        ), strokeWidth = 3, colors = brewer.pal(3, "Set2"),
-        drawPoints = FALSE, pointSize = 3, labelsKMB = use_si,
-        includeZero = TRUE
-      )
-      ,css = "./assets/css/custom.css")
-  })
+  return(dygraph(data, main = title, xlab = x, ylab = y) %>%
+           dyLegend(width = 400, show = "always") %>%
+           dyOptions(strokeWidth = 3,
+                     colors = brewer.pal(max(3, ncol(data)), "Set2"),
+                     drawPoints = FALSE, pointSize = 3, labelsKMB = use_si,
+                     includeZero = TRUE) %>%
+           dyCSS(css = "./assets/css/custom.css"))
 }
 
 # Computes a median absolute deviation
@@ -145,55 +193,4 @@
     message("Sorting by the date/timestamp column before returning the bottom 
", n, " rows.")
   }
   return(tail(x[order(x[[timestamp_column[1]]]), ], n))
-}
-
-# Takes an untidy (read: dygraph-appropriate) dataset and adds
-# columns for each variable consisting of the smoothed, averaged mean
-smooth_mean <- function(dataset, smooth_level = "day"){
-
-  # Determine the names and levels of aggregation. By default
-  # a smoothing level of "day" is assumed, which is no smoothing
-  # whatsoever, and so the original dataset is returned.
-  switch(smooth_level,
-    week = {
-      dataset$filter_1 <- lubridate::week(dataset[, 1])
-      dataset$filter_2 <- lubridate::year(dataset[, 1])
-      name_append <- "(Weekly average)"
-    },
-    month = {
-      dataset$filter_1 <- lubridate::month(dataset[, 1])
-      dataset$filter_2 <- lubridate::year(dataset[, 1])
-      name_append <- "(Monthly average)"
-    },
-    {
-      return(dataset)
-    }
-  )
-
-  # If we're still here it was weekly or monthly. Calculate
-  # the average for each unique permutation of filters
-
-  result <- ddply(.data = dataset,
-                  .variables = c("filter_1", "filter_2"),
-                  .fun = function(df, name_append){
-
-                    # Construct output names for the averages, compute those 
averages, and
-                    # apply said names.
-                    output_names <- paste(names(df)[2:(ncol(df) - 2)], 
name_append)
-                    holding <- as.data.frame(t(round(apply(df[,2:(ncol(df) - 
2)], 2, FUN = median))))
-                    names(holding) <- output_names
-
-                    # Return the bound original values and averaged values
-                    return(cbind(cbind(df[,1, drop=FALSE], holding)))
-                  }, name_append = name_append)
-
-  return(result[,!names(result) %in% c("filter_1","filter_2")])
-}
-
-# Standardised input selector for smoothing
-standard_select <- function(input_id){
-  return(
-    shiny::selectInput(inputId = input_id, label = "Smoothing",
-                       choices = c("no smoothing","weekly median","monthly 
median"), selected = "day")
-  )
 }

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I5b749ff426019bb218fddf1b1bfeb3d369273572
Gerrit-PatchSet: 1
Gerrit-Project: wikimedia/discovery/rainbow
Gerrit-Branch: master
Gerrit-Owner: Bearloga <[email protected]>

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

Reply via email to