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

Improvements + deletion of some files #1

Merged
merged 1 commit into from
Oct 11, 2024
Merged
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
50 changes: 0 additions & 50 deletions .github/workflows/test-coverage.yaml

This file was deleted.

2 changes: 1 addition & 1 deletion .lintr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
linters: with_defaults(line_length_linter(90))
linters: linters_with_defaults (line_length_linter(90))
exclude: "# Exclude Linting"
exclude_start: "# Begin Exclude Linting"
exclude_end: "# End Exclude Linting"
9 changes: 2 additions & 7 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: shinyusertracking
Title: Add user tracking to Shiny apps
Version: 1.0.0.000
Version: 1.0.1.000
Authors@R:
person(
"Mark", "McPherson", ,
Expand All @@ -11,13 +11,8 @@ Description: Log session and user data to a Google sheet.
License: MIT + file LICENSE
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.2.2
Suggests:
covr,
testthat (>= 3.0.0)
RoxygenNote: 7.3.1
Config/testthat/edition: 3
Imports:
shiny,
googlesheets4,
hms,
lubridate
129 changes: 96 additions & 33 deletions R/usertracking.R
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@

#' Add user tracking
#'
#' Log session ID, username (only for Private apps), session start, end and
#' duration to a Google sheet.
#'
#' @param google_email Email used for Google account username.
#' @param sheet_id Google sheet ID.
#' @param columns Which columns to record, from id, username, login, logout and
#' duration. By default all will be recorded.
#' @param session Shiny session object.
#'
#' @return Nothing; used for side effect.
Expand All @@ -18,53 +17,117 @@
#' ui <- fluidPage()
#' server <- function(input, output, session) {
#' shinyusertracking::set_user_tracking(
#' "[email protected]",
#' "1234567890987654321",
#' c("login", "logout", "duration"),
#' session
#' )
#' }
#'
#' shinyApp(ui, server)
#' }
#'
set_user_tracking <- function(google_email, sheet_id, session) {
if (sheet_id == "") {
return(invisible())
set_user_tracking <- function(columns = NULL, session) {
known_cols <- c(
"id",
"username",
"login",
"logout",
"duration"
)

if (is.null(columns)) {
columns <- known_cols
} else {
stopifnot({
columns %in% known_cols
})
}

eval_lines(".google-sheets-credentials")

google_email <- NULL
sheet_id <- NULL

try({
google_email <- get("GOOGLE_SHEET_USER")
})
try({
sheet_id <- get("GOOGLE_SHEET_ID")
})

if (is.null(google_email) || is.null(sheet_id)) {
warning(
"Credentials missing for shinyusertracking::set_user_tracking",
call. = FALSE
)
return()
}

googlesheets4::gs4_auth(
email = google_email,
cache = ".secret/"
)

shiny::isolate({
userdata <<- userdata <- data.frame( # Exclude Linting
id = session$token,
username = ifelse(is.null(session$user), "unknown", session$user),
login = Sys.time(),
logout = lubridate::NA_POSIXct_,
duration = NA_character_
)
})
session$userData$tracking <- data.frame(
id = session$token,
username = ifelse(is.null(session$user), "unknown", session$user),
login = Sys.time(),
logout = lubridate::NA_POSIXct_,
duration = NA_character_
)

session$onSessionEnded(function() {
shiny::isolate({
userdata[userdata$id == session$token, "logout"] <- Sys.time()
userdata[userdata$id == session$token, "duration"] <- as.character(
hms::hms(
round(
lubridate::as.period(
userdata[userdata$id == session$token, "logout"] -
userdata[userdata$id == session$token, "login"],
"seconds"
)
)
)
)
session$userData$tracking$logout <- Sys.time()

googlesheets4::sheet_append(sheet_id, userdata)
})
duration <- difftime(
session$userData$tracking$logout,
session$userData$tracking$login,
units = "secs"
)
duration <- abs(as.numeric(duration))
duration <- sprintf(
"%02d:%02d:%02d", # hh:mm:ss
duration %/% 3600, # whole hours (could be > 24)
duration %% 3600 %/% 60, # whole minutes left
duration %% 60 %/% 1 + round(duration %% 60 %% 1) # rounded seconds left
)

session$userData$tracking$duration <- as.character(duration)

googlesheets4::sheet_append(
sheet_id,
subset(session$userData$tracking, select = columns)
)
})
}


#' Evaluate each line of plain text file
#'
#' Reads a plain text file line by line, evaluating each line. Useful for
#' creating variables dynamically, e.g. reading in parameters.
#'
#' @param filepath Filepath as a String.
#' @param envir Environment to evaluate in. Default is calling environment.
#'
#' @return Nothing
#'
#' @examples
#' \dontrun{
#' filepath <- tempfile()
#' writeLines(
#' text = "LEFT = \"right\"",
#' con = filepath
#' )
#' eval_lines(filepath)
#' print(LEFT)
#' unlink(filepath) # delete temporary file
#' rm(left) # remove example variable
#' }
eval_lines <- function(filepath, envir = parent.frame()) {
con <- file(filepath, open = "r")
on.exit(close(con))

invisible()
while (length(line <- readLines(con, n = 1, warn = FALSE)) > 0) {
eval(parse(text = line), envir = envir)
}
}
22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
# shinyusertracking

<!-- badges: start -->
[![Codecov test coverage](https://codecov.io/gh/MarkMc1089/shinyusertracking/branch/master/graph/badge.svg)](https://app.codecov.io/gh/MarkMc1089/shinyusertracking?branch=master)
[![R-CMD-check](https://github.com/MarkMc1089/shinyusertracking/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/MarkMc1089/shinyusertracking/actions/workflows/R-CMD-check.yaml)
<!-- badges: end -->

Expand All @@ -19,7 +18,7 @@ devtools::install_github("nhsbsa-data-analytics/shinyusertracking")

## Example

Just add the function at the top of your `server` code. You will need to provide the ID of a Google Sheet and the username (email) of the Google account it is in.
Just add the function at the top of your `server` code. You will need to provide the ID of a Google Sheet and the username (email) of the Google account it is in.

``` r
library(shiny)
Expand All @@ -28,11 +27,26 @@ ui <- fluidPage()

server <- function(input, output, session) {
shinyusertracking::set_user_tracking(
"[email protected]",
"1234567890987654321",
session
)
}

shinyApp(ui, server)
```

Optionally, you can choose to log specific columns only.

Column|Description
:---:|:---:
id|The Shiny session ID
username|The username of user, if available (`null` if app is public)
login|Timestamp of session start
logout|Timestamp of session end
duration|Duration of session in `hh:mm:ss` format

``` r
shinyusertracking::set_user_tracking(
columns = c("login", "logout", "duration"),
session
)
```
14 changes: 0 additions & 14 deletions codecov.yml

This file was deleted.

33 changes: 33 additions & 0 deletions man/eval_lines.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 4 additions & 6 deletions man/set_user_tracking.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 0 additions & 12 deletions tests/testthat.R

This file was deleted.

3 changes: 0 additions & 3 deletions tests/testthat/test-usertracking.R

This file was deleted.

Loading