-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #17 from The-Strategy-Unit/15-content
- Loading branch information
Showing
5 changed files
with
276 additions
and
121 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,130 @@ | ||
# Make connection to Connect client | ||
get_client <- function( | ||
server_name = Sys.getenv("CONNECT_SERVER"), | ||
api_key = Sys.getenv("CONNECT_API_KEY") | ||
) { | ||
connectapi::connect(server_name, api_key) | ||
} | ||
|
||
# Return a dataframe with guid and group name | ||
get_groups <- function(client = get_client()) { | ||
client |> | ||
connectapi::get_groups() |> | ||
dplyr::select(guid, name) | ||
} | ||
|
||
get_group_membership <- function(client = get_client(), groups = get_groups()) { | ||
groups[["guid"]] |> | ||
# Return a list of dataframes of user information, one named element per group | ||
get_group_membership <- function(client = get_client()) { | ||
|
||
groups <- get_groups(client) | ||
|
||
groups_members <- groups[["guid"]] |> | ||
purrr::set_names(groups[["name"]]) |> | ||
purrr::map(\(guid) connectapi::get_group_members(client, guid)) | ||
|
||
# Retain empty groups by giving them '[None]' as a user | ||
for (group in names(groups_members)) { | ||
df_is_empty <- nrow(groups_members[[group]]) == 0 | ||
if (df_is_empty) { # i.e. no users were returned | ||
groups_members[[group]] <- tibble::tibble(username = "[none]") | ||
} | ||
} | ||
|
||
groups_members | ||
|
||
} | ||
|
||
get_user_groups <- function(group_membership = get_group_membership()) { | ||
# Return a dataframe with one row per username and group membership | ||
get_user_groups <- function(client = get_client()) { | ||
|
||
group_membership <- get_group_membership(client) | ||
|
||
group_membership |> | ||
purrr::list_rbind(names_to = "group") |> | ||
dplyr::select(username, group) | ||
|
||
} | ||
|
||
get_all_users <- function(client = get_client()) { | ||
connectapi::get_users(client) |> | ||
# Return a dataframe of user details (username, name, user_role, active_time) | ||
get_all_users <- function( | ||
client = get_client(), | ||
include_guid = FALSE | ||
) { | ||
|
||
users <- connectapi::get_users(client) |> | ||
dplyr::mutate( | ||
name = glue::glue("{first_name} {last_name}") |> | ||
stringr::str_squish() | ||
) |> | ||
dplyr::select(username, name, user_role, active_time) | ||
dplyr::select(guid, username, name, user_role, active_time) | ||
|
||
if (!include_guid) dplyr::select(users, -guid) else return(users) | ||
|
||
} | ||
|
||
get_all_users_groups <- function( | ||
all_users = get_all_users(), | ||
user_groups = get_user_groups() | ||
) { | ||
# Returns a dataframe of user details with one row per user and group | ||
get_all_users_groups <- function(client = get_client()) { | ||
|
||
all_users <- get_all_users(client) | ||
user_groups <- get_user_groups(client) | ||
|
||
all_users |> | ||
dplyr::full_join( | ||
user_groups, | ||
by = "username", | ||
relationship = "many-to-many" | ||
) |> | ||
dplyr::mutate( | ||
last_login = as.character(format(active_time, "%Y-%m-%d")), | ||
.after = user_role | ||
) |> | ||
dplyr::select(-active_time) |> | ||
dplyr::mutate( | ||
dplyr::across( | ||
tidyselect::everything(), | ||
\(column) tidyr::replace_na(column, "[none]") | ||
) | ||
) |> | ||
dplyr::arrange(tolower(username), tolower(group)) | ||
|
||
} | ||
|
||
# Returns a dataframe of content details, one row per content item | ||
get_content <- function(client = get_client()) { | ||
client |> | ||
connectapi::get_content() |> | ||
tidyr::hoist(owner, "username") |> | ||
dplyr::mutate( | ||
has_tags = purrr::map(tags, \(x) !is.null(x)), | ||
has_tags = unlist(has_tags), | ||
app_mode = dplyr::if_else( | ||
content_category != "", | ||
glue::glue("{app_mode} ({content_category})"), | ||
app_mode | ||
) | ||
) |> | ||
dplyr::select( | ||
content_id = id, | ||
name, | ||
title, | ||
description, | ||
mode = app_mode, | ||
has_tags, | ||
last_deployed_time, | ||
owner_username = username, | ||
content_url, | ||
dashboard_url | ||
) |> | ||
dplyr::arrange(dplyr::desc(as.numeric(content_id))) |> | ||
dplyr::mutate( | ||
last_deployed_time = format(last_deployed_time, "%Y-%m-%d %H:%M"), | ||
dplyr::across( | ||
dplyr::where(is.character), | ||
\(col) dplyr::na_if(col, "") | ||
), | ||
dplyr::across( | ||
tidyselect::everything(), | ||
\(column) tidyr::replace_na(column, "[none]") | ||
) | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# Exit rendering if environment variables are missing (prevents error emails) | ||
check_env_vars <- function(required_env_vars) { | ||
if (any(Sys.getenv(required_env_vars) == "")) { | ||
cat("One of the following environment variables was not set, so exiting \n\n") | ||
cat(paste("*", required_env_vars, collapse = "\n"), "\n\n") | ||
knitr::knit_exit() | ||
} | ||
} | ||
|
||
# Create a {DT} datatable with common settings | ||
create_dt <- function(dat, type = c("users", "content")) { | ||
dat |> | ||
DT::datatable( | ||
filter = "top", | ||
rownames = FALSE, | ||
escape = FALSE, # for URLs | ||
extensions = "Buttons", | ||
options = list( | ||
dom = "Bftipr", | ||
autoWidth = TRUE, | ||
buttons = list( | ||
list( | ||
extend = "csv", | ||
title = glue::glue("{Sys.Date()}_su-posit-connect_{type}-lookup") | ||
) | ||
) | ||
) | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,110 +1,79 @@ | ||
--- | ||
title: 'Lookup: Posit Connect People' | ||
title: "Lookup: Posit Connect People and Content" | ||
date: "last-modified" | ||
date-format: D MMM YYYY HH:mm | ||
format: html | ||
author: Data Science Team, The Strategy Unit | ||
format: | ||
html: | ||
page-layout: full | ||
execute: | ||
echo: false | ||
resource_files: | ||
- R/api.R | ||
- R/utils.R | ||
--- | ||
|
||
```{r} | ||
#| label: check-env-vars | ||
#| results: "asis" | ||
required_env_vars <- c("CONNECT_SERVER", "CONNECT_API_KEY") | ||
if (any(Sys.getenv(required_env_vars) == "")) { | ||
cat("One of the following environment variables was not set, so exiting \n\n") | ||
cat(paste("*", required_env_vars, collapse = "\n"), "\n\n") | ||
knitr::knit_exit() | ||
} | ||
list.files("R", "\\.R$", , TRUE) |> purrr::walk(source) | ||
check_env_vars(c("CONNECT_SERVER", "CONNECT_API_KEY")) | ||
``` | ||
|
||
```{r} | ||
#| label: prepare-workspace | ||
#| label: prepare-variables | ||
#| include: false | ||
list.files("R", "\\.R$", , TRUE) |> purrr::walk(source) | ||
server_name <- Sys.getenv("CONNECT_SERVER") | ||
people_path <- glue::glue("{server_name}connect/#/people/") | ||
dat <- get_all_users_groups() |> | ||
tidyr::replace_na(list(username = "[No user]", group = "[No group]")) | ||
``` | ||
content_path <- glue::glue("{server_name}connect/#/content/") | ||
## Purpose | ||
client <- get_client() | ||
users_groups <- get_all_users_groups(client) | ||
content <- get_content(client) | ||
A quick lookup of [users and groups](`r people_path`) on the server `r server_name`. Find [the source on GitHub](https://github.com/The-Strategy-Unit/posit-connect-people/). | ||
n_users <- users_groups |> | ||
dplyr::filter(username != "[none]") |> | ||
dplyr::distinct() |> | ||
nrow() | ||
## By user | ||
n_groups <- users_groups |> | ||
dplyr::filter(group != "[none]") |> | ||
dplyr::distinct() |> | ||
nrow() | ||
```{r} | ||
#| label: by-user | ||
dat |> | ||
dplyr::select(username, group) |> | ||
dplyr::arrange(tolower(username), tolower(group)) |> | ||
reactable::reactable( | ||
groupBy = "username", | ||
columns = list( | ||
username = reactable::colDef( | ||
filterable = TRUE, | ||
filterInput = function(values, name) { | ||
htmltools::tags$select( | ||
# Set to undefined to clear the filter | ||
onchange = sprintf("Reactable.setFilter('cars-select', '%s', event.target.value || undefined)", name), | ||
# "All" has an empty value to clear the filter, and is the default option | ||
htmltools::tags$option(value = "", "All"), | ||
lapply(unique(values), htmltools::tags$option), | ||
"aria-label" = sprintf("Filter %s", name), | ||
style = "width: 100%; height: 28px;" | ||
) | ||
} | ||
) | ||
) | ||
) | ||
n_content <- nrow(content) | ||
``` | ||
|
||
## By group | ||
## Purpose | ||
|
||
This page contains tabular lookups of [users](`r people_path`) and [content](`r content_path`) on the server [`r server_name`](`r server_name`). Find [the source on GitHub](https://github.com/The-Strategy-Unit/posit-connect-people/). | ||
|
||
## Users and groups | ||
|
||
This table shows one row per user and group. There are `r n_users` users and `r n_groups` groups. Note that it's possible for users not to have a group and vice versa. You can search, sort and filter the data and click the 'CSV' button to download the current view. | ||
|
||
```{r} | ||
#| label: by-group | ||
dat |> | ||
dplyr::select(group, username) |> | ||
dplyr::arrange(tolower(group), tolower(username)) |> | ||
reactable::reactable( | ||
groupBy = "group", | ||
columns = list( | ||
group = reactable::colDef( | ||
filterable = TRUE, | ||
filterInput = function(values, name) { | ||
htmltools::tags$select( | ||
# Set to undefined to clear the filter | ||
onchange = sprintf("Reactable.setFilter('cars-select', '%s', event.target.value || undefined)", name), | ||
# "All" has an empty value to clear the filter, and is the default option | ||
htmltools::tags$option(value = "", "All"), | ||
lapply(unique(values), htmltools::tags$option), | ||
"aria-label" = sprintf("Filter %s", name), | ||
style = "width: 100%; height: 28px;" | ||
) | ||
} | ||
) | ||
) | ||
) | ||
#| label: users-table | ||
users_groups |> | ||
dplyr::mutate(dplyr::across(dplyr::where(is.character), as.factor)) |> | ||
create_dt("users") | ||
``` | ||
|
||
## All data | ||
## Content | ||
|
||
This table shows one row per content item. There are `r n_content` content items. You can search, sort and filter the data and click the 'CSV' button to download the current view. Click the links in the `*_url` columns to open the item with or without the Posit Connect dashboard framing the content. | ||
|
||
```{r} | ||
#| label: all-data | ||
htmltools::browsable( | ||
htmltools::tagList( | ||
htmltools::tags$button( | ||
"Download CSV", | ||
onclick = "Reactable.downloadDataCSV('data-table', 'posit-connect-people.csv')" | ||
), | ||
reactable::reactable( | ||
dat |> dplyr::arrange(tolower(username), tolower(group)), | ||
filterable = TRUE, | ||
elementId = "data-table", | ||
) | ||
) | ||
) | ||
#| label: content-table | ||
content |> | ||
dplyr::mutate( | ||
content_url = glue::glue("<a href='{content_url}'>Link</a>"), | ||
dashboard_url = glue::glue("<a href='{dashboard_url}'>Link</a>"), | ||
dplyr::across(dplyr::where(is.character), as.factor) | ||
) |> | ||
create_dt("content") | ||
``` |
Oops, something went wrong.