Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First iteration of the dashboard (version 2) #46

Open
wants to merge 1 commit into
base: dashboard-v2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 5 additions & 16 deletions data_updater.R
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,16 @@ curl::curl_download("https://raw.githubusercontent.com/ccodwg/CovidTimelineCanad
curl::curl_download("https://raw.githubusercontent.com/ccodwg/CovidTimelineCanada/main/geo/health_regions.geojson", "data/health_regions.geojson")

# health region data
for (d in c(
"cases",
"deaths"
)) {
curl::curl_download(paste0("https://github.com/ccodwg/CovidTimelineCanada/raw/main/data/hr/", d, "_hr.csv"), paste0("data/", d, "_hr.csv"))
}
curl::curl_download(paste0("https://github.com/ccodwg/CovidTimelineCanada/raw/main/data/hr/deaths_hr.csv"), paste0("data/deaths_hr.csv"))

# province/territory data
for (d in c(
"cases",
"deaths",
"tests_completed",
"vaccine_coverage_dose_1",
"vaccine_coverage_dose_2",
"vaccine_coverage_dose_3",
"vaccine_coverage_dose_4"
"hospitalizations",
"icu"
)) {
curl::curl_download(paste0("https://github.com/ccodwg/CovidTimelineCanada/raw/main/data/pt/", d, "_pt.csv"), paste0("data/", d, "_pt.csv"))
}

# metadata
for (d in c("cases", "deaths")) {
curl::curl_download(paste0("https://github.com/ccodwg/CovidTimelineCanada/raw/main/data/can/", d, "_can_completeness.json"), paste0("data/", d, "_can_completeness.json"))
}
# wastewater data
curl::curl_download("https://raw.githubusercontent.com/ccodwg/CovidTimelineCanada/main/raw_data/active_ts/can/can_wastewater_copies_per_ml_subhr_ts.csv","data/wastewater_data.csv")
180 changes: 162 additions & 18 deletions global.R
Original file line number Diff line number Diff line change
Expand Up @@ -58,24 +58,168 @@ for (f in files) {
}
}

# set constants

## PT colour palette
palette_pt <- list(
"AB" = "#00E676",
"BC" = "#304FFE",
"MB" = "#FF80AB",
"NB" = "#76FF03",
"NL" = "#B388FF",
"NS" = "#00E5FF",
"NT" = "#00ACC1",
"NU" = "#FFFF00",
"ON" = "#FF6F00",
"PE" = "#1B5E20",
"QC" = "#D50000",
"SK" = "#AA00FF",
"YT" = "#D500F9"
)
### FINDS start state for this calendar year (simplified function of MMWRweek package)
### Start of week is Monday

start_week_calc <- function(week, year){
begin_date <- function(year) {
jan1 <- as.Date(paste0(year, "-01-01"))
wday <- as.numeric(lubridate::wday(jan1, week_start = 1))
date <- jan1 - (wday-1) + 7*(wday>4)
return(date)
}
jan1 <- begin_date(year)
return(jan1 + (week-1)*7)
}

##### ----- FUNCTION to process data: PROVINCE/ TERRITORIES
### Variable names are standardized but include option to identify different value (cumulative vs daily)
### TYPE == type of data: "mean" (wastewater); or "total" (mortality, hospitalizations)
### ALSO ADDS week 53 to week 52 data (and if week 53 is NAN, then == 0 for addition)

process_pt_data <- function(data, val, type){

names(data)[names(data) == val] <- "val"

if(type == "mean"){

data <- data %>%
dplyr::mutate(year = lubridate::year(date), week = lubridate::week(date)) %>%
dplyr::group_by(region, year, week) %>% dplyr::summarise(mean = mean(val, na.rm = T), .groups = "keep")

data <- data %>% dplyr::rename(val = mean) %>%
dplyr::mutate(date = start_week_calc(week, year))

}

if(type == "total"){

data <- data %>%
dplyr::mutate(year = lubridate::year(date), week = lubridate::week(date)) %>%
dplyr::group_by(region, year, week) %>% dplyr::summarise(total = sum(val, na.rm = T), .groups = "keep")

data <- data %>% dplyr::rename(val = total) %>%
dplyr::mutate(date = start_week_calc(week, year))
}

### Separates out week 53 so it can be added to week 52
w53 <- data[(data$week==53),]
w53$week <- 52
w53 <- w53[c("week","year","region","val")]
colnames(w53) <- c("week","year","region","val_add")

data <- data[(data$week!=53),]
data <- merge(data,w53, by = c("week","year","region"), all.x = T)
data$val <- rowSums(data[c("val","val_add")], na.rm = T)

data <- data[c("week","year","date","region","val")]
names(data)[names(data) == "val"] <- val

### ADD AREAS - FACTOR ACCORDINGLY

data$area <- NA
data$area[data$region=="NL" | data$region=="NS" | data$region=="PE" | data$region=="NB"] <- "Atlantic Canada"
data$area[data$region=="ON" | data$region=="QC"] <- "Central Canada"
data$area[data$region=="MB" | data$region=="AB" | data$region=="SK" | data$region=="BC"] <- "Western / Prairie Provinces"
data$area[data$region=="NT" | data$region=="NU" | data$region=="YT"] <- "Northern Canada"

return(data)

}


process_hr_data <- function(data, hr, pt, val, type){

names(data)[names(data) == val] <- "val"
names(data)[names(data) == hr] <- "hr"
names(data)[names(data) == pt] <- "pt"

areas <- data[c("hr","pt")]
areas$unique <- duplicated(areas$hr)
areas <- areas[(areas$unique==FALSE),]

if(type == "mean"){

data <- data %>%
dplyr::mutate(year = lubridate::year(date), week = lubridate::week(date)) %>%
dplyr::group_by(hr, year, week) %>% dplyr::summarise(mean = mean(val, na.rm = T), .groups = "keep")

data <- data %>% dplyr::rename(val = mean) %>%
dplyr::mutate(date = start_week_calc(week, year))

}

if(type == "total"){

data <- data %>%
dplyr::mutate(year = lubridate::year(date), week = lubridate::week(date)) %>%
dplyr::group_by(hr, year, week) %>% dplyr::summarise(total = sum(val, na.rm = T), .groups = "keep")

data <- data %>% dplyr::rename(val = total) %>%
dplyr::mutate(date = start_week_calc(week, year))

}

### Separates out week 53 so it can be added to week 52
w53 <- data[(data$week==53),]
w53$week <- 52
w53 <- w53[c("week","year","hr","val")]
colnames(w53) <- c("week","year","hr","val_add")

data <- data[(data$week!=53),]
data <- merge(data,w53, by = c("week","year","hr"), all.x = T)
data$val <- rowSums(data[c("val","val_add")], na.rm = T)

data <- data[c("week","year","date","hr","val")]
names(data)[names(data) == "val"] <- val

### ADD AREAS - FACTOR ACCORDINGLY

areas$area <- NA
areas$area[areas$pt=="NL" | areas$pt=="NS" | areas$pt=="PE" | areas$pt=="NB"] <- "Atlantic Canada"
areas$area[areas$pt=="ON" | areas$pt=="QC"] <- "Central Canada"
areas$area[areas$pt=="MB" | areas$pt=="AB" | areas$pt=="SK" | areas$pt=="BC"] <- "Western / Prairie Provinces"
areas$area[areas$pt=="NT" | areas$pt=="NU" | areas$pt=="YT"] <- "Northern Canada"

atlantic_can <- areas$hr[areas$area=="Atlantic Canada"]
central_can <- areas$hr[areas$area=="Central Canada"]
western_can <- areas$hr[areas$area=="Western / Prairie Provinces"]
northern_can <- areas$hr[areas$area=="Northern Canada"]

data$area[data$hr %in% atlantic_can] <- "Atlantic Canada"
data$area[data$hr %in% central_can] <- "Central Canada"
data$area[data$hr %in% western_can] <- "Western / Prairie Provinces"
data$area[data$hr %in% northern_can] <- "Northern Canada"

names(data)[names(data) == "hr"] <- "region" # rename to region for simplicity

return(data)

}

##### ----- WASTEWATER DATA

#!!!# For now, fix missing regional data:
wastewater_data$region[wastewater_data$sub_region_1=="Saskatoon"] <- "SK"
wastewater_data$region[wastewater_data$sub_region_1=="Prince Albert"] <- "SK"
wastewater_data$region[wastewater_data$sub_region_1=="North Battleford"] <- "SK"
wastewater_data$region[wastewater_data$sub_region_1=="Moncton"] <- "NB"

wastewater_pt <- process_pt_data(wastewater_data,"value","mean")
wastewater_hr <- process_hr_data(wastewater_data,"sub_region_1","region","value","mean")

wastewater_pt$area <- factor(wastewater_pt$area, levels = c("Western / Prairie Provinces","Central Canada","Atlantic Canada"))
wastewater_hr$area <- factor(wastewater_hr$area, levels = c("Western / Prairie Provinces","Central Canada","Atlantic Canada"))

##### ----- HOSPITALIZATION DATA

hospitalization_data <- process_pt_data(hospitalizations_pt,"value","total")
hospitalization_data$area <- factor(hospitalization_data$area, levels = c("Western / Prairie Provinces","Central Canada","Atlantic Canada","Northern Canada"))

##### ----- MORTALITY DATA

mortality_data <- process_pt_data(deaths_pt,"value_daily","total")
mortality_data$area <- factor(mortality_data$area, levels = c("Western / Prairie Provinces","Central Canada","Atlantic Canada","Northern Canada"))

# load analytics (if available)
analytics <- if (file.exists("google-analytics.html")) {
Expand Down
115 changes: 87 additions & 28 deletions server.R
Original file line number Diff line number Diff line change
@@ -1,33 +1,5 @@
server <- function(input, output, session) {

# JavaScript function to format number with a thousands separator
format_thousands <- apexcharter::JS(
"function(value) {if (typeof(value) == 'number') {return value.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\");} else {return value;}}")

# render Apex chart of PT timeseries for "value" or "value_daily"
ts_chart_pt <- function(
dat, val = c("value", "value_daily"), xlab, ylab, remove_negative_values = FALSE) {
match.arg(val, c("value", "value_daily"), several.ok = FALSE)
if (remove_negative_values) {dat[!is.na(dat[val]) & dat[val] < 0, val] <- NA}
apexcharter::renderApexchart(
apexcharter::apex(dat, type = "line", mapping = apexcharter::aes(
x = date, y = !!rlang::sym(val), colour = region)) %>%
apexcharter::ax_labs(x = xlab, y = ylab) %>%
apexcharter::ax_chart(animations = list(enabled = FALSE)) %>%
apexcharter::ax_yaxis(labels = list(formatter = format_thousands)) %>%
apexcharter::ax_tooltip(y = list(formatter = format_thousands)) %>%
apexcharter::ax_colors_manual(palette_pt)
)
}

# render case time series chart
output$pt_cases_ts <- ts_chart_pt(
dat = cases_pt,
val = "value_daily",
xlab = "Date",
ylab = "Daily confirmed cases",
remove_negative_values = TRUE)

# check if dataset has been updated on GitHub and if so, download it and redeploy the app
shiny::observe({
## run only if app is running on the server, not locally
Expand All @@ -50,10 +22,97 @@ server <- function(input, output, session) {
}
})

##### ----- WASTEWATER PLOT

output$pt_wastewater_ts <- apexcharter::renderApexfacet(wastewater_pt %>%
apexcharter::apex(mapping = apexcharter::aes(date,value,colour = region),type = "area", synchronize = "value") %>%
apexcharter::ax_facet_wrap(apexcharter::vars(area), ncol = 1, scales = "free_y") %>% ## don't facet by province but instead areas
apexcharter::ax_chart(animations = list(enabled = FALSE)) %>%
apexcharter::ax_legend(showForSingleSeries = TRUE) %>%
apexcharter::ax_stroke(lineCap = "round", curve = "smooth") %>%
apexcharter::ax_fill(gradient = list(enabled = TRUE, opacityFrom = 0.9, opacityTo = 0.3)) %>%
apexcharter::ax_xaxis(tooltip = list(enabled = FALSE), title = list(text = ""),
crosshairs = list(
opacity = 0.75,
width = 2,
fill = list(color = "#1C4579"),
stroke = list(width = 0))) %>%
apexcharter::ax_yaxis(labels = list(formatter = apexcharter::JS("function(val) {return val.toFixed(1);}")),
tooltip = list(enabled = FALSE), title = list(text = "Weekly Average of Raw copies per / ml")) %>%
apexcharter::ax_tooltip(y = list(formatter = apexcharter::JS("function(val) {return val.toFixed(1);}")), followCursor = FALSE) %>%
apexcharter::ax_colors_manual(list("AB" = "#5C4742","BC" = "#8D5B4C","MB" = "#948375","SK" = "#5A2A27",
"ON" = "#5C4742","QC" = "#948375",
"NB" = "#5C4742","NL" = "#8D5B4C","NS" = "#948375","PE" = "#5A2A27"))
)

output$hr_wastewater_ts <- apexcharter::renderApexfacet(wastewater_hr %>%
apexcharter::apex(mapping = apexcharter::aes(date,value,colour = region),type = "area", synchronize = "value") %>%
apexcharter::ax_facet_wrap(apexcharter::vars(area), ncol = 1, scales = "free_y") %>% ## don't facet by province but instead areas
apexcharter::ax_chart(animations = list(enabled = FALSE)) %>%
apexcharter::ax_legend(showForSingleSeries = TRUE) %>%
apexcharter::ax_stroke(lineCap = "round", curve = "smooth") %>%
apexcharter::ax_fill(gradient = list(enabled = TRUE, opacityFrom = 0.9, opacityTo = 0.3)) %>%
apexcharter::ax_xaxis(tooltip = list(enabled = FALSE), title = list(text = ""),
crosshairs = list(
opacity = 0.75,
width = 2,
fill = list(color = "#1C4579"),
stroke = list(width = 0))) %>%
apexcharter::ax_yaxis(labels = list(formatter = apexcharter::JS("function(val) {return val.toFixed(1);}")),
tooltip = list(enabled = FALSE), title = list(text = "Weekly Average of Raw copies per / ml")) %>%
apexcharter::ax_tooltip(y = list(formatter = apexcharter::JS("function(val) {return val.toFixed(1);}")), followCursor = FALSE) %>%
apexcharter::ax_theme(palette = "palette9")
)

output$pt_hospitalizations_ts <- apexcharter::renderApexfacet(hospitalization_data %>%
apexcharter::apex(mapping = apexcharter::aes(date,value,colour = region),type = "area", synchronize = "value") %>%
apexcharter::ax_facet_wrap(apexcharter::vars(area), ncol = 1, scales = "free_y") %>%
apexcharter::ax_chart(animations = list(enabled = FALSE)) %>%
apexcharter::ax_legend(showForSingleSeries = TRUE) %>%
apexcharter::ax_stroke(lineCap = "round", curve = "smooth") %>%
apexcharter::ax_fill(gradient = list(enabled = TRUE, opacityFrom = 0.8, opacityTo = 0.2)) %>%
apexcharter::ax_xaxis(tooltip = list(enabled = FALSE), title = list(text = ""),
crosshairs = list(
opacity = 0.75,
width = 2,
fill = list(color = "#1C4579"),
stroke = list(width = 0))) %>%
apexcharter::ax_yaxis(labels = list(formatter = apexcharter::JS("function(val) {return val.toFixed(1);}")),
tooltip = list(enabled = FALSE), title = list(text = "Weekly Total Hospitalizations")) %>%
apexcharter::ax_tooltip(y = list(formatter = apexcharter::JS("function(val) {return val.toFixed(0);}"))) %>%
apexcharter::ax_colors_manual(list("ON" = "#4A62C4","QC" = "#70B3D1",
"AB" = "#4A62C4","BC" = "#0393D2","MB" = "#70B3D1","SK" = "#0327Bf",
"NB" = "#4A62C4","NL" = "#0393D2","NS" = "#70B3D1","PE" = "#0327Bf",
"NT" = "#4A62C4", "NU" = "#70B3D1", "YK" = "#0327Bf"))
)

output$deaths_pt_ts <- apexcharter::renderApexfacet(mortality_data %>%
apexcharter::apex(mapping = apexcharter::aes(date, value_daily, colour = region), type = "area", synchronize = "value") %>%
apexcharter::ax_facet_wrap(apexcharter::vars(area), ncol = 1, scales = "free_y") %>%
apexcharter::ax_chart(animations = list(enabled = FALSE)) %>%
apexcharter::ax_legend(showForSingleSeries = TRUE) %>%
apexcharter::ax_stroke(lineCap = "round", curve = "smooth") %>%
apexcharter::ax_fill(gradient = list(enabled = TRUE, opacityFrom = 0.8, opacityTo = 0.2)) %>%
apexcharter::ax_xaxis(tooltip = list(enabled = FALSE), title = list(text = ""),
crosshairs = list(
opacity = 0.75,
width = 2,
fill = list(color = "#1C4579"),
stroke = list(width = 0))) %>%
apexcharter::ax_yaxis(labels = list(formatter = apexcharter::JS("function(val) {return val.toFixed(1);}")),
tooltip = list(enabled = FALSE), title = list(text = "Weekly Total Deaths")) %>%
apexcharter::ax_tooltip(y = list(formatter = apexcharter::JS("function(val) {return val.toFixed(0);}"))) %>%
apexcharter::ax_colors_manual(list("ON" = "#5D704D","QC" = "#8CA38B",
"AB" = "#5D704D","BC" = "#4E8852","MB" = "#8CA38B","SK" = "#20431D",
"NB" = "#5D704D","NL" = "#4E8852","NS" = "#8CA38B","PE" = "#20431D",
"NT" = "#5D704D", "NU" = "#8CA38B", "YK" = "#20431D"))
)

# when app stops, update file time of app.R
# trick to update cache: https://stackoverflow.com/a/55476883
shiny::onStop(function() {
Sys.setFileTime("app.R", lubridate::now())
})


}
Loading