Skip to content

Commit 5e5066d

Browse files
authored
most used content (table edition) (#31)
* most used content, table edition * update timestamp display * move date formatting to DT * add caching to most used content * Respond to feedback - remove date formatting - remove errant #nolint - add cache clear button
1 parent a53f5f0 commit 5e5066d

File tree

8 files changed

+3874
-0
lines changed

8 files changed

+3874
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
source("renv/activate.R")
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.posit/
2+
app_cache/

extensions/most-used-content/app.R

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
library(shiny)
2+
library(bslib)
3+
library(DT)
4+
library(connectapi)
5+
library(dplyr)
6+
library(purrr)
7+
library(lubridate)
8+
9+
shinyOptions(
10+
cache = cachem::cache_disk("./app_cache/cache/", max_age = 60 * 60 * 8)
11+
)
12+
13+
source("get_usage.R")
14+
15+
ui <- page_fillable(
16+
theme = bs_theme(version = 5),
17+
18+
card(
19+
card_header("Most Used Content"),
20+
layout_sidebar(
21+
sidebar = sidebar(
22+
title = "No Filters Yet",
23+
open = FALSE,
24+
25+
actionButton("clear_cache", "Clear Cache", icon = icon("refresh"))
26+
),
27+
card(
28+
DTOutput(
29+
"content_usage_table"
30+
)
31+
)
32+
)
33+
)
34+
)
35+
36+
server <- function(input, output, session) {
37+
# Cache invalidation button ----
38+
cache <- cachem::cache_disk("./app_cache/cache/")
39+
observeEvent(input$clear_cache, {
40+
print("Cache cleared!")
41+
cache$reset() # Clears all cached data
42+
session$reload() # Reload the app to ensure fresh data
43+
})
44+
45+
# Loading and processing data ----
46+
client <- connect()
47+
48+
# Default dates. "This week" is best "common sense" best represented by six
49+
# days ago thru the end of today. Without these, content takes too long to
50+
# display on some servers.
51+
date_range <- reactive({
52+
list(
53+
from_date = today() - ddays(6),
54+
to_date = today()
55+
)
56+
})
57+
58+
content <- reactive({
59+
get_content(client)
60+
}) |> bindCache("static_key")
61+
62+
usage_data <- reactive({
63+
get_usage(
64+
client,
65+
from = date_range()$from_date,
66+
to = date_range()$to_date + hours(23) + minutes(59) + seconds(59)
67+
)
68+
}) |> bindCache(date_range()$from_date, date_range()$to_date)
69+
70+
# Compute basic usage stats
71+
content_usage_data <- reactive({
72+
usage_summary <- usage_data() |>
73+
group_by(content_guid) |>
74+
summarize(
75+
total_views = n(),
76+
unique_viewers = n_distinct(user_guid, na.rm = TRUE),
77+
last_viewed_at = max(timestamp, na.rm = TRUE)
78+
)
79+
80+
content() |>
81+
mutate(owner_username = map_chr(owner, "username")) |>
82+
select(title, content_guid = guid, owner_username) |>
83+
right_join(usage_summary, by = "content_guid") |>
84+
arrange(desc(total_views))
85+
}) |> bindCache(date_range()$from_date, date_range()$to_date)
86+
87+
output$content_usage_table <- renderDT({
88+
datatable(
89+
content_usage_data(),
90+
options = list(
91+
order = list(list(4, "desc")),
92+
paging = FALSE
93+
),
94+
colnames = c(
95+
"Content Title" = "title",
96+
"Content GUID" = "content_guid",
97+
"Owner Username" = "owner_username",
98+
"Total Views" = "total_views",
99+
"Unique Logged-in Viewers" = "unique_viewers",
100+
"Last Viewed At" = "last_viewed_at"
101+
)
102+
) |>
103+
formatDate(columns = "Last Viewed At", method = "toLocaleString")
104+
105+
})
106+
}
107+
108+
shinyApp(ui, server)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
library(connectapi)
2+
3+
# This file contains functions that ultimately will more likely be part of
4+
# connectapi. As such, I'm not using dplyr or pipes here.
5+
6+
NA_datetime_ <- vctrs::new_datetime(NA_real_, tzone = "UTC")
7+
NA_list_ <- list(list())
8+
9+
usage_dtype <- tibble::tibble(
10+
"id" = NA_integer_,
11+
"user_guid" = NA_character_,
12+
"content_guid" = NA_character_,
13+
"timestamp" = NA_datetime_,
14+
"data" = NA_list_
15+
)
16+
17+
# A rough implementation of how a new firehose usage function would work in
18+
# `connectapi`.
19+
get_usage_firehose <- function(client, from = NULL, to = NULL) {
20+
usage_raw <- client$GET(
21+
connectapi:::unversioned_url("instrumentation", "content", "hits"),
22+
query = list(
23+
from = from,
24+
to = to
25+
)
26+
)
27+
28+
# FIXME for connectapi: This is slow, it's where most of the slowness is with
29+
# the new endpoint.
30+
usage_parsed <- connectapi:::parse_connectapi_typed(usage_raw, usage_dtype)
31+
32+
usage_parsed[c("user_guid", "content_guid", "timestamp")]
33+
}
34+
35+
get_usage_legacy <- function(client, from = NULL, to = NULL) {
36+
shiny_usage <- get_usage_shiny(client, limit = Inf, from = from, to = to)
37+
shiny_usage_cols <- shiny_usage[c("user_guid", "content_guid")]
38+
shiny_usage_cols$timestamp <- shiny_usage$started
39+
40+
static_usage <- get_usage_static(client, limit = Inf, from = from, to = to)
41+
static_usage_cols <- static_usage[c("user_guid", "content_guid")]
42+
static_usage_cols$timestamp <- static_usage$time
43+
44+
bind_rows(shiny_usage_cols, static_usage_cols)
45+
}
46+
47+
get_usage <- function(client, from = NULL, to = NULL) {
48+
tryCatch(
49+
{
50+
print("Trying firehose usage endpoint.")
51+
get_usage_firehose(client, from, to)
52+
},
53+
error = function(e) {
54+
print("Could not use firehose endpoint; trying legacy usage endpoints.")
55+
get_usage_legacy(client, from, to)
56+
}
57+
)
58+
}

0 commit comments

Comments
 (0)