Skip to content

Commit fbab422

Browse files
authored
EOL Runtime Scanner: Initial Table Edition (posit-dev#163)
* initial version * adjust version filtering strategy
1 parent 7986535 commit fbab422

File tree

8 files changed

+3986
-0
lines changed

8 files changed

+3986
-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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.posit/
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
library(shiny)
2+
library(bslib)
3+
library(connectapi)
4+
library(reactable)
5+
library(dplyr)
6+
library(shinycssloaders)
7+
library(lubridate)
8+
library(bsicons)
9+
10+
source("get_usage.R")
11+
12+
options(
13+
spinner.type = 1,
14+
spinner.color = "#7494b1"
15+
)
16+
17+
# Shiny app definition
18+
19+
ui <- page_sidebar(
20+
title = "End-of-Life Runtime Scanner",
21+
22+
sidebar = sidebar(
23+
open = TRUE,
24+
width = 275,
25+
26+
selectizeInput(
27+
"r_versions",
28+
label = "Select R Versions",
29+
choices = NULL,
30+
multiple = TRUE
31+
),
32+
33+
selectizeInput(
34+
"py_versions",
35+
label = "Select Python Versions",
36+
choices = NULL,
37+
multiple = TRUE
38+
),
39+
40+
selectizeInput(
41+
"quarto_versions",
42+
label = "Select Quarto Versions",
43+
choices = NULL,
44+
multiple = TRUE
45+
)
46+
),
47+
48+
withSpinner(reactableOutput("content_table"))
49+
)
50+
51+
server <- function(input, output, session) {
52+
client <- connect()
53+
54+
content <- reactive({
55+
content <- get_content(client) |>
56+
filter(
57+
!if_all(c(r_version, py_version, quarto_version), is.na)
58+
)
59+
})
60+
61+
observeEvent(
62+
content(),
63+
{
64+
rv <- sort(unique(content()$r_version))
65+
pv <- sort(unique(content()$py_version))
66+
qv <- sort(unique(content()$quarto_version))
67+
68+
updateSelectizeInput(
69+
session,
70+
"r_versions",
71+
choices = rv,
72+
selected = character(0)
73+
)
74+
updateSelectizeInput(
75+
session,
76+
"py_versions",
77+
choices = pv,
78+
selected = character(0)
79+
)
80+
updateSelectizeInput(
81+
session,
82+
"quarto_versions",
83+
choices = qv,
84+
selected = character(0)
85+
)
86+
},
87+
ignoreNULL = TRUE
88+
)
89+
90+
usage <- reactive({
91+
get_usage(
92+
client,
93+
from = today() - days(6),
94+
to = today()
95+
) |>
96+
group_by(content_guid) |>
97+
summarize(hits = n())
98+
})
99+
100+
content_filtered <- reactive({
101+
df <- content()
102+
rv <- input$r_versions
103+
pv <- input$py_versions
104+
qv <- input$quarto_versions
105+
106+
df |>
107+
filter(
108+
(length(rv) > 0 & r_version %in% rv) |
109+
(length(pv) > 0 & py_version %in% pv) |
110+
(length(qv) > 0 & quarto_version %in% qv) |
111+
(length(rv) == 0 & length(pv) == 0 & length(qv) == 0)
112+
)
113+
})
114+
115+
content_table_data <- reactive({
116+
content_filtered() |>
117+
select(
118+
guid,
119+
title,
120+
app_mode,
121+
r_version,
122+
py_version,
123+
quarto_version,
124+
dashboard_url
125+
) |>
126+
left_join(usage(), by = c("guid" = "content_guid"))
127+
})
128+
129+
output$content_table <- renderReactable({
130+
data <- content_table_data()
131+
132+
reactable(
133+
data,
134+
defaultPageSize = 25,
135+
columns = list(
136+
guid = colDef(name = "GUID"),
137+
title = colDef(name = "Title"),
138+
app_mode = colDef(name = "App Mode"),
139+
r_version = colDef(name = "R Version"),
140+
py_version = colDef(name = "Python Version"),
141+
quarto_version = colDef(name = "Quarto Version"),
142+
dashboard_url = colDef(
143+
name = "",
144+
width = 32,
145+
sortable = FALSE,
146+
cell = function(url) {
147+
if (is.na(url) || url == "") return("")
148+
HTML(as.character(tags$div(
149+
onclick = "event.stopPropagation()",
150+
tags$a(
151+
href = url,
152+
target = "_blank",
153+
bsicons::bs_icon("arrow-up-right-square")
154+
)
155+
)))
156+
},
157+
html = TRUE
158+
),
159+
hits = colDef(name = "Hits in Last Week")
160+
)
161+
)
162+
})
163+
}
164+
165+
shinyApp(ui, server)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
library(connectapi)
2+
3+
NA_datetime_ <- vctrs::new_datetime(NA_real_, tzone = "UTC")
4+
NA_list_ <- list(list())
5+
6+
usage_dtype <- tibble::tibble(
7+
"id" = NA_integer_,
8+
"user_guid" = NA_character_,
9+
"content_guid" = NA_character_,
10+
"timestamp" = NA_datetime_,
11+
"data" = NA_list_
12+
)
13+
14+
to_iso8601 <- function(x) {
15+
strftime(x, "%Y-%m-%dT%H:%M:%S%z") |>
16+
sub("([+-]\\d{2})(\\d{2})$", "\\1:\\2", x = _)
17+
}
18+
19+
get_usage <- function(client, from = NULL, to = NULL) {
20+
# Allow us to pass in either dates or specific timestamps.
21+
if (is.Date(from)) {
22+
from <- as.POSIXct(paste(from, "00:00:00"), tz = "")
23+
}
24+
if (is.Date(to)) {
25+
to <- as.POSIXct(paste(to, "23:59:59"), tz = "")
26+
}
27+
from_timestamp <- to_iso8601(from)
28+
to_timestamp <- to_iso8601(to)
29+
30+
usage_raw <- client$GET(
31+
connectapi:::v1_url("instrumentation", "content", "hits"),
32+
query = list(
33+
from = from_timestamp,
34+
to = to_timestamp
35+
)
36+
)
37+
usage_parsed <- connectapi:::parse_connectapi_typed(usage_raw, usage_dtype)
38+
usage_parsed[c("user_guid", "content_guid", "timestamp")]
39+
}

0 commit comments

Comments
 (0)