Bearloga has uploaded a new change for review.

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

Change subject: Add time frame selection (preset + custom)   > Added an option 
to view last 7/30/90 days or have a custom range   > Uses global setting by 
default with ability to override for "local" plot   > This is independent of 
the KPI date range stuff because that g
......................................................................

Add time frame selection (preset + custom)
  > Added an option to view last 7/30/90 days or have a custom range
  > Uses global setting by default with ability to override for "local" plot
  > This is independent of the KPI date range stuff because that gets 2x range
    in order to compute a % change, so letting the user specify a "custom" range
    would take a lot of work and would be wonky. So the new selector (specific
    to KPI tabset) only has presets: Yesterday and  Last 7/30/90 days.
+ Moved smoothing into a 'Global Settings' menu where time frame also lives
+ Fixes the KPI date range selection
  > It relied on unstable data, so that got fixed.
  > It used "safe_tail" which is bad because it relies on n rather than dates
  > Switched over to subsetting by dates

! Note: This relies on polloi 0.0.5 to be CR'd and merged.

Bug: T116782
Change-Id: I72a90a00235c536da35a893e03a68f9a23caa62d
---
M CHANGELOG.md
M server.R
M tab_documentation/kpis_summary.md
M ui.R
M utils.R
5 files changed, 314 insertions(+), 202 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/wikimedia/discovery/rainbow 
refs/changes/74/251174/1

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3628ba9..12412a7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,14 @@
 # Change Log (Patch Notes)
 All notable changes to this project will be documented in this file.
 
+## 2015/11/05
+- Added time frame selection
+- Fixed KPI date range selection (more robust, correct)
+
+## 2015/11/04
+- Began the move to 'date'-named datasets (as opposed to 'timestamp'-named 
datasets)
+- Removed moving average as a smoothing option
+
 ## 2015/09/30
 - Added a change log
 - Added a contributor code of conduct
diff --git a/server.R b/server.R
index a17775e..287487c 100644
--- a/server.R
+++ b/server.R
@@ -1,4 +1,4 @@
-## Version 0.2.0
+## Version 0.3.0
 source("utils.R")
 
 existing_date <- Sys.Date() - 1
@@ -14,6 +14,24 @@
     read_augmented_clickthrough()
     read_lethal_dose()
     existing_date <<- Sys.Date()
+  }
+
+  # This is used to subset for getting the bounds for 
polloi::subset_by_date_range():
+  time_frame_range <- function(tf_selector) {
+    tf_setting <- input[[tf_selector]]
+    if ( tf_setting == 'global' ) {
+      if ( input$timeframe_global == 'custom' ) {
+        return(input$daterange_global)
+      } else {
+        tf_setting <- input$timeframe_global
+      }
+    }
+    return(switch(tf_setting,
+                  all = c(as.Date("2015-04-14"), Sys.Date()-1),
+                  week = c(Sys.Date()-8, Sys.Date()-1),
+                  month = c(Sys.Date()-31, Sys.Date()-1),
+                  quarter = c(Sys.Date()-91, Sys.Date()-1),
+                  custom = input[[paste(tf_selector, "daterange", sep = 
"_")]]))
   }
 
   ## Desktop value boxes
@@ -46,18 +64,17 @@
 
   ## The dynamic graphs of events on desktop
   output$desktop_event_plot <- renderDygraph({
-    polloi::make_dygraph(data = polloi::smoother(desktop_dygraph_set,
-                                         smooth_level = 
polloi::smooth_switch(input$smoothing_global,
-                                                                              
input$smoothing_desktop_event)),
-                         xlab = "Date", ylab = "Events", title = "Desktop 
search events, by day")
+    desktop_dygraph_set %>%
+      polloi::smoother(smooth_level = 
polloi::smooth_switch(input$smoothing_global, input$smoothing_desktop_event)) 
%>%
+      
polloi::subset_by_date_range(time_frame_range("desktop_event_timeframe")) %>%
+      polloi::make_dygraph(xlab = "Date", ylab = "Events", title = "Desktop 
search events, by day")
   })
 
   output$desktop_load_plot <- renderDygraph({
-    polloi::make_dygraph(data = polloi::smoother(desktop_load_data,
-                                         smooth_level = 
polloi::smooth_switch(input$smoothing_global,
-                                                                              
input$smoothing_desktop_load)),
-                         xlab = "Date", ylab = "Load time (ms)", title = 
"Desktop load times, by day",
-                         use_si = FALSE)
+    desktop_load_data %>%
+      polloi::smoother(smooth_level = 
polloi::smooth_switch(input$smoothing_global, input$smoothing_desktop_load)) %>%
+      polloi::subset_by_date_range(time_frame_range("desktop_load_timeframe")) 
%>%
+      polloi::make_dygraph(xlab = "Date", ylab = "Load time (ms)", title = 
"Desktop load times, by day", use_si = FALSE)
   })
 
   ## Mobile value boxes
@@ -90,18 +107,17 @@
 
   ## Mobile plots
   output$mobile_event_plot <- renderDygraph({
-    polloi::make_dygraph(data = polloi::smoother(mobile_dygraph_set,
-                                         smooth_level = 
polloi::smooth_switch(input$smoothing_global,
-                                                                              
input$smoothing_mobile_event)),
-                         xlab = "Date", ylab = "Events", title = "Mobile 
search events, by day")
+    mobile_dygraph_set %>%
+      polloi::smoother(smooth_level = 
polloi::smooth_switch(input$smoothing_global, input$smoothing_mobile_event)) %>%
+      polloi::subset_by_date_range(time_frame_range("mobile_event_timeframe")) 
%>%
+      polloi::make_dygraph(xlab = "Date", ylab = "Events", title = "Mobile 
search events, by day")
   })
 
   output$mobile_load_plot <- renderDygraph({
-    polloi::make_dygraph(data = polloi::smoother(mobile_load_data,
-                                         smooth_level = 
polloi::smooth_switch(input$smoothing_global,
-                                                                              
input$smoothing_mobile_load)),
-                         xlab = "Date", ylab = "Load time (ms)", "Mobile 
result load times, by day",
-                         use_si = FALSE)
+    mobile_load_data %>%
+      polloi::smoother(smooth_level = 
polloi::smooth_switch(input$smoothing_global, input$smoothing_mobile_load)) %>%
+      polloi::subset_by_date_range(time_frame_range("mobile_load_timeframe")) 
%>%
+      polloi::make_dygraph(xlab = "Date", ylab = "Load time (ms)", title = 
"Mobile search events, by day", use_si = FALSE)
   })
 
   ## App value boxes
@@ -134,112 +150,103 @@
 
   ## App plots
   output$android_event_plot <- renderDygraph({
-    polloi::make_dygraph(data = polloi::smoother(android_dygraph_set,
-                                         smooth_level = 
polloi::smooth_switch(input$smoothing_global,
-                                                                              
input$smoothing_app_event)),
-                         xlab = "Date", ylab = "Events", "Android mobile app 
search events, by day")
+    android_dygraph_set %>%
+      polloi::smoother(smooth_level = 
polloi::smooth_switch(input$smoothing_global, input$smoothing_app_event)) %>%
+      polloi::subset_by_date_range(time_frame_range("app_event_timeframe")) %>%
+      polloi::make_dygraph(xlab = "Date", ylab = "Events", title = "Android 
mobile app search events, by day")
   })
 
   output$android_load_plot <- renderDygraph({
-    polloi::make_dygraph(data = polloi::smoother(android_load_data,
-                                         smooth_level = 
polloi::smooth_switch(input$smoothing_global,
-                                                                              
input$smoothing_app_load)),
-                         xlab = "Date", ylab = "Load time (ms)", "Android 
result load times, by day",
-                         use_si = FALSE)
+    android_load_data %>%
+      polloi::smoother(smooth_level = 
polloi::smooth_switch(input$smoothing_global, input$smoothing_app_load)) %>%
+      polloi::subset_by_date_range(time_frame_range("app_load_timeframe")) %>%
+      polloi::make_dygraph(xlab = "Date", ylab = "Load time (ms)", title = 
"Android result load times, by day", use_si = FALSE)
   })
 
   output$ios_event_plot <- renderDygraph({
-    polloi::make_dygraph(data = polloi::smoother(ios_dygraph_set,
-                                         smooth_level = 
polloi::smooth_switch(input$smoothing_global,
-                                                                              
input$smoothing_app_event)),
-                         xlab = "Date", ylab = "Events", "iOS mobile app 
search events, by day")
+    ios_dygraph_set %>%
+      polloi::smoother(smooth_level = 
polloi::smooth_switch(input$smoothing_global, input$smoothing_app_event)) %>%
+      polloi::subset_by_date_range(time_frame_range("app_event_timeframe")) %>%
+      polloi::make_dygraph(xlab = "Date", ylab = "Events", title = "iOS mobile 
app search events, by day")
   })
 
   output$ios_load_plot <- renderDygraph({
-    polloi::make_dygraph(data = polloi::smoother(ios_load_data,
-                                         smooth_level = 
polloi::smooth_switch(input$smoothing_global,
-                                                                              
input$smoothing_app_load)),
-                         xlab = "Date", ylab = "Load time (ms)", "iOS result 
load times, by day",
-                         use_si = FALSE)
+    ios_load_data %>%
+      polloi::smoother(smooth_level = 
polloi::smooth_switch(input$smoothing_global, input$smoothing_app_load)) %>%
+      polloi::subset_by_date_range(time_frame_range("app_load_timeframe")) %>%
+      polloi::make_dygraph(xlab = "Date", ylab = "Load time (ms)", title = 
"iOS result load times, by day", use_si = FALSE)
   })
 
   ## API plots
   output$cirrus_aggregate <- renderDygraph({
-    polloi::make_dygraph(data = polloi::smoother(split_dataset$cirrus[,c(1,3)],
-                                         smooth_level = 
polloi::smooth_switch(input$smoothing_global,
-                                                                              
input$smoothing_fulltext_search)),
-                         xlab = "Date", ylab = "Searches", "Full-text via API 
usage by day",
-                         legend_name = "Searches")
+    split_dataset$cirrus[, c(1, 3)] %>%
+      polloi::smoother(smooth_level = 
polloi::smooth_switch(input$smoothing_global, input$smoothing_fulltext_search)) 
%>%
+      
polloi::subset_by_date_range(time_frame_range("fulltext_search_timeframe")) %>%
+      polloi::make_dygraph(xlab = "Date", ylab = "Searches", title = 
"Full-text via API usage by day", legend_name = "Searches")
   })
 
   output$open_aggregate <- renderDygraph({
-    polloi::make_dygraph(data = polloi::smoother(split_dataset$open[,c(1,3)],
-                                         smooth_level = 
polloi::smooth_switch(input$smoothing_global,
-                                                                              
input$smoothing_open_search)),
-                         xlab = "Date", ylab = "Searches", "OpenSearch API 
usage by day",
-                         legend_name = "Searches")
+    split_dataset$open[, c(1, 3)] %>%
+      polloi::smoother(smooth_level = 
polloi::smooth_switch(input$smoothing_global, input$smoothing_open_search)) %>%
+      polloi::subset_by_date_range(time_frame_range("open_search_timeframe")) 
%>%
+      polloi::make_dygraph(xlab = "Date", ylab = "Searches", title = 
"OpenSearch API usage by day", legend_name = "Searches")
   })
 
   output$geo_aggregate <- renderDygraph({
-    polloi::make_dygraph(data = polloi::smoother(split_dataset$geo[,c(1,3)],
-                                         smooth_level = 
polloi::smooth_switch(input$smoothing_global,
-                                                                              
input$smoothing_geo_search)),
-                         xlab = "Date", ylab = "Searches", "Geo Search API 
usage by day",
-                         legend_name = "Searches")
+    split_dataset$geo[, c(1, 3)] %>%
+      polloi::smoother(smooth_level = 
polloi::smooth_switch(input$smoothing_global, input$smoothing_geo_search)) %>%
+      polloi::subset_by_date_range(time_frame_range("geo_search_timeframe")) 
%>%
+      polloi::make_dygraph(xlab = "Date", ylab = "Searches", title = "Geo 
Search API usage by day", legend_name = "Searches")
   })
 
   output$language_aggregate <- renderDygraph({
-    polloi::make_dygraph(data = 
polloi::smoother(split_dataset$language[,c(1,3)],
-                                         smooth_level = 
polloi::smooth_switch(input$smoothing_global,
-                                                                              
input$smoothing_language_search)),
-                         xlab = "Date", ylab = "Searches", "Language Search 
API usage by day",
-                         legend_name = "Searches")
+    split_dataset$language[, c(1, 3)] %>%
+      polloi::smoother(smooth_level = 
polloi::smooth_switch(input$smoothing_global, input$smoothing_language_search)) 
%>%
+      
polloi::subset_by_date_range(time_frame_range("language_search_timeframe")) %>%
+      polloi::make_dygraph(xlab = "Date", ylab = "Searches", title = "Language 
Search API usage by day", legend_name = "Searches")
   })
 
   output$prefix_aggregate <- renderDygraph({
-    polloi::make_dygraph(data = polloi::smoother(split_dataset$prefix[,c(1,3)],
-                                         smooth_level = 
polloi::smooth_switch(input$smoothing_global,
-                                                                              
input$smoothing_prefix_search)),
-                         xlab = "Date", ylab = "Searches", "Prefix Search API 
usage by day",
-                         legend_name = "Searches")
+    split_dataset$prefix[, c(1, 3)] %>%
+      polloi::smoother(smooth_level = 
polloi::smooth_switch(input$smoothing_global, input$smoothing_prefix_search)) 
%>%
+      
polloi::subset_by_date_range(time_frame_range("prefix_search_timeframe")) %>%
+      polloi::make_dygraph(xlab = "Date", ylab = "Searches", title = "Prefix 
Search API usage by day", legend_name = "Searches")
   })
 
   # Failure plots
   output$failure_rate_plot <- renderDygraph({
-    polloi::make_dygraph(data = polloi::smoother(failure_dygraph_set,
-                                         smooth_level = 
polloi::smooth_switch(input$smoothing_global,
-                                                                              
input$smoothing_failure_rate)),
-                         xlab = "Date", ylab = "Queries", "Search Queries with 
Zero Results, by day")
+    failure_dygraph_set %>%
+      polloi::smoother(smooth_level = 
polloi::smooth_switch(input$smoothing_global, input$smoothing_failure_rate)) %>%
+      polloi::subset_by_date_range(time_frame_range("failure_rate_timeframe")) 
%>%
+      polloi::make_dygraph(xlab = "Date", ylab = "Queries", title = "Search 
Queries with Zero Results, by day")
   })
 
   output$failure_rate_change_plot <- renderDygraph({
-    polloi::make_dygraph(data = 
polloi::smoother(failure_roc_dygraph_set[,c(1,3)],
-                                         smooth_level = 
polloi::smooth_switch(input$smoothing_global,
-                                                                              
input$smoothing_failure_rate)),
-                         xlab = "Date", ylab = "Change (%)", "Zero Results 
rate change, by day",
-                         legend_name = "Change")
+    failure_roc_dygraph_set[, c(1, 3)] %>%
+      polloi::smoother(smooth_level = 
polloi::smooth_switch(input$smoothing_global, input$smoothing_failure_rate)) %>%
+      polloi::subset_by_date_range(time_frame_range("failure_rate_timeframe")) 
%>%
+      polloi::make_dygraph(xlab = "Date", ylab = "Change (%)", title = "Zero 
Results rate change, by day", legend_name = "Change")
   })
 
   output$failure_breakdown_plot <- renderDygraph({
-    polloi::make_dygraph(data = polloi::smoother(failure_breakdown_dygraph_set,
-                                         smooth_level = 
polloi::smooth_switch(input$smoothing_global,
-                                                                              
input$smoothing_failure_breakdown)),
-                         xlab = "Date", ylab = "Zero Results Rate (%)", "Zero 
result rate by search type")
+    failure_breakdown_dygraph_set %>%
+      polloi::smoother(smooth_level = 
polloi::smooth_switch(input$smoothing_global, 
input$smoothing_failure_breakdown)) %>%
+      
polloi::subset_by_date_range(time_frame_range("failure_breakdown_timeframe")) 
%>%
+      polloi::make_dygraph(xlab = "Date", ylab = "Zero Results Rate (%)", 
title = "Zero result rate by search type")
   })
 
   output$suggestion_dygraph_plot <- renderDygraph({
-    polloi::make_dygraph(data = polloi::smoother(suggestion_dygraph_set,
-                                         smooth_level = 
polloi::smooth_switch(input$smoothing_global,
-                                                                              
input$smoothing_failure_suggestions)),
-                         xlab = "Date", ylab = "Zero Results Rate (%)", "Zero 
Result Rates with Search Suggestions")
+    suggestion_dygraph_set %>%
+      polloi::smoother(smooth_level = 
polloi::smooth_switch(input$smoothing_global, 
input$smoothing_failure_suggestions)) %>%
+      
polloi::subset_by_date_range(time_frame_range("failure_suggestions_timeframe")) 
%>%
+      polloi::make_dygraph(xlab = "Date", ylab = "Zero Results Rate (%)", 
title = "Zero Result Rates with Search Suggestions")
   })
 
   output$lethal_dose_plot <- renderDygraph({
-    polloi::make_dygraph(data = polloi::smoother(user_page_visit_dataset,
-                                                 smooth_level = 
polloi::smooth_switch(input$smoothing_global,
-                                                                               
       input$smoothing_lethal_dose_plot)),
-                         xlab = "", ylab = "Time (s)",
-                         title = "Time at which we have lost N% of the users") 
%>%
+    user_page_visit_dataset %>%
+      polloi::smoother(smooth_level = 
polloi::smooth_switch(input$smoothing_global, 
input$smoothing_lethal_dose_plot)) %>%
+      polloi::subset_by_date_range(time_frame_range("lethal_dose_timeframe")) 
%>%
+      polloi::make_dygraph(xlab = "", ylab = "Time (s)", title = "Time at 
which we have lost N% of the users") %>%
       dyAxis("x", ticker = "Dygraph.dateTicker", axisLabelFormatter = 
CustomAxisFormatter,
              axisLabelWidth = 100, pixelsPerLabel = 80) %>%
       dyLegend(labelsDiv = "lethal_dose_plot_legend")
@@ -250,65 +257,74 @@
     date_range <- input$kpi_summary_date_range_selector
     switch(date_range,
            daily = {
-             temp <- polloi::safe_tail(desktop_load_data, 2)$date %>% {
-               paste0(as.character(., "%A, %b "),
-                      sub("([a-z]{2})", "<sup>\\1</sup>",
-                          sapply(as.numeric(as.character(., "%e")), 
toOrdinal)))
-             }
+             dates <- Sys.Date() - c(1, 2)
+             temp <- dates %>%
+               as.character("%e") %>%
+               as.numeric %>%
+               sapply(toOrdinal) %>%
+               sub("([a-z]{2})", "<sup>\\1</sup>", .) %>%
+               paste0(as.character(dates, "%A, %b "), .)
            },
            weekly = {
-             date_range_index <- c(1, 7, 8, 14)
-             temp <- polloi::safe_tail(desktop_load_data, 
date_range_index[4])$date %>% {
-               paste0(as.character(.[date_range_index], "%b "),
-                      sub("([a-z]{2})", "<sup>\\1</sup>",
-                          sapply(as.numeric(as.character(.[date_range_index], 
"%e")), toOrdinal)))
-             } %>% {
-               c(paste(.[1:2], collapse = "-"), paste(.[3:4], collapse = "-"))
-             }
+             dates <- Sys.Date() - c(1, 8, 9, 15)
+             temp <- dates %>%
+               as.character("%e") %>%
+               as.numeric %>%
+               sapply(toOrdinal) %>%
+               sub("([a-z]{2})", "<sup>\\1</sup>", .) %>%
+               paste0(as.character(dates, "%b "), .) %>%
+               {
+                 c(paste(.[1:2], collapse = "-"), paste(.[3:4], collapse = 
"-"))
+               }
            },
            monthly = {
-             date_range_index <- c(1, 31, 31, 60)
-             temp <- polloi::safe_tail(desktop_load_data, 
date_range_index[4])$date %>% {
-               paste0(as.character(.[date_range_index], "%b "),
-                      sub("([a-z]{2})", "<sup>\\1</sup>",
-                          sapply(as.numeric(as.character(.[date_range_index], 
"%e")), toOrdinal)))
-             } %>% {
-               c(paste(.[1:2], collapse = "-"), paste(.[3:4], collapse = "-"))
-             }
+             dates <- Sys.Date() - c(1, 31, 32, 61)
+             temp <- dates %>%
+               as.character("%e") %>%
+               as.numeric %>%
+               sapply(toOrdinal) %>%
+               sub("([a-z]{2})", "<sup>\\1</sup>", .) %>%
+               paste0(as.character(dates, "%b "), .) %>%
+               {
+                 c(paste(.[1:2], collapse = "-"), paste(.[3:4], collapse = 
"-"))
+               }
            },
            quarterly = {
-             date_range_index <- c(1, 90)
-             temp <- polloi::safe_tail(desktop_load_data, 
date_range_index[2])$date %>% {
-               paste0(as.character(.[date_range_index], "%B "),
-                      sub("([a-z]{2})", "<sup>\\1</sup>",
-                          sapply(as.numeric(as.character(.[date_range_index], 
"%e")), toOrdinal)))
-             } %>% paste0(collapse = "-")
-             return(HTML("<h3 class='kpi_date'>KPI summary for ", temp, 
":</h3>"))
+             dates <- Sys.Date() - c(1, 91)
+             return(dates %>%
+                      as.character("%e") %>%
+                      as.numeric %>%
+                      sapply(toOrdinal) %>%
+                      sub("([a-z]{2})", "<sup>\\1</sup>", .) %>%
+                      paste0(as.character(dates, "%B "), .) %>%
+                      paste0(collapse = "-") %>%
+                      HTML("<h3 class='kpi_date'>KPI summary for ", ., 
":</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({
     date_range <- input$kpi_summary_date_range_selector
-    x <- lapply(list(desktop_load_data, mobile_load_data,
-                     android_load_data, ios_load_data),
-                polloi::safe_tail, n = date_range_switch(date_range)) %>%
+    x <- list(desktop_load_data, mobile_load_data, android_load_data, 
ios_load_data) %>%
+      lapply(polloi::subset_by_date_range, from = start_date(date_range), to = 
Sys.Date() - 1) %>%
       lapply(function(data_tail) return(data_tail$Median))
     if ( date_range == "quarterly" ) {
-      y <- median(apply(do.call(cbind, x), 1, median))
+      y <- median(apply(do.call(cbind_fill, x), 1, median, na.rm = TRUE))
       return(valueBox(subtitle = "Load time", value = sprintf("%.0fms", y), 
color = "orange"))
     }
-    x %<>% do.call(cbind, .) %>% apply(MARGIN = 1, FUN = median)
+    missing_values <- any(is.na(do.call(cbind_fill, x)))
+    x %<>% do.call(cbind_fill, .) %>% apply(MARGIN = 1, FUN = median, na.rm = 
TRUE)
     y1 <- median(polloi::half(x)); y2 <- median(polloi::half(x, FALSE)); z <- 
100 * (y2 - y1) / y1
     if (abs(z) > 0) {
-      return(valueBox(subtitle = sprintf("Load time (%.1f%%)", z),
-                      value = sprintf("%.0fms", y2),
+      return(valueBox(subtitle = sprintf("Load time (%s%.1f%%)", 
ifelse(missing_values, "~", ""), z),
+                      value = sprintf("%s%.0fms", ifelse(missing_values, "~", 
""), y2),
                       color = polloi::cond_color(z > 0, "red"), icon = 
polloi::cond_icon(z > 0)))
     }
     return(valueBox(subtitle = "Load time (no change)", value = 
sprintf("%.0fms", y2), color = "orange"))
   })
   output$kpi_summary_box_zero_results <- renderValueBox({
     date_range <- input$kpi_summary_date_range_selector
-    x <- polloi::safe_tail(failure_dygraph_set, date_range_switch(date_range))
+    x <- polloi::subset_by_date_range(failure_dygraph_set, from = 
start_date(date_range), to = Sys.Date() - 1)
     x <- transform(x, Rate = `Zero Result Queries` / `Search Queries`)$Rate
     if (date_range == "quarterly") {
       return(valueBox(subtitle = "Zero results rate", color = "orange",
@@ -327,9 +343,10 @@
   })
   output$kpi_summary_box_api_usage <- renderValueBox({
     date_range <- input$kpi_summary_date_range_selector
-    x <- lapply(split_dataset, function(x) {
-      polloi::safe_tail(x, date_range_switch(date_range))$events
-    }) %>% do.call(cbind, .) %>%
+    x <- split_dataset %>%
+      lapply(polloi::subset_by_date_range, from = start_date(date_range), to = 
Sys.Date() - 1) %>%
+      lapply(function(x) return(x$events)) %>%
+      do.call(cbind, .) %>%
       transform(total = cirrus + geo + language + open + prefix) %>%
       { .$total }
     if (date_range == "quarterly") {
@@ -351,7 +368,7 @@
       return(valueBox(subtitle = "User engagement", color = "black", value = 
"NA"))
     }
     #=========================================================================
-    x <- polloi::safe_tail(augmented_clickthroughs, 
date_range_switch(date_range))
+    x <- polloi::subset_by_date_range(augmented_clickthroughs, from = 
start_date(date_range), to = Sys.Date() - 1)
     if (date_range == "quarterly") {
       return(valueBox(subtitle = "User engagement", color = "orange",
                       value = sprintf("%.1f%%", median(x$user_engagement))))
@@ -370,12 +387,13 @@
                     value = sprintf("%.1f%%", y2), color = "orange"))
   })
   output$kpi_summary_api_usage_proportions <- renderPlot({
-    n <- date_range_switch(input$kpi_summary_date_range_selector, 1, 7, 30, 90)
-    api_latest <- cbind("Full-text via API" = 
polloi::safe_tail(split_dataset$cirrus, n)$events,
-                        "Geo Search" = polloi::safe_tail(split_dataset$geo, 
n)$events,
-                        "OpenSearch" = polloi::safe_tail(split_dataset$open, 
n)$events,
-                        "Language" = polloi::safe_tail(split_dataset$language, 
n)$events,
-                        "Prefix" = polloi::safe_tail(split_dataset$prefix, 
n)$events) %>%
+    start_date <- Sys.Date() - switch(input$kpi_summary_date_range_selector,
+                                      daily = 1, weekly = 8, monthly = 31, 
quarterly = 91)
+    api_latest <- cbind("Full-text via API" = 
polloi::subset_by_date_range(split_dataset$cirrus, from = start_date, to = 
Sys.Date() - 1)$events,
+                        "Geo Search" = 
polloi::subset_by_date_range(split_dataset$geo, from = start_date, to = 
Sys.Date() - 1)$events,
+                        "OpenSearch" = 
polloi::subset_by_date_range(split_dataset$open, from = start_date, to = 
Sys.Date() - 1)$events,
+                        "Language" = 
polloi::subset_by_date_range(split_dataset$language, from = start_date, to = 
Sys.Date() - 1)$events,
+                        "Prefix" = 
polloi::subset_by_date_range(split_dataset$prefix, from = start_date, to = 
Sys.Date() - 1)$events) %>%
       apply(2, median) %>% round
     api_latest <- data.frame(API = names(api_latest),
                              Events = api_latest,
@@ -392,27 +410,28 @@
   ## KPI Modules
   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))
+    date_range <- input$kpi_summary_date_range_selector
+    start_date <- Sys.Date() - switch(input$kpi_summary_date_range_selector,
+                                      daily = 1, weekly = 8, monthly = 31, 
quarterly = 91)
     load_times <- list(desktop_load_data, mobile_load_data, android_load_data, 
ios_load_data) %>%
-      lapply(polloi::safe_tail, num_of_days_in_common) %>%
-      lapply(function(data_tail) return(data_tail$Median)) %>%
-      as.data.frame %>%
-      { colnames(.) <- c("Desktop", "Mobile Web", "Android", "iOS"); . } %>%
+      lapply(polloi::subset_by_date_range, from = start_date, to = Sys.Date() 
- 1) %>%
+      lapply(function(data_tail) return(data_tail[, c('date', 'Median')])) %>%
+      { names(.) <- c("Desktop", "Mobile Web", "Android", "iOS"); . } %>%
+      dplyr::bind_rows(.id = "Platform") %>%
+      unique %>%
+      tidyr::spread('Platform', 'Median')
+    missing_values <- any(is.na(load_times))
+    load_times %<>%
       {
-        Median = apply(., 1, median)
-        cbind(Median = Median, .)
+        Median = apply(.[, -1], 1, median, na.rm = TRUE)
+        cbind(., Median = Median)
       } %>%
-      cbind(date = polloi::safe_tail(desktop_load_data, 
num_of_days_in_common)$date, .) %>%
       polloi::smoother(smooth_level = ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level), rename = FALSE) %>%
       { xts::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") %>%
+    return(dygraph(load_times, xlab = "Date", ylab = "Load time (ms)",
+                   main = ifelse(missing_values, "Approximate load times over 
time", "Load times over time")) %>%
+             dySeries("Median", axis = 'y', strokeWidth = 4, label = 
"Cross-platform Median") %>%
+             dyLegend(width = 500, show = "always", labelsDiv = 
"kpi_load_time_series_legend") %>%
              dyOptions(strokeWidth = 2, colors = RColorBrewer::brewer.pal(5, 
"Set2")[5:1],
                        drawPoints = FALSE, pointSize = 3, labelsKMB = TRUE,
                        includeZero = TRUE) %>%
@@ -420,20 +439,21 @@
   })
   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 <- data.frame(date = failure_dygraph_set$date[-1], rate = zrr[-1], 
change = zrr_change)
-    zrr %<>% polloi::smoother(ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level), rename = FALSE)
+    start_date <- Sys.Date() - switch(input$kpi_summary_date_range_selector, 
daily = 1, weekly = 8, monthly = 31, quarterly = 91)
+    zrr <- failure_dygraph_set %>%
+      polloi::subset_by_date_range(from = start_date, to = Sys.Date()) %>%
+      transform(`Rate` = 100 * `Zero Result Queries` / `Search Queries`)
+    zrr_change <- 100 * (zrr$Rate[2:nrow(zrr)] - 
zrr$Rate[1:(nrow(zrr)-1)])/zrr$Rate[1:(nrow(zrr)-1)]
+    zrr <- cbind(zrr[, c('date', 'Rate')], Change = c(NA, zrr_change)) %>%
+      polloi::smoother(ifelse(smooth_level == "global", 
input$smoothing_global, smooth_level), rename = FALSE)
     zrr <- xts::xts(zrr[, -1], zrr[, 1])
-    return(dygraph(zrr,
-                   main = "Zero results rate over time",
-                   xlab = "Date",
+    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) %>%
+             dySeries("Change", axis = 'y2', label = "Day-to-day % change", 
strokeWidth = 1) %>%
              dyLimit(limit = 12.50, label = "Goal: 12.50% zero results rate",
                      color = RColorBrewer::brewer.pal(3, "Set2")[3]) %>%
              dyAxis("y2", label = "Day-to-day % change",
-                    valueRange = c(-1, 1) * 
max(max(abs(as.numeric(zrr$change))), 10),
+                    valueRange = c(-1, 1) * 
max(max(abs(as.numeric(zrr$Change)), na.rm = TRUE), 10),
                     axisLineColor = RColorBrewer::brewer.pal(3, "Set2")[2],
                     axisLabelColor = RColorBrewer::brewer.pal(3, "Set2")[2],
                     independentTicks = TRUE, drawGrid = FALSE) %>%
@@ -449,7 +469,12 @@
   })
   output$kpi_api_usage_series <- renderDygraph({
     smooth_level <- input$smoothing_kpi_api_usage
-    api_usage <- cbind(date = split_dataset$cirrus$date, 
as.data.frame(lapply(split_dataset, function(x) x$events)))
+    start_date <- Sys.Date() - switch(input$kpi_summary_date_range_selector, 
daily = 1, weekly = 8, monthly = 31, quarterly = 91)
+    api_usage <- split_dataset %>%
+      lapply(polloi::subset_by_date_range, from = start_date, to = Sys.Date() 
- 1) %>%
+      dplyr::bind_rows() %>%
+      tidyr::spread("event_type", "events") %>%
+      as.data.frame
     if ( input$kpi_api_usage_series_include_open ) {
       api_usage <- transform(api_usage, all = cirrus + geo + language + open + 
prefix)
     } else {
@@ -493,8 +518,10 @@
              dyCSS(css = system.file("custom.css", package = "polloi")))
   })
   output$kpi_augmented_clickthroughs_series <- renderDygraph({
-    smoothed_data <- polloi::smoother(augmented_clickthroughs,
-      smooth_level = polloi::smooth_switch(input$smoothing_global, 
input$smoothing_augmented_clickthroughs))
+    start_date <- Sys.Date() - switch(input$kpi_summary_date_range_selector, 
daily = 1, weekly = 8, monthly = 31, quarterly = 91)
+    smoothed_data <- augmented_clickthroughs %>%
+      polloi::subset_by_date_range(from = start_date, to = Sys.Date() - 1) %>%
+      polloi::smoother(smooth_level = 
polloi::smooth_switch(input$smoothing_global, 
input$smoothing_augmented_clickthroughs))
     polloi::make_dygraph(data = smoothed_data, xlab = "Date", ylab = "Rates", 
"User engagement (augmented clickthroughs) by day") %>%
       dySeries(name = colnames(smoothed_data)[2], strokeWidth = 1.5, 
strokePattern = "dashed") %>%
       dySeries(name = colnames(smoothed_data)[3], strokeWidth = 1.5, 
strokePattern = "dashed") %>%
diff --git a/tab_documentation/kpis_summary.md 
b/tab_documentation/kpis_summary.md
index 24dada2..4bec1fd 100644
--- a/tab_documentation/kpis_summary.md
+++ b/tab_documentation/kpis_summary.md
@@ -6,10 +6,10 @@
 - **API usage** We want people, both within our movement and outside it, to be 
able to easily access our information.
 - **User Engagement** (not quite **[User 
Satisfaction](https://meta.wikimedia.org/wiki/Research:Measuring_User_Search_Satisfaction)**)
 This is an augmented version of clickthrough rate. In it we are including the 
proportion of users' sessions exceeding a pre-specified threshold. **Note** 
that we deployed v2.0 of the satisfaction schema on 9/2/2015. You may see 
**NA** if we do not have enough data available at the time.
 
-Outages and inaccuracies
+Additional information
 ------
 
-* None so far!
+In the case of a data outage, the medians will be computed from non-missing 
data. When this is the case, the value displayed will be approximate and will 
have a '~'.
 
 Questions, bug reports, and feature suggestions
 ------
diff --git a/ui.R b/ui.R
index 2ee9956..6071b7d 100644
--- a/ui.R
+++ b/ui.R
@@ -6,6 +6,24 @@
 #Header elements for the visualisation
 header <- dashboardHeader(title = "Search Metrics", disable = FALSE)
 
+# 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", 
"Weekly Median" = "week", "Monthly Median" = "month")))
+}
+# Standardized selectors for time frame
+timeframe_select <- function(input_id, label = "Time Frame") {
+  return(selectInput(inputId = input_id, label = label, selectize = TRUE, 
selected = "global",
+                     choices = c("Use Global Setting" = "global", "Last 7 
days" = "week",
+                                 "Last 30 days" = "month", "Last 90 days" = 
"quarter", "Custom" = "custom")))
+}
+timeframe_daterange <- function(select_input_id, label = "Custom Date Range") {
+  return(conditionalPanel(paste0("input.", select_input_id," == 'custom'"),
+                          dateRangeInput(paste(select_input_id, "daterange", 
sep = "_"), label = label,
+                                         start = Sys.Date()-11, end = 
Sys.Date()-1, min = "2015-04-14")))
+}
+
 #Sidebar elements for the search visualisations.
 sidebar <- dashboardSidebar(
   tags$head(
@@ -14,11 +32,17 @@
   ),
   sidebarMenu(
     menuItem(text = "KPIs",
+             div(selectInput("kpi_summary_date_range_selector",
+                             label = "KPI data range", multiple = FALSE, 
selected = "weekly",
+                             choices = list("Yesterday" = "daily", "Last 7 
days" = "weekly",
+                                            "Last 30 days" = "monthly", "Last 
90 days" = "quarterly")),
+                 style = "margin-bottom:-10px;"),
              menuSubItem(text = "Summary", tabName = "kpis_summary"),
              menuSubItem(text = "Load times", tabName = "kpi_load_time"),
              menuSubItem(text = "Zero results", tabName = "kpi_zero_results"),
              menuSubItem(text = "API usage", tabName = "kpi_api_usage"),
-             menuSubItem(text = "Augmented Clickthrough", tabName = 
"kpi_augmented_clickthroughs")),
+             menuSubItem(text = "Augmented Clickthrough", tabName = 
"kpi_augmented_clickthroughs"),
+             icon = icon("star", lib = "glyphicon")),
     menuItem(text = "Desktop",
              menuSubItem(text = "Events", tabName = "desktop_events"),
              menuSubItem(text = "Load times", tabName = "desktop_load")),
@@ -38,37 +62,36 @@
              menuSubItem(text = "Summary", tabName = "failure_rate"),
              menuSubItem(text = "Search Type Breakdown", tabName = 
"failure_breakdown"),
              menuSubItem(text = "Search Suggestions", tabName = 
"failure_suggestions")),
-    menuItem(text = "Page Visit Times", tabName = "survival",
-             badgeLabel = "new", badgeColor = "fuchsia"),
-    selectInput(inputId = "smoothing_global", label = "Smoothing (Global 
Setting)", selectize = TRUE, selected = "day",
-                choices = c("No Smoothing" = "day", "Weekly Median" = "week", 
"Monthly Median" = "month"))
+    menuItem(text = "Page Visit Times", tabName = "survival"),
+    menuItem(text = "Global Settings",
+             selectInput(inputId = "smoothing_global", label = "Smoothing", 
selectize = TRUE, selected = "day",
+                         choices = c("No Smoothing" = "day", "Weekly Median" = 
"week", "Monthly Median" = "month")),
+             selectInput(inputId = "timeframe_global", label = "Time Frame", 
selectize = TRUE, selected = "",
+                         choices = c("All available data" = "all", "Last 7 
days" = "week", "Last 30 days" = "month",
+                                     "Last 90 days" = "quarter", "Custom" = 
"custom")),
+             conditionalPanel("input.timeframe_global == 'custom'",
+                              dateRangeInput("daterange_global", label = 
"Custom Date Range",
+                                             start = Sys.Date()-11, end = 
Sys.Date()-1, min = "2015-04-14")),
+             icon = icon("cog", lib = "glyphicon"))
   )
 )
-
-# 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", "Weekly Median" = "week", 
"Monthly Median" = "month")))
-}
 
 #Body elements for the search visualisations.
 body <- dashboardBody(
   tabItems(
     tabItem(tabName = "kpis_summary",
-            selectInput("kpi_summary_date_range_selector", label = "Data 
range", multiple = FALSE, selected = "weekly",
-                        choices = list("Yesterday" = "daily", "Last 7 days" = 
"weekly",
-                                       "Last 30 days" = "monthly", "Last 90 
days" = "quarterly")),
             htmlOutput("kpi_summary_date_range"),
             fluidRow(valueBoxOutput("kpi_summary_box_load_time", width = 3),
                      valueBoxOutput("kpi_summary_box_zero_results", width = 3),
                      valueBoxOutput("kpi_summary_box_api_usage", width = 3),
                      valueBoxOutput("kpi_summary_box_augmented_clickthroughs", 
width = 3)),
             plotOutput("kpi_summary_api_usage_proportions", height = "30px"),
-            includeMarkdown("./tab_documentation/kpis_summary.md")
-            ),
+            includeMarkdown("./tab_documentation/kpis_summary.md")),
     tabItem(tabName = "kpi_load_time",
-            smooth_select("smoothing_kpi_load_time"),
+            fluidRow(
+              column(smooth_select("smoothing_kpi_load_time"), width = 4),
+              column(div(id = "kpi_load_time_series_legend"), width = 8)
+            ),
             dygraphOutput("kpi_load_time_series"),
             includeMarkdown("./tab_documentation/kpi_load_time.md")),
     tabItem(tabName = "kpi_zero_results",
@@ -103,27 +126,37 @@
             fluidRow(
               valueBoxOutput("desktop_event_searches"),
               valueBoxOutput("desktop_event_resultsets"),
-              valueBoxOutput("desktop_event_clickthroughs")
-            ),
-            smooth_select("smoothing_desktop_event"),
+              valueBoxOutput("desktop_event_clickthroughs")),
+            fluidRow(
+              column(smooth_select("smoothing_desktop_event"), width = 4),
+              column(timeframe_select("desktop_event_timeframe"), width = 4),
+              column(timeframe_daterange("desktop_event_timeframe"), width = 
4)),
             dygraphOutput("desktop_event_plot"),
             includeMarkdown("./tab_documentation/desktop_events.md")),
     tabItem(tabName = "desktop_load",
-            smooth_select("smoothing_desktop_load"),
+            fluidRow(
+              column(smooth_select("smoothing_desktop_load"), width = 4),
+              column(timeframe_select("desktop_load_timeframe"), width = 4),
+              column(timeframe_daterange("desktop_load_timeframe"), width = 
4)),
             dygraphOutput("desktop_load_plot"),
             includeMarkdown("./tab_documentation/desktop_load.md")),
     tabItem(tabName = "mobile_events",
             fluidRow(
               valueBoxOutput("mobile_event_searches"),
               valueBoxOutput("mobile_event_resultsets"),
-              valueBoxOutput("mobile_event_clickthroughs")
-            ),
-            smooth_select("smoothing_mobile_event"),
+              valueBoxOutput("mobile_event_clickthroughs")),
+            fluidRow(
+              column(smooth_select("smoothing_mobile_event"), width = 4),
+              column(timeframe_select("mobile_event_timeframe"), width = 4),
+              column(timeframe_daterange("mobile_event_timeframe"), width = 
4)),
             dygraphOutput("mobile_event_plot"),
             includeMarkdown("./tab_documentation/mobile_events.md")
     ),
     tabItem(tabName = "mobile_load",
-            smooth_select("smoothing_mobile_load"),
+            fluidRow(
+              column(smooth_select("smoothing_mobile_load"), width = 4),
+              column(timeframe_select("mobile_load_timeframe"), width = 4),
+              column(timeframe_daterange("mobile_load_timeframe"), width = 4)),
             dygraphOutput("mobile_load_plot"),
             includeMarkdown("./tab_documentation/mobile_load.md")
     ),
@@ -131,65 +164,95 @@
             fluidRow(
               valueBoxOutput("app_event_searches"),
               valueBoxOutput("app_event_resultsets"),
-              valueBoxOutput("app_event_clickthroughs")
-            ),
-            smooth_select("smoothing_app_event"),
+              valueBoxOutput("app_event_clickthroughs")),
+            fluidRow(
+              column(smooth_select("smoothing_app_event"), width = 4),
+              column(timeframe_select("app_event_timeframe"), width = 4),
+              column(timeframe_daterange("app_event_timeframe"), width = 4)),
             dygraphOutput("android_event_plot"),
             dygraphOutput("ios_event_plot"),
             includeMarkdown("./tab_documentation/app_events.md")
     ),
     tabItem(tabName = "app_load",
-            smooth_select("smoothing_app_load"),
+            fluidRow(
+              column(smooth_select("smoothing_app_load"), width = 4),
+              column(timeframe_select("app_load_timeframe"), width = 4),
+              column(timeframe_daterange("app_load_timeframe"), width = 4)),
             dygraphOutput("android_load_plot"),
             dygraphOutput("ios_load_plot"),
             includeMarkdown("./tab_documentation/app_load.md")
     ),
     tabItem(tabName = "fulltext_search",
-            smooth_select("smoothing_fulltext_search"),
+            fluidRow(
+              column(smooth_select("smoothing_fulltext_search"), width = 4),
+              column(timeframe_select("fulltext_search_timeframe"), width = 4),
+              column(timeframe_daterange("fulltext_search_timeframe"), width = 
4)),
             dygraphOutput("cirrus_aggregate"),
             includeMarkdown("./tab_documentation/fulltext_basic.md")
     ),
     tabItem(tabName = "open_search",
-            smooth_select("smoothing_open_search"),
+            fluidRow(
+              column(smooth_select("smoothing_open_search"), width = 4),
+              column(timeframe_select("open_search_timeframe"), width = 4),
+              column(timeframe_daterange("open_search_timeframe"), width = 4)),
             dygraphOutput("open_aggregate"),
             includeMarkdown("./tab_documentation/open_basic.md")
     ),
     tabItem(tabName = "geo_search",
-            smooth_select("smoothing_geo_search"),
+            fluidRow(
+              column(smooth_select("smoothing_geo_search"), width = 4),
+              column(timeframe_select("geo_search_timeframe"), width = 4),
+              column(timeframe_daterange("geo_search_timeframe"), width = 4)),
             dygraphOutput("geo_aggregate"),
             includeMarkdown("./tab_documentation/geo_basic.md")
     ),
     tabItem(tabName = "prefix_search",
-            smooth_select("smoothing_prefix_search"),
+            fluidRow(
+              column(smooth_select("smoothing_prefix_search"), width = 4),
+              column(timeframe_select("prefix_search_timeframe"), width = 4),
+              column(timeframe_daterange("prefix_search_timeframe"), width = 
4)),
             dygraphOutput("prefix_aggregate"),
             includeMarkdown("./tab_documentation/prefix_basic.md")
     ),
     tabItem(tabName = "language_search",
-            smooth_select("smoothing_language_search"),
+            fluidRow(
+              column(smooth_select("smoothing_language_search"), width = 4),
+              column(timeframe_select("language_search_timeframe"), width = 4),
+              column(timeframe_daterange("language_search_timeframe"), width = 
4)),
             dygraphOutput("language_aggregate"),
             includeMarkdown("./tab_documentation/language_basic.md")
     ),
     tabItem(tabName = "failure_rate",
-            smooth_select("smoothing_failure_rate"),
+            fluidRow(
+              column(smooth_select("smoothing_failure_rate"), width = 4),
+              column(timeframe_select("failure_rate_timeframe"), width = 4),
+              column(timeframe_daterange("failure_rate_timeframe"), width = 
4)),
             dygraphOutput("failure_rate_plot"),
             dygraphOutput("failure_rate_change_plot"),
             includeMarkdown("./tab_documentation/failure_rate.md")
     ),
     tabItem(tabName = "failure_breakdown",
-            smooth_select("smoothing_failure_breakdown"),
+            fluidRow(
+              column(smooth_select("smoothing_failure_breakdown"), width = 4),
+              column(timeframe_select("failure_breakdown_timeframe"), width = 
4),
+              column(timeframe_daterange("failure_breakdown_timeframe"), width 
= 4)),
             dygraphOutput("failure_breakdown_plot"),
             includeMarkdown("./tab_documentation/failure_breakdown.md")
     ),
     tabItem(tabName = "failure_suggestions",
-            smooth_select("smoothing_failure_suggestions"),
+            fluidRow(
+              column(smooth_select("smoothing_failure_suggestions"), width = 
4),
+              column(timeframe_select("failure_suggestions_timeframe"), width 
= 4),
+              column(timeframe_daterange("failure_suggestions_timeframe"), 
width = 4)),
             dygraphOutput("suggestion_dygraph_plot"),
             includeMarkdown("./tab_documentation/failure_suggests.md")
     ),
     tabItem(tabName = "survival",
             fluidRow(
               column(smooth_select("smoothing_lethal_dose_plot"), width = 4),
-              column(div(id = "lethal_dose_plot_legend"), width = 8)
-            ),
+              column(timeframe_select("lethal_dose_timeframe"), width = 4),
+              column(timeframe_daterange("lethal_dose_timeframe"), width = 4)),
+            div(id = "lethal_dose_plot_legend"),
             dygraphOutput("lethal_dose_plot"),
             includeMarkdown("./tab_documentation/survival.md")
     )
diff --git a/utils.R b/utils.R
index f6535f0..4393a69 100644
--- a/utils.R
+++ b/utils.R
@@ -128,8 +128,22 @@
                   x = 1))
 }
 
-date_range_switch <- function(date_range, daily = 2, weekly = 14, monthly = 
60, quarterly = 90) {
-  return(switch(date_range, daily = daily, weekly = weekly, monthly = monthly, 
quarterly = quarterly))
+# This is used in subsetting data for KPIs.
+start_date <- function(date_range) {
+  return(Sys.Date() - (switch(date_range, daily = 2, weekly = 14, monthly = 
60, quarterly = 90) + 1))
+}
+
+# Usage: date_range %>% name(c('start', 'end'))
+name <- function(x, labels) {
+  names(x) <- labels
+  return(x)
+}
+
+# From: 
http://r.789695.n4.nabble.com/How-to-join-matrices-of-different-row-length-from-a-list-td3177212.html
+cbind_fill <- function(...) {
+  nm <- lapply(list(...), as.matrix)
+  n <- max(sapply(nm, nrow))
+  do.call(cbind, lapply(nm, function (x) rbind(x, matrix(, n-nrow(x), 
ncol(x)))))
 }
 
 CustomAxisFormatter <- 'function (d, gran) {

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I72a90a00235c536da35a893e03a68f9a23caa62d
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