From 1b1513106f3912a1e4c6c13c2628e7dcd1e369c0 Mon Sep 17 00:00:00 2001 From: Howard Baek <50791792+howardbaek@users.noreply.github.com> Date: Mon, 11 Dec 2023 20:22:43 +0900 Subject: [PATCH 01/36] Write Google Analytics data to Google Sheet --- R/write-data.R | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 R/write-data.R diff --git a/R/write-data.R b/R/write-data.R new file mode 100644 index 0000000..e561f68 --- /dev/null +++ b/R/write-data.R @@ -0,0 +1,26 @@ +library(metricminer) +library(googlesheets4) + +# The data is publicly available so we don't need to authenticate +gs4_deauth() +# Publicly accessible Google Sheet +sheet_url <- "https://docs.google.com/spreadsheets/d/13x8lD9SeuPGCs8SbtbRRK_q6o1V_rd3B07JuhebT-vk/edit?usp=sharing" + +# Google Analytics---- +authorize("google") + +accounts <- get_ga_user() +stats_list <- all_ga_metrics(account_id = accounts$items$id[5]) + +# Save the 3 elements of list as data frames +metrics <- stats_list$metrics +dimensions <- stats_list$dimensions +link_clicks <- stats_list$link_clicks + +# Write to Google Sheets +metrics %>% sheet_write(ss = sheet_url, sheet = "metrics") +dimensions %>% sheet_write(ss = sheet_url, sheet = "dimensions") +link_clicks %>% sheet_write(ss = sheet_url, sheet = "link_clicks") + + +# GitHub---- From 4a56ac7cf50518251766bdaa6e24de4b90694ec3 Mon Sep 17 00:00:00 2001 From: Howard Baek <50791792+howardbaek@users.noreply.github.com> Date: Mon, 11 Dec 2023 20:28:03 +0900 Subject: [PATCH 02/36] Initial GHA --- .github/workflows/write-data.yaml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/write-data.yaml diff --git a/.github/workflows/write-data.yaml b/.github/workflows/write-data.yaml new file mode 100644 index 0000000..ac4a10c --- /dev/null +++ b/.github/workflows/write-data.yaml @@ -0,0 +1,23 @@ +on: + schedule: + - cron: '*/2 * * * *' + +jobs: + import-data: + runs-on: ubuntu-latest + steps: + - name: Set up R + uses: r-lib/actions/setup-r@v2 + + - name: Install packages + uses: r-lib/actions/setup-r-dependencies@v2 + with: + packages: | + any::metricminer + any::googlesheets4 + + - name: Check out repository + uses: actions/checkout@v3 + + - name: Import data + run: Rscript -e 'source("R/write-data.R")' From df271258e5ed0ed6ce69235a8adc1ae6bb83b497 Mon Sep 17 00:00:00 2001 From: Howard Baek <50791792+howardbaek@users.noreply.github.com> Date: Mon, 11 Dec 2023 20:34:47 +0900 Subject: [PATCH 03/36] Change name of GitHub Action --- .github/workflows/write-data.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/write-data.yaml b/.github/workflows/write-data.yaml index ac4a10c..b920a8a 100644 --- a/.github/workflows/write-data.yaml +++ b/.github/workflows/write-data.yaml @@ -3,7 +3,7 @@ on: - cron: '*/2 * * * *' jobs: - import-data: + write-data: runs-on: ubuntu-latest steps: - name: Set up R From d433bd37c4758411dba2a774929492a8f3f133a4 Mon Sep 17 00:00:00 2001 From: Howard Baek <50791792+howardbaek@users.noreply.github.com> Date: Tue, 26 Dec 2023 14:09:31 -0800 Subject: [PATCH 04/36] Add GHA step to authorize metricminer --- .github/workflows/write-data.yaml | 22 +++++++++++++++++++++- R/write-data.R | 2 -- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.github/workflows/write-data.yaml b/.github/workflows/write-data.yaml index b920a8a..1a8625e 100644 --- a/.github/workflows/write-data.yaml +++ b/.github/workflows/write-data.yaml @@ -19,5 +19,25 @@ jobs: - name: Check out repository uses: actions/checkout@v3 - - name: Import data + - name: Authorize metricminer + env: + METRICMINER_CALENDLY: ${{ secrets.METRICMINER_CALENDLY }} + METRICMINER_GITHUB_PAT: ${{ secrets.METRICMINER_GITHUB_PAT }} + METRICMINER_GOOGLE_ACCESS: ${{ secrets.METRICMINER_GOOGLE_ACCESS }} + METRICMINER_GOOGLE_REFRESH: ${{ secrets.METRICMINER_GOOGLE_REFRESH }} + run: | + # Authorize Calendly + auth_from_secret("calendly", token = Sys.getenv("METRICMINER_CALENDLY")) + + # Authorize GitHub + auth_from_secret("github", token = Sys.getenv("METRICMINER_GITHUB_PAT")) + + # Authorize Google + auth_from_secret("google", + refresh_token = Sys.getenv("METRICMINER_GOOGLE_REFRESH"), + access_token = Sys.getenv("METRICMINER_GOOGLE_ACCESS"), + cache = TRUE + ) + + - name: Write data run: Rscript -e 'source("R/write-data.R")' diff --git a/R/write-data.R b/R/write-data.R index e561f68..ad3f8cf 100644 --- a/R/write-data.R +++ b/R/write-data.R @@ -7,8 +7,6 @@ gs4_deauth() sheet_url <- "https://docs.google.com/spreadsheets/d/13x8lD9SeuPGCs8SbtbRRK_q6o1V_rd3B07JuhebT-vk/edit?usp=sharing" # Google Analytics---- -authorize("google") - accounts <- get_ga_user() stats_list <- all_ga_metrics(account_id = accounts$items$id[5]) From 7e65df5d296976dc9404572dc4ee6e32514db903 Mon Sep 17 00:00:00 2001 From: Howard Baek <50791792+howardbaek@users.noreply.github.com> Date: Tue, 26 Dec 2023 14:15:00 -0800 Subject: [PATCH 05/36] Fix bug in R/write-data.R --- R/write-data.R | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/R/write-data.R b/R/write-data.R index ad3f8cf..7405ac2 100644 --- a/R/write-data.R +++ b/R/write-data.R @@ -8,7 +8,7 @@ sheet_url <- "https://docs.google.com/spreadsheets/d/13x8lD9SeuPGCs8SbtbRRK_q6o1 # Google Analytics---- accounts <- get_ga_user() -stats_list <- all_ga_metrics(account_id = accounts$items$id[5]) +stats_list <- all_ga_metrics(account_id = accounts$id[5]) # Save the 3 elements of list as data frames metrics <- stats_list$metrics @@ -20,5 +20,4 @@ metrics %>% sheet_write(ss = sheet_url, sheet = "metrics") dimensions %>% sheet_write(ss = sheet_url, sheet = "dimensions") link_clicks %>% sheet_write(ss = sheet_url, sheet = "link_clicks") - # GitHub---- From c2b1759d71f894a9e9baa82d3c3883c3fef84bc3 Mon Sep 17 00:00:00 2001 From: Howard Baek <50791792+howardbaek@users.noreply.github.com> Date: Tue, 26 Dec 2023 14:24:10 -0800 Subject: [PATCH 06/36] Install `remotes` and `metricminer` on GHA --- .github/workflows/write-data.yaml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/write-data.yaml b/.github/workflows/write-data.yaml index 1a8625e..ac23a40 100644 --- a/.github/workflows/write-data.yaml +++ b/.github/workflows/write-data.yaml @@ -9,13 +9,20 @@ jobs: - name: Set up R uses: r-lib/actions/setup-r@v2 - - name: Install packages + - name: Install googlesheets4 uses: r-lib/actions/setup-r-dependencies@v2 with: packages: | - any::metricminer any::googlesheets4 + - name: Install remotes + run: | + Rscript -e "install.packages('remotes')" + + - name: Install metricminer from Github + run: | + Rscript -e "remotes::install_github("fhdsl/metricminer")" + - name: Check out repository uses: actions/checkout@v3 From 9f4b9a0d9492f4726492a03dd0fb44f5a55eb02f Mon Sep 17 00:00:00 2001 From: Howard Baek <50791792+howardbaek@users.noreply.github.com> Date: Tue, 26 Dec 2023 14:32:23 -0800 Subject: [PATCH 07/36] Hmmmm --- R/write-data.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/write-data.R b/R/write-data.R index 7405ac2..d4bfa94 100644 --- a/R/write-data.R +++ b/R/write-data.R @@ -1,4 +1,4 @@ -library(metricminer) +# library(metricminer) library(googlesheets4) # The data is publicly available so we don't need to authenticate From 3b9b0770727eed6837cfeafc60a1e6573b1dd60b Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Thu, 4 Jan 2024 09:36:33 -0500 Subject: [PATCH 08/36] Basic structure --- R/write-data.R | 54 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/R/write-data.R b/R/write-data.R index d4bfa94..0b09e39 100644 --- a/R/write-data.R +++ b/R/write-data.R @@ -1,23 +1,41 @@ -# library(metricminer) -library(googlesheets4) -# The data is publicly available so we don't need to authenticate -gs4_deauth() -# Publicly accessible Google Sheet -sheet_url <- "https://docs.google.com/spreadsheets/d/13x8lD9SeuPGCs8SbtbRRK_q6o1V_rd3B07JuhebT-vk/edit?usp=sharing" +#' Writes data to a Googlesheet +#' @description This is a function to +#' @param input +#' @param gsheet +#' @return The googlesheet URL where the data has been written +#' @importFrom utils menu installed.packages +#' @export +#' @examples \dontrun{ +#' +#' authorize("github") +#' repo_list <- get_repo_list(owner = "fhdsl") +#' +#' write_to_gsheet(repo_list) +#' } +#' -# Google Analytics---- -accounts <- get_ga_user() -stats_list <- all_ga_metrics(account_id = accounts$id[5]) +write_to_gsheet <- function() { -# Save the 3 elements of list as data frames -metrics <- stats_list$metrics -dimensions <- stats_list$dimensions -link_clicks <- stats_list$link_clicks +} -# Write to Google Sheets -metrics %>% sheet_write(ss = sheet_url, sheet = "metrics") -dimensions %>% sheet_write(ss = sheet_url, sheet = "dimensions") -link_clicks %>% sheet_write(ss = sheet_url, sheet = "link_clicks") -# GitHub---- + +#' Writes data to a tabular file +#' @description +#' @param input +#' @param file_path +#' @param table_type +#' @return The file path where the data has been written +#' @importFrom utils menu installed.packages +#' @export +#' @examples \dontrun{ +#' +#' authorize("github") +#' repo_list <- get_repo_list(owner = "fhdsl") +#' +#' write_to_table(repo_list) +#' } +write_to_table <- function() { + +} From bf42329b628cace67791764d3435408f03e2a949 Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Thu, 4 Jan 2024 10:09:24 -0500 Subject: [PATCH 09/36] Declare GitHub classes --- R/classes.R | 2 ++ R/github.R | 12 ++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 R/classes.R diff --git a/R/classes.R b/R/classes.R new file mode 100644 index 0000000..f83b78c --- /dev/null +++ b/R/classes.R @@ -0,0 +1,2 @@ +class_gh <- R6::R6Class("github") + diff --git a/R/github.R b/R/github.R index ec19744..846d8ce 100644 --- a/R/github.R +++ b/R/github.R @@ -28,6 +28,9 @@ get_github <- function(token = NULL, url) { # Process and return results result_content <- httr::content(result, "text") result_list <- jsonlite::fromJSON(result_content) + + class(result_list) <- "api_response" + return(result_list) } @@ -82,6 +85,8 @@ get_repo_list <- function(owner, count = "all", token = NULL) { .limit = count ) + class(repo_list) <- "api_response" + return(repo_list) } @@ -134,11 +139,14 @@ get_github_metrics <- function(repo, token = NULL, count = "all", data_format = names(results) <- names(api_calls) + class(results) <- "multi_api_response" + if (data_format == "dataframe") { results <- clean_repo_metrics( repo_name = paste0(c(owner, repo), collapse = "/"), repo_metric_list = results ) + class(results) <- "gh_metric_dataframe" } return(results) } @@ -195,8 +203,11 @@ get_repos_metrics <- function(owner = NULL, repo_names = NULL, token = NULL, dat # Keep names names(repo_metrics) <- repo_names + class(results) <- "multi_api_response" + if (data_format == "dataframe") { repo_metrics <- dplyr::bind_rows(repo_metrics) + class(results) <- "gh_metric_dataframe" } return(repo_metrics) @@ -236,6 +247,7 @@ gh_repo_wrapper <- function(api_call, owner, repo, token = NULL, count = Inf, da if (grepl("404", result[1])) result <- "No results" if (grepl("Error", result[1])) result <- "No results" + class(result) <- "api_response" return(result) } From 8d1d02476bbad3a05d26167c351c80cbebe83fae Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Thu, 4 Jan 2024 10:16:46 -0500 Subject: [PATCH 10/36] Add ga classes --- R/export.R | 19 ------------------- R/google-analytics.R | 11 ++++++++++- 2 files changed, 10 insertions(+), 20 deletions(-) delete mode 100644 R/export.R diff --git a/R/export.R b/R/export.R deleted file mode 100644 index d4a3ddc..0000000 --- a/R/export.R +++ /dev/null @@ -1,19 +0,0 @@ - -#' Export data to Googlesheets -#' @description -#' @param app_name app would you like to authorize? Supported apps are 'google' 'calendly' and 'github' -#' @param cache Should the token be cached as an .httr-oauth file or API keys stored as global options? -#' @return OAuth token saved to the environment so the package can use the users' Google data -#' @importFrom utils menu installed.packages browseURL -#' @importFrom httr oauth_app oauth_endpoints oauth2.0_token -#' @export -#' @examples \dontrun{ -#' -#' authorize("google") -#' -#' google -#' ss <- googlesheets4::gs4_create("metricminer_data", sheets = ) -#' -#' -#' } - diff --git a/R/google-analytics.R b/R/google-analytics.R index d972c27..ae6b6fd 100644 --- a/R/google-analytics.R +++ b/R/google-analytics.R @@ -49,6 +49,8 @@ request_ga <- function(token, url, query = NULL, body_params = NULL, request_typ # Process and return results result_content <- httr::content(result, "text") result_list <- jsonlite::fromJSON(result_content) + + class(result_list) <- "api_response" return(result_list) } @@ -82,7 +84,9 @@ get_ga_user <- function(token = NULL, request_type = "GET") { "Are you sure you have Google Analytics properties underneath THIS google account?")) } - return(results$items) + result <- results$items + class(result) <- "api_response" + return(result) } #' Get all property ids for all Google Analytics associated with an account id @@ -119,6 +123,7 @@ get_ga_properties <- function(account_id, token = NULL) { "Are you sure you have Google Analytics properties underneath THIS account id?")) } + class(results) <- "api_response" return(results) } @@ -156,6 +161,7 @@ get_ga_metadata <- function(property_id, token = NULL) { request_type = "GET" ) + class(results) <- "api_response" return(results) } @@ -233,9 +239,12 @@ get_ga_stats <- function(property_id, start_date = "2015-08-14", token = NULL, b request_type = "POST" ) + class(results) <- c("api_response") + if (dataformat == "dataframe") { if (stats_type == "metrics") results <- clean_metric_data(results) if (stats_type %in% c("dimensions", "link_clicks")) results <- wrangle_dimensions(results) + class(results) <- c("ga_df", "list") } return(results) From 845c3a48042c8aa2c5420b5a0b63075aefef3c4c Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Thu, 4 Jan 2024 10:19:59 -0500 Subject: [PATCH 11/36] google form classes --- R/google-forms.R | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/R/google-forms.R b/R/google-forms.R index 187aef8..e24eb01 100644 --- a/R/google-forms.R +++ b/R/google-forms.R @@ -50,6 +50,7 @@ request_google_forms <- function(token, url, if (return_request) { return(list(result = result_list, request_info = request_info)) } else { + class(result_list) <- c("api_response") return(result_list) } } @@ -105,6 +106,7 @@ get_google_form <- function(form_id, token = NULL, dataformat = "dataframe") { form_metadata = form_info, response_info = response_info ) + class(result) <- "google_form__response" if (dataformat == "dataframe") { metadata <- get_question_metadata(form_info) @@ -114,11 +116,13 @@ get_google_form <- function(form_id, token = NULL, dataformat = "dataframe") { } else { answers_df <- "no responses yet" } - return(list( + result <- list( title = result$form_metadata$result$info$title, metadata = metadata, answers = answers_df - )) + ) + class(result) <- "google_form_df_result" + return(result) } return(result) } From 88a11fd1ba643366488664c5140d4514728f4ebb Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Thu, 4 Jan 2024 11:25:32 -0500 Subject: [PATCH 12/36] Add classes --- R/google-forms.R | 2 ++ R/slido.R | 2 ++ R/youtube.R | 3 +++ 3 files changed, 7 insertions(+) diff --git a/R/google-forms.R b/R/google-forms.R index e24eb01..fc5390b 100644 --- a/R/google-forms.R +++ b/R/google-forms.R @@ -161,6 +161,8 @@ get_multiple_forms <- function(form_ids = NULL, token = NULL) { # Set as names names(all_form_info) <- titles + class(all_form_info) <- "multi_api_response" + all_form_info } diff --git a/R/slido.R b/R/slido.R index bf972cb..be49e94 100644 --- a/R/slido.R +++ b/R/slido.R @@ -86,6 +86,8 @@ get_slido_files <- function(drive_id, token = NULL, recursive = TRUE, keep_dupli return(slido_data_df) }, USE.NAMES = TRUE) + class(slido_data) <- c("list", "slido_df") + return(slido_data) } diff --git a/R/youtube.R b/R/youtube.R index 30cf6bb..c2e2fb1 100644 --- a/R/youtube.R +++ b/R/youtube.R @@ -44,5 +44,8 @@ get_youtube_stats <- function(channel_id) { # Process and return results result_content <- httr::content(result, "text") result_list <- jsonlite::fromJSON(result_content) + + class(result_list) <- "api_response" + return(result_list) } From ecb5786960cf2c62d92a6fad6bfc0ac13afa979a Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Thu, 4 Jan 2024 11:26:41 -0500 Subject: [PATCH 13/36] Fix error --- R/github.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/github.R b/R/github.R index 846d8ce..bdb4a08 100644 --- a/R/github.R +++ b/R/github.R @@ -203,11 +203,11 @@ get_repos_metrics <- function(owner = NULL, repo_names = NULL, token = NULL, dat # Keep names names(repo_metrics) <- repo_names - class(results) <- "multi_api_response" + class(repo_metrics) <- "multi_api_response" if (data_format == "dataframe") { repo_metrics <- dplyr::bind_rows(repo_metrics) - class(results) <- "gh_metric_dataframe" + class(repo_metrics) <- "gh_metric_dataframe" } return(repo_metrics) From 3b5edf2572c427a9898d185d4871ba484d557a3a Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Thu, 4 Jan 2024 11:27:00 -0500 Subject: [PATCH 14/36] Fix warning about global variable --- R/utils.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/utils.R b/R/utils.R index 80c9e7f..4a1c55a 100644 --- a/R/utils.R +++ b/R/utils.R @@ -1,6 +1,6 @@ utils::globalVariables(c( "result", "num", "test_name", "scopes", "set_token", "browseURL", "remove_token", "get_token", "get_github", "get_calendly", "%>%", - "token", "query_params", "file_name" + "token", "query_params", "file_name", "accounts" )) #' Supported endpoints #' @description This is function stores endpoints and supported app names From e95907ca42114d62eed082419af127f9b7a17d4b Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Tue, 9 Jan 2024 13:05:05 -0500 Subject: [PATCH 15/36] Revert "Add classes" This reverts commit 88a11fd1ba643366488664c5140d4514728f4ebb. --- R/google-forms.R | 2 -- R/slido.R | 2 -- R/youtube.R | 3 --- 3 files changed, 7 deletions(-) diff --git a/R/google-forms.R b/R/google-forms.R index fc5390b..e24eb01 100644 --- a/R/google-forms.R +++ b/R/google-forms.R @@ -161,8 +161,6 @@ get_multiple_forms <- function(form_ids = NULL, token = NULL) { # Set as names names(all_form_info) <- titles - class(all_form_info) <- "multi_api_response" - all_form_info } diff --git a/R/slido.R b/R/slido.R index be49e94..bf972cb 100644 --- a/R/slido.R +++ b/R/slido.R @@ -86,8 +86,6 @@ get_slido_files <- function(drive_id, token = NULL, recursive = TRUE, keep_dupli return(slido_data_df) }, USE.NAMES = TRUE) - class(slido_data) <- c("list", "slido_df") - return(slido_data) } diff --git a/R/youtube.R b/R/youtube.R index c2e2fb1..30cf6bb 100644 --- a/R/youtube.R +++ b/R/youtube.R @@ -44,8 +44,5 @@ get_youtube_stats <- function(channel_id) { # Process and return results result_content <- httr::content(result, "text") result_list <- jsonlite::fromJSON(result_content) - - class(result_list) <- "api_response" - return(result_list) } From 749693c701265a238c0e26985adfacd70e057a45 Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Tue, 9 Jan 2024 13:05:11 -0500 Subject: [PATCH 16/36] Revert "google form classes" This reverts commit 845c3a48042c8aa2c5420b5a0b63075aefef3c4c. --- R/google-forms.R | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/R/google-forms.R b/R/google-forms.R index e24eb01..187aef8 100644 --- a/R/google-forms.R +++ b/R/google-forms.R @@ -50,7 +50,6 @@ request_google_forms <- function(token, url, if (return_request) { return(list(result = result_list, request_info = request_info)) } else { - class(result_list) <- c("api_response") return(result_list) } } @@ -106,7 +105,6 @@ get_google_form <- function(form_id, token = NULL, dataformat = "dataframe") { form_metadata = form_info, response_info = response_info ) - class(result) <- "google_form__response" if (dataformat == "dataframe") { metadata <- get_question_metadata(form_info) @@ -116,13 +114,11 @@ get_google_form <- function(form_id, token = NULL, dataformat = "dataframe") { } else { answers_df <- "no responses yet" } - result <- list( + return(list( title = result$form_metadata$result$info$title, metadata = metadata, answers = answers_df - ) - class(result) <- "google_form_df_result" - return(result) + )) } return(result) } From b69a5923d64fe81588d6477b78684e85b58e9d9c Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Tue, 9 Jan 2024 13:15:41 -0500 Subject: [PATCH 17/36] Minor fixes --- R/.gitignore | 1 + R/github.R | 10 ---------- R/google-analytics.R | 7 ++++--- R/google-forms.R | 3 ++- 4 files changed, 7 insertions(+), 14 deletions(-) create mode 100644 R/.gitignore diff --git a/R/.gitignore b/R/.gitignore new file mode 100644 index 0000000..b49c113 --- /dev/null +++ b/R/.gitignore @@ -0,0 +1 @@ +.httr-oauth diff --git a/R/github.R b/R/github.R index bdb4a08..2dd114d 100644 --- a/R/github.R +++ b/R/github.R @@ -29,8 +29,6 @@ get_github <- function(token = NULL, url) { result_content <- httr::content(result, "text") result_list <- jsonlite::fromJSON(result_content) - class(result_list) <- "api_response" - return(result_list) } @@ -85,8 +83,6 @@ get_repo_list <- function(owner, count = "all", token = NULL) { .limit = count ) - class(repo_list) <- "api_response" - return(repo_list) } @@ -139,14 +135,12 @@ get_github_metrics <- function(repo, token = NULL, count = "all", data_format = names(results) <- names(api_calls) - class(results) <- "multi_api_response" if (data_format == "dataframe") { results <- clean_repo_metrics( repo_name = paste0(c(owner, repo), collapse = "/"), repo_metric_list = results ) - class(results) <- "gh_metric_dataframe" } return(results) } @@ -203,11 +197,8 @@ get_repos_metrics <- function(owner = NULL, repo_names = NULL, token = NULL, dat # Keep names names(repo_metrics) <- repo_names - class(repo_metrics) <- "multi_api_response" - if (data_format == "dataframe") { repo_metrics <- dplyr::bind_rows(repo_metrics) - class(repo_metrics) <- "gh_metric_dataframe" } return(repo_metrics) @@ -247,7 +238,6 @@ gh_repo_wrapper <- function(api_call, owner, repo, token = NULL, count = Inf, da if (grepl("404", result[1])) result <- "No results" if (grepl("Error", result[1])) result <- "No results" - class(result) <- "api_response" return(result) } diff --git a/R/google-analytics.R b/R/google-analytics.R index ae6b6fd..b48b55c 100644 --- a/R/google-analytics.R +++ b/R/google-analytics.R @@ -317,6 +317,7 @@ all_ga_metrics <- function(account_id = NULL, property_names = NULL, token = NUL stop("No properties retrieved from account id:", account_id) } property_names <- gsub("properties/", "", properties_list$properties$name) + display_names <- properties_list$properties$displayName } if (is.null(property_names) & is.null(account_id)) { @@ -338,7 +339,7 @@ all_ga_metrics <- function(account_id = NULL, property_names = NULL, token = NUL }) # Save the names - names(all_google_analytics_metrics) <- property_names + names(all_google_analytics_metrics) <- display_names # Now loop through all the properties all_google_analytics_dimensions <- sapply(property_names, function(property_id) { @@ -351,7 +352,7 @@ all_ga_metrics <- function(account_id = NULL, property_names = NULL, token = NUL }) # Save the names - names(all_google_analytics_dimensions) <- property_names + names(all_google_analytics_dimensions) <- display_names # Now loop through all the properties all_google_analytics_links <- lapply(property_names, function(property_id) { @@ -364,7 +365,7 @@ all_ga_metrics <- function(account_id = NULL, property_names = NULL, token = NUL }) # Save the names - names(all_google_analytics_links) <- property_names + names(all_google_analytics_links) <- display_names if (dataformat == "dataframe") { all_google_analytics_metrics <- clean_metric_data(all_google_analytics_metrics) diff --git a/R/google-forms.R b/R/google-forms.R index 187aef8..38164ba 100644 --- a/R/google-forms.R +++ b/R/google-forms.R @@ -118,7 +118,8 @@ get_google_form <- function(form_id, token = NULL, dataformat = "dataframe") { title = result$form_metadata$result$info$title, metadata = metadata, answers = answers_df - )) + ) + return(result) } return(result) } From 40762ea449373d2982362018a0112c0a940fb7f4 Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Wed, 10 Jan 2024 08:24:01 -0500 Subject: [PATCH 18/36] Fix some stray parantheses --- R/google-forms.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/google-forms.R b/R/google-forms.R index 38164ba..e84ebae 100644 --- a/R/google-forms.R +++ b/R/google-forms.R @@ -114,14 +114,14 @@ get_google_form <- function(form_id, token = NULL, dataformat = "dataframe") { } else { answers_df <- "no responses yet" } - return(list( + result <- list( title = result$form_metadata$result$info$title, metadata = metadata, answers = answers_df ) - return(result) - } return(result) + } + } From 3512303be05af411e3608b607330bc5d8ab96567 Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Wed, 10 Jan 2024 10:18:35 -0500 Subject: [PATCH 19/36] Adding dummy example data --- R/auth.R | 4 +- R/calendly.R | 1 + R/github.R | 38 ++++++++++++++++-- R/google-analytics.R | 17 +------- R/utils.R | 18 +++++++++ R/write-data.R | 20 ++++----- inst/extdata/example_data/calendly_events.RDS | Bin 0 -> 630 bytes inst/extdata/example_data/calendly_user.RDS | Bin 0 -> 405 bytes inst/extdata/example_data/example_data.md | 3 ++ inst/extdata/example_data/ga_properties.RDS | Bin 0 -> 389 bytes inst/extdata/example_data/ga_user.RDS | Bin 0 -> 184 bytes man/example_data_folder.Rd | 11 +++++ man/get_calendly_user.Rd | 1 + man/get_google_form.Rd | 6 ++- ...{get_repo_list.Rd => get_org_repo_list.Rd} | 10 ++--- man/get_user_repo_list.Rd | 29 +++++++++++++ man/write_to_gsheet.Rd | 31 ++++++++++++++ man/write_to_table.Rd | 32 +++++++++++++++ 18 files changed, 184 insertions(+), 37 deletions(-) create mode 100644 inst/extdata/example_data/calendly_events.RDS create mode 100644 inst/extdata/example_data/calendly_user.RDS create mode 100644 inst/extdata/example_data/example_data.md create mode 100644 inst/extdata/example_data/ga_properties.RDS create mode 100644 inst/extdata/example_data/ga_user.RDS create mode 100644 man/example_data_folder.Rd rename man/{get_repo_list.Rd => get_org_repo_list.Rd} (76%) create mode 100644 man/get_user_repo_list.Rd create mode 100644 man/write_to_gsheet.Rd create mode 100644 man/write_to_table.Rd diff --git a/R/auth.R b/R/auth.R index 57a3f2e..e5b5561 100644 --- a/R/auth.R +++ b/R/auth.R @@ -164,8 +164,8 @@ delete_creds <- function(app_name = "all") { #' token <- authorize("google") #' auth_from_secret( #' app_name = "google", -#' refresh_token = token$credentials$access_token, -#' access_token = token$credentials$refresh_token +#' access_token = token$credentials$access_token, +#' refresh_token = token$credentials$refresh_token #' ) #' } #' diff --git a/R/calendly.R b/R/calendly.R index 341f04e..3cd0c30 100644 --- a/R/calendly.R +++ b/R/calendly.R @@ -53,6 +53,7 @@ calendly_get <- function(url, token = NULL, user = NULL, count = NULL, page_toke #' #' authorize("calendly") #' get_calendly_user() +#' #' } get_calendly_user <- function(token = NULL) { if (is.null(token)) { diff --git a/R/github.R b/R/github.R index 2dd114d..22b2438 100644 --- a/R/github.R +++ b/R/github.R @@ -54,12 +54,12 @@ get_github_user <- function(token = NULL) { ) } -#' Retrieve list of repositories for an owner +#' Retrieve list of repositories for an organization #' @description This is a function to get the information about a repository #' @param token You can provide the Personal Access Token key directly or this function will attempt to grab a PAT that was stored using the `authorize("github")` function #' @param owner The owner of the repository. So for `https://github.com/fhdsl/metricminer`, it would be `fhdsl` #' @param count The number of responses that should be returned. Default is 20 or you can say "all" to retrieve all. -#' @return Information regarding a github account +#' @return a list of repos that an organization has #' @importFrom gh gh #' @export #' @examples \dontrun{ @@ -68,7 +68,7 @@ get_github_user <- function(token = NULL) { #' get_repo_list(owner = "fhdsl") #' } #' -get_repo_list <- function(owner, count = "all", token = NULL) { +get_org_repo_list <- function(owner, count = "all", token = NULL) { if (count == "all") count <- "Inf" if (is.null(token)) { @@ -86,6 +86,38 @@ get_repo_list <- function(owner, count = "all", token = NULL) { return(repo_list) } +#' Retrieve list of repositories for an organization +#' @description This is a function to get the information about a repository +#' @param token You can provide the Personal Access Token key directly or this function will attempt to grab a PAT that was stored using the `authorize("github")` function +#' @param owner The owner of the repository. So for `https://github.com/fhdsl/metricminer`, it would be `fhdsl` +#' @param count The number of responses that should be returned. Default is 20 or you can say "all" to retrieve all. +#' @return a list of repos that an organization has +#' @importFrom gh gh +#' @export +#' @examples \dontrun{ +#' +#' authorize("github") +#' get_user_list(owner = "metricminer") +#' } +#' +get_user_repo_list <- function(owner, count = "all", token = NULL) { + if (count == "all") count <- "Inf" + + if (is.null(token)) { + # Get auth token + token <- get_token(app_name = "github", try = TRUE) + if (is.null(token)) warning("No token found. Only public repositories will be retrieved.") + } + + repo_list <- gh::gh("GET /users/{owner}/repos", + owner = owner, + .token = token, + .limit = count + ) + + return(repo_list) +} + #' Get the repository metrics #' @description This is a function to get the information about a repository #' @param token You can provide the Personal Access Token key directly or this function will attempt to grab a PAT that was stored using the `authorize("github")` function diff --git a/R/google-analytics.R b/R/google-analytics.R index b48b55c..d8e29a8 100644 --- a/R/google-analytics.R +++ b/R/google-analytics.R @@ -50,7 +50,6 @@ request_ga <- function(token, url, query = NULL, body_params = NULL, request_typ result_content <- httr::content(result, "text") result_list <- jsonlite::fromJSON(result_content) - class(result_list) <- "api_response" return(result_list) } @@ -68,10 +67,6 @@ request_ga <- function(token, url, query = NULL, body_params = NULL, request_typ #' get_ga_user() #' } get_ga_user <- function(token = NULL, request_type = "GET") { - if (is.null(token)) { - # Get auth token - token <- get_token(app_name = "google") - } results <- request_ga( token = token, @@ -85,7 +80,7 @@ get_ga_user <- function(token = NULL, request_type = "GET") { } result <- results$items - class(result) <- "api_response" + return(result) } @@ -106,11 +101,6 @@ get_ga_user <- function(token = NULL, request_type = "GET") { #' } get_ga_properties <- function(account_id, token = NULL) { - if (is.null(token)) { - # Get auth token - token <- get_token(app_name = "google") - } - results <- request_ga( token = token, url = "https://analyticsadmin.googleapis.com/v1alpha/properties", @@ -123,7 +113,6 @@ get_ga_properties <- function(account_id, token = NULL) { "Are you sure you have Google Analytics properties underneath THIS account id?")) } - class(results) <- "api_response" return(results) } @@ -161,7 +150,6 @@ get_ga_metadata <- function(property_id, token = NULL) { request_type = "GET" ) - class(results) <- "api_response" return(results) } @@ -239,12 +227,9 @@ get_ga_stats <- function(property_id, start_date = "2015-08-14", token = NULL, b request_type = "POST" ) - class(results) <- c("api_response") - if (dataformat == "dataframe") { if (stats_type == "metrics") results <- clean_metric_data(results) if (stats_type %in% c("dimensions", "link_clicks")) results <- wrangle_dimensions(results) - class(results) <- c("ga_df", "list") } return(results) diff --git a/R/utils.R b/R/utils.R index 4a1c55a..d6d999f 100644 --- a/R/utils.R +++ b/R/utils.R @@ -2,6 +2,24 @@ utils::globalVariables(c( "result", "num", "test_name", "scopes", "set_token", "browseURL", "remove_token", "get_token", "get_github", "get_calendly", "%>%", "token", "query_params", "file_name", "accounts" )) + +save_example_data <- function(data) { + data_name <- deparse(substitute(data)) + + saveRDS(data, file.path(example_data_folder(), paste0(data_name, ".RDS"))) +} + +#' Get file path to an key encryption RDS +example_data_folder <- function() { + file <- list.files( + pattern = "example_data.md", + recursive = TRUE, + system.file("extdata", package = "metricminer"), + full.names = TRUE + ) + dirname(file) +} + #' Supported endpoints #' @description This is function stores endpoints and supported app names supported_endpoints <- function() { diff --git a/R/write-data.R b/R/write-data.R index 0b09e39..5c63b8d 100644 --- a/R/write-data.R +++ b/R/write-data.R @@ -1,15 +1,16 @@ #' Writes data to a Googlesheet -#' @description This is a function to -#' @param input -#' @param gsheet +#' @description This is a function to write metricminer data to a GoogleSheet +#' @param input input data to write to a googlesheet +#' @param gsheet Optionally a googlesheet to write to +#' @param overwrite TRUE/FALSE overwrite if there is data at the destination #' @return The googlesheet URL where the data has been written #' @importFrom utils menu installed.packages #' @export #' @examples \dontrun{ #' #' authorize("github") -#' repo_list <- get_repo_list(owner = "fhdsl") +#' repo_list <- get_user_repo_list(owner = "metricminer") #' #' write_to_gsheet(repo_list) #' } @@ -22,17 +23,18 @@ write_to_gsheet <- function() { #' Writes data to a tabular file -#' @description -#' @param input -#' @param file_path -#' @param table_type +#' @description This is a function to write metricminer data to a tabular file +#' @param input input data to write to a googlesheet +#' @param file_path A file path where the table should be saved to +#' @param overwrite TRUE/FALSE overwrite if there is data at the destination +#' @param table_type CSV and TSV are options. CSV is default #' @return The file path where the data has been written #' @importFrom utils menu installed.packages #' @export #' @examples \dontrun{ #' #' authorize("github") -#' repo_list <- get_repo_list(owner = "fhdsl") +#' repo_list <- get_user_repo_list(owner = "metricminer") #' #' write_to_table(repo_list) #' } diff --git a/inst/extdata/example_data/calendly_events.RDS b/inst/extdata/example_data/calendly_events.RDS new file mode 100644 index 0000000000000000000000000000000000000000..1e355cd16ff79abad73a6723b2e80f7b7fc0c35c GIT binary patch literal 630 zcmV-+0*U<}iwFP!000001Eo}3Z__XocAIY63PYRr2142&aB3%ME`35mLgHaO4GHm5 zx%NqC$fd+i$KLkKSDYo;T0@5rmK@v1=i>97@8ruEA>^QuGeFLP*`kBbS08X}Was8^ z(yc=?Pt6>l5jr;O>wKNANEk1^@laHL6>_2S;>RKfVrd>O%W|0kLZji9^q^ols7`g* z317fZ4OPLixfGpf^j#LhvOTWK8@IPVY3%RJXJdvPgl9}C>pf$^G;`C+*qhV6b^FF6 zUVsV3lw8qZ<|Q+ax>FL;VET2hYSgIuWJZWf9!8^j{-D~f=;{1NG~Zuqy;8Hugsr5@ zSq6p3Ze3pHlUhNgCL~}HTf~5cpaKVpALC@2q?n{}5YUu?4^zwK+Z=Qyc`gg%piK1* ztKi)-XEJO2wm=OwRslv$xE`lwKZnMm)pj=M86+Ex;ifCdpqnyJS zgrA7WqZB7Rig7?aieoRL*dsAd8S{fAPC6hRE8w2}PO?rzG>(7%qJI@vZys&TmooW zET5-1u_(TiONZuP+sSjIB#b?3JNUGh8;W0ps521r2bQP_;}P>kIK_ShfCDdzFeB3l zbDzWuVJ=A+S)iVDQC_v4S=IXLVd51qZ33`fa%7a|TIM}HUbX~X)}XZN;cI0%u!d%k}p5EnQrjhGw!g{Z8-k`lQDy72?GECI19*8 literal 0 HcmV?d00001 diff --git a/inst/extdata/example_data/example_data.md b/inst/extdata/example_data/example_data.md new file mode 100644 index 0000000..e6d52a6 --- /dev/null +++ b/inst/extdata/example_data/example_data.md @@ -0,0 +1,3 @@ +# README for example data + +This folder includes example data for use with testing and practice tutorials for metricminer. diff --git a/inst/extdata/example_data/ga_properties.RDS b/inst/extdata/example_data/ga_properties.RDS new file mode 100644 index 0000000000000000000000000000000000000000..d268b25dd19533bb7ac6382ff07d014d89d8f6b0 GIT binary patch literal 389 zcmV;00eb!)iwFP!000001D%pxYr-%ThO5?Xehg%evHb|OM#bt~(as5_l^S97ri7SD zL7S3PNB{h`sIAW240aKc^XAFPdrtCRRurY6sD+$TD2k<0T)@|cF0{G`a_L+Vqjro{ zr6lIZh|!3!I3T>$1)$f{2l_x_9uUTK7sk8=26_kR{XUS0XP|*@Lu)tM8fH9x z^5Aqd_vg^C$A&9|f4AUi<~lReg`N++ndv+3cxoH2u&L&MQN<8#&&DdLs2;?0eG?%@ z!Z@oJGlF6QgY$lQ7p<<9$|~T|1|{|-Ni7IhJ07!SB+xZwN!Hd_99z;b9&yehOU=k6zK)gX*?tEETM$Hd)aCnWE82xN!J-I*q)8qI$ jfw^xv)#bmQh`dZxQ55)$@Q8+-95Vd?!FYl=83O8dU|?WoU}0utU}gm}8CXL@+;lB~V!}WU3lIwcF(Z)0 z38XoVEG>+UEX>V~&?F=h^Ad9^OEQy-l@pVb^Gowef=hFA6N@U*)Ul=jg+YSCU_l5C zvWN*;QC?zhYB5wN3sjIPGX=_G$ Date: Wed, 10 Jan 2024 10:23:42 -0500 Subject: [PATCH 20/36] More polishing --- .gitignore | 1 + NAMESPACE | 5 +- R/google-analytics.R | 6 +-- .../extdata/example_data/example-data-setup.R | 45 ++++++++++++++++++ inst/extdata/example_data/ga_properties.RDS | Bin 389 -> 377 bytes 5 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 inst/extdata/example_data/example-data-setup.R diff --git a/.gitignore b/.gitignore index 053a816..7d87576 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ inst/extdata/cached-secrets/* testthat-problems.rds .Renviron inst/doc +local_auth.R \ No newline at end of file diff --git a/NAMESPACE b/NAMESPACE index 2e79fd9..65c1563 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -15,14 +15,17 @@ export(get_ga_user) export(get_github) export(get_github_metrics) export(get_github_user) -export(get_repo_list) +export(get_org_repo_list) export(get_repos_metrics) export(get_slido_files) +export(get_user_repo_list) export(get_youtube_stats) export(gh_repo_wrapper) export(list_calendly_events) export(request_ga) export(request_google_forms) +export(write_to_gsheet) +export(write_to_table) import(dplyr) importFrom(assertthat,assert_that) importFrom(assertthat,is.string) diff --git a/R/google-analytics.R b/R/google-analytics.R index d8e29a8..996386c 100644 --- a/R/google-analytics.R +++ b/R/google-analytics.R @@ -113,7 +113,7 @@ get_ga_properties <- function(account_id, token = NULL) { "Are you sure you have Google Analytics properties underneath THIS account id?")) } - return(results) + return(results$properties) } #' Get metadata associated Google Analytics property @@ -298,10 +298,10 @@ all_ga_metrics <- function(account_id = NULL, property_names = NULL, token = NUL properties_list <- get_ga_properties(account_id = account_id) # This is the code for one website/property - if (length(properties_list$properties$name) == 0) { + if (length(properties_list$name) == 0) { stop("No properties retrieved from account id:", account_id) } - property_names <- gsub("properties/", "", properties_list$properties$name) + property_names <- gsub("properties/", "", properties_list$name) display_names <- properties_list$properties$displayName } diff --git a/inst/extdata/example_data/example-data-setup.R b/inst/extdata/example_data/example-data-setup.R new file mode 100644 index 0000000..1d06b4d --- /dev/null +++ b/inst/extdata/example_data/example-data-setup.R @@ -0,0 +1,45 @@ +# Create example datasets + +# Auth from secret +source(file.path(example_data_folder(), "local_auth.R")) + +####### Calendly Data Examples + +# User +calendly_user <- get_calendly_user() +save_example_data(calendly_user) +class(calendly_user) + +# Events +calendly_events <- list_calendly_events(user = calendly_user$resource$uri) +save_example_data(calendly_events) +class(calendly_events) + +####### GitHub Data Examples +gh_user <- get_github_user() +save_example_data(gh_user) +class(gh_user) + +gh_repo_list <- get_user_repo_list(owner = "metricminer") +save_example_data(gh_repo_list) +class(gh_repo_list) + +gh_repo_metrics <- get_github_metrics(repo = "metricminer/my-cool-repo") +save_example_data(gh_repo_metrics) +class(gh_repo_metrics) + +repo_names <- c("fhdsl/metricminer", "jhudsl/OTTR_Template") +gh_repos_metrics <- get_repos_metrics(repo_names = repo_names) +save_example_data(gh_repos_metrics) + +####### Google Analytics Examples + +# User info +ga_user <- get_ga_user() +save_example_data(ga_user) +class(ga_user) + +# GA properties +ga_properties <- get_ga_properties(account_id = ga_user$id[1]) +save_example_data(ga_properties) +class(ga_properties) diff --git a/inst/extdata/example_data/ga_properties.RDS b/inst/extdata/example_data/ga_properties.RDS index d268b25dd19533bb7ac6382ff07d014d89d8f6b0..b57e3e691eb7b276ada178594191ca4ca9b8ee2e 100644 GIT binary patch literal 377 zcmV-<0fzn`iwFP!000001D%r5Zh|lrhO6KV6Y~`8$zHn zBnYH{3opN|DCq3Xl3g_I`Onw(Kc{^c0011|xf zdC;WuV@#qLlhnsaZwR3z$zyrUFb|IFMp>99Jvf#JP#%pSLp(zf4m)DM(-%>HI2lM2 zD0W8@-29WfDP84^E})tErW=|ux0p;@*(!KzUgm}|*Jie& zE1HEC>dIarMV~4r3;x}Lr=@8uXC|_2WG!d5VNMsiVp5xY{TI~;o!!Y;s}%V{EZ0}D zLvWZ@^|}N*DMtP&FJ!TIskA&li32Cs&xhRmp_e5o$yJJOBa&BbP3g8x6jt1=n-O}O zo2!v`(h^MezKb=y$3dxGm(^ED^*ShzF$1)$f{2l_x_9uUTK7sk8=26_kR{XUS0XP|*@Lu)tM8fH9x z^5Aqd_vg^C$A&9|f4AUi<~lReg`N++ndv+3cxoH2u&L&MQN<8#&&DdLs2;?0eG?%@ z!Z@oJGlF6QgY$lQ7p<<9$|~T|1|{|-Ni7IhJ07!SB+xZwN!Hd_99z;b9&yehOU=k6zK)gX*?tEETM$Hd)aCnWE82xN!J-I*q)8qI$ jfw^xv)#bmQh`dZxQ55)$@Q8+-95Vd?!FYl=83O Date: Wed, 10 Jan 2024 11:31:13 -0500 Subject: [PATCH 21/36] Fix use case of no data collected --- NAMESPACE | 4 +- R/google-analytics.R | 107 +++--- inst/extdata/example_data/all_ga_metrics.RDS | Bin 0 -> 29076 bytes .../extdata/example_data/example-data-setup.R | 28 ++ inst/extdata/example_data/ga_dimensions.RDS | Bin 0 -> 116 bytes inst/extdata/example_data/ga_link_clicks.RDS | Bin 0 -> 116 bytes inst/extdata/example_data/ga_metrics.RDS | Bin 0 -> 137 bytes inst/extdata/example_data/ga_properties.RDS | Bin 377 -> 363 bytes .../example_data/ga_property_metadata.RDS | Bin 0 -> 13765 bytes man/auth_from_secret.Rd | 4 +- ...ean_metric_data.Rd => clean_ga_metrics.Rd} | 6 +- ...ll_ga_metrics.Rd => get_all_ga_metrics.Rd} | 21 +- man/get_ga_metadata.Rd | 2 +- man/get_ga_stats.Rd | 2 +- vignettes/data_dictionary.Rmd | 330 ++++++++++++++++++ 15 files changed, 422 insertions(+), 82 deletions(-) create mode 100644 inst/extdata/example_data/all_ga_metrics.RDS create mode 100644 inst/extdata/example_data/ga_dimensions.RDS create mode 100644 inst/extdata/example_data/ga_link_clicks.RDS create mode 100644 inst/extdata/example_data/ga_metrics.RDS create mode 100644 inst/extdata/example_data/ga_property_metadata.RDS rename man/{clean_metric_data.Rd => clean_ga_metrics.Rd} (80%) rename man/{all_ga_metrics.Rd => get_all_ga_metrics.Rd} (66%) create mode 100644 vignettes/data_dictionary.Rmd diff --git a/NAMESPACE b/NAMESPACE index 65c1563..8b27c44 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,12 +1,12 @@ # Generated by roxygen2: do not edit by hand -export(all_ga_metrics) export(auth_from_secret) export(authorize) export(calendly_get) -export(clean_metric_data) +export(clean_ga_metrics) export(clean_repo_metrics) export(delete_creds) +export(get_all_ga_metrics) export(get_calendly_user) export(get_ga_metadata) export(get_ga_properties) diff --git a/R/google-analytics.R b/R/google-analytics.R index 996386c..653503f 100644 --- a/R/google-analytics.R +++ b/R/google-analytics.R @@ -131,7 +131,7 @@ get_ga_properties <- function(account_id, token = NULL) { #' #' properties_list <- get_ga_properties(account_id = accounts$id[1]) #' -#' property_id <- gsub("properties/", "", properties_list$properties$name[1]) +#' property_id <- gsub("properties/", "", properties_list$name[1]) #' property_metadata <- get_ga_metadata(property_id = property_id) #' } get_ga_metadata <- function(property_id, token = NULL) { @@ -172,7 +172,7 @@ get_ga_metadata <- function(property_id, token = NULL) { #' #' properties_list <- get_ga_properties(account_id = accounts$id[1]) #' -#' property_id <- gsub("properties/", "", properties_list$properties$name[1]) +#' property_id <- gsub("properties/", "", properties_list$name[1]) #' metrics <- get_ga_stats(property_id, stats_type = "metrics") #' dimensions <- get_ga_stats(property_id, stats_type = "dimensions") #' } @@ -228,8 +228,8 @@ get_ga_stats <- function(property_id, start_date = "2015-08-14", token = NULL, b ) if (dataformat == "dataframe") { - if (stats_type == "metrics") results <- clean_metric_data(results) - if (stats_type %in% c("dimensions", "link_clicks")) results <- wrangle_dimensions(results) + if (stats_type == "metrics") results <- wrangle_ga_metrics(results) + if (stats_type %in% c("dimensions", "link_clicks")) results <- wrangle_ga_dimensions(results) } return(results) @@ -270,7 +270,7 @@ link_clicks <- function() { #' Get all metrics for all properties associated with an account #' @description This is a function to gets metrics and dimensions for all properties associated with an account #' @param account_id the account id that you'd like to retrieve stats for all properties associated with it. -#' @param property_names a vector of property names for stats to be retrieved for. Note you can only provide one or the other. +#' @param property_ids a vector of property ids for stats to be retrieved for. Note you can only provide one or the other. #' @param token credentials for access to Google using OAuth. `authorize("google")` #' @param dataformat How would you like the data returned to you? Default is a "dataframe" but if you'd like to see the original API list result, put "raw". #' @returns Either a list of dataframes where `metrics`, `dimensions` and `link clicks` are reported. But if `format` is set to "raw" then the original raw API results will be returned @@ -280,13 +280,9 @@ link_clicks <- function() { #' authorize("google") #' accounts <- get_ga_user() #' -#' stats_list <- all_ga_metrics(account_id = accounts$id[5]) -#' -#' property_names <- c("358228687", "377543643", "377952717") -#' -#' some_stats_list <- all_ga_metrics(property_names = property_names) +#' some_stats_list <- get_all_ga_metrics(property_ids = property_ids) #' } -all_ga_metrics <- function(account_id = NULL, property_names = NULL, token = NULL, dataformat = "dataframe") { +get_all_ga_metrics <- function(account_id = NULL, token = NULL, dataformat = "dataframe") { if (is.null(token)) { # Get auth token @@ -297,25 +293,19 @@ all_ga_metrics <- function(account_id = NULL, property_names = NULL, token = NUL message("Retrieving all properties underneath this account") properties_list <- get_ga_properties(account_id = account_id) + # This is the code for one website/property if (length(properties_list$name) == 0) { - stop("No properties retrieved from account id:", account_id) - } - property_names <- gsub("properties/", "", properties_list$name) - display_names <- properties_list$properties$displayName - } - - if (is.null(property_names) & is.null(account_id)) { - stop("need to provide either an account_id or vector of property_names to retrieve") - } - - if (is.null(property_names) && is.null(account_id)) { - stop("Neither an account_id nor a property_names argument provided.", - "Not sure what properties to retrieve stats for ") + stop("No properties retrieved from account id:", account_id) + } + property_ids <- gsub("properties/", "", properties_list$name) + display_names <- properties_list$displayName + } else { + stop("Must provide an account id for the property ids you wish to retrieve") } # Now loop through all the properties - all_google_analytics_metrics <- lapply(property_names, function(property_id) { + all_ga_metrics <- lapply(property_ids, function(property_id) { # Be vocal about it message(paste("Retrieving", property_id, "metrics")) # Get the stats @@ -323,11 +313,8 @@ all_ga_metrics <- function(account_id = NULL, property_names = NULL, token = NUL return(metrics) }) - # Save the names - names(all_google_analytics_metrics) <- display_names - # Now loop through all the properties - all_google_analytics_dimensions <- sapply(property_names, function(property_id) { + all_ga_dimensions <- sapply(property_ids, function(property_id) { # Be vocal about it message(paste("Retrieving", property_id, "dimensions")) # Get the stats @@ -336,11 +323,8 @@ all_ga_metrics <- function(account_id = NULL, property_names = NULL, token = NUL return(dimensions) }) - # Save the names - names(all_google_analytics_dimensions) <- display_names - # Now loop through all the properties - all_google_analytics_links <- lapply(property_names, function(property_id) { + all_ga_links <- lapply(property_ids, function(property_id) { # Be vocal about it message(paste("Retrieving", property_id, "link clicks")) # Get the stats @@ -349,20 +333,24 @@ all_ga_metrics <- function(account_id = NULL, property_names = NULL, token = NUL return(links) }) - # Save the names - names(all_google_analytics_links) <- display_names + if (length(display_names) > 1) { + # Save the names + names(all_google_analytics_metrics) <- display_names + names(all_google_analytics_dimensions) <- display_names + names(all_google_analytics_links) <- display_names + } if (dataformat == "dataframe") { - all_google_analytics_metrics <- clean_metric_data(all_google_analytics_metrics) - all_google_analytics_dimensions <- clean_dimension_data(all_google_analytics_dimensions) - all_google_analytics_links <- clean_link_data(all_google_analytics_links) + all_ga_metrics <- clean_ga_metrics(all_ga_metrics) + all_ga_dimensions <- clean_ga_dimensions(all_ga_dimensions) + all_ga_links <- clean_ga_dimensions(all_ga_links) } return(list( - metrics = all_google_analytics_metrics, - dimensions = all_google_analytics_dimensions, - link_clicks = all_google_analytics_links + metrics = all_ga_metrics, + dimensions = all_ga_dimensions, + link_clicks = all_ga_links )) } @@ -374,7 +362,7 @@ all_ga_metrics <- function(account_id = NULL, property_names = NULL, token = NUL #' @importFrom tidyr separate #' @export -clean_metric_data <- function(metrics = NULL) { +clean_ga_metrics <- function(metrics = NULL) { # This is if we are running it with all the data at once if (length(metrics$metricHeaders$name) == 0 ){ @@ -385,31 +373,32 @@ clean_metric_data <- function(metrics = NULL) { stat_names <- metrics$metricHeaders$name clean_df <- metrics$rows } - clean_df <- clean_df %>% - dplyr::bind_rows(.id = "website") %>% - tidyr::separate(col = "metricValues", sep = ",", into = stat_names) %>% - dplyr::mutate_all(~ gsub("list\\(value = c\\(|\\)\\)|\"|", "", .)) %>% - dplyr::mutate_at(stat_names, as.numeric) + + if (is.null(clean_df$metricValues)) { + clean_df <- data.frame(metrics = "No data collected yet") + } else{ + clean_df <- clean_df %>% + dplyr::bind_rows(.id = "website") %>% + tidyr::separate(col = "metricValues", sep = ",", into = stat_names) %>% + dplyr::mutate_all(~ gsub("list\\(value = c\\(|\\)\\)|\"|", "", .)) %>% + dplyr::mutate_at(stat_names, as.numeric) + } return(clean_df) } -clean_dimension_data <- function(dimensions = NULL) { - all_website_dims <- lapply(dimensions, wrangle_dimensions) %>% - dplyr::bind_rows(.id = "website") +clean_ga_dimensions <- function(dimensions = NULL) { - return(all_website_dims) -} - -clean_link_data <- function(link_clicks = NULL) { - all_website_links <- lapply(link_clicks, wrangle_dimensions) %>% + if () + all_website_dims <- lapply(dimensions, wrangle_ga_dimensions) %>% dplyr::bind_rows(.id = "website") - return(all_website_links) + return(all_website_dims) } -wrangle_dimensions <- function(dims_for_website) { +wrangle_ga_dimensions <- function(dims_for_website) { + if ("dimensionHeaders" %in% names(dims_for_website)) { stat_names <- dims_for_website$dimensionHeaders values_list <- lapply(dims_for_website$rows$dimensionValues, t) @@ -419,6 +408,8 @@ wrangle_dimensions <- function(dims_for_website) { colnames(clean_df) <- dims_for_website$dimensionHeaders$name rownames(clean_df) <- NULL - + } else { + clean_df <- data.frame(dims = "No data collected yet") + } return(clean_df) } diff --git a/inst/extdata/example_data/all_ga_metrics.RDS b/inst/extdata/example_data/all_ga_metrics.RDS new file mode 100644 index 0000000000000000000000000000000000000000..b9e258207eca72d0de8e05ec2ef71635465d2bc7 GIT binary patch literal 29076 zcmXVXdmz*Q_kRf85JHmXE+j<~Vj_`CR7x({r-YCwgv?%vLddO>l;svBtz4p9#+!1t zTvsZSdzjf6+ib7b@43D|f6exKJ@mUf zyMWXFzV(8Ia1Zj!h_bvGG5V`U_s8}_*m_(9DWW%O=0)`4r>TY?p3Zxcj&D)9CmfY= zSA~7>OxWT(17E$Q9kO!ARqnA>+LL#j=-ilm_ILSjI*;*Jn_cdb*l{=f?n&C&QIbgN zul9;6`{U72f!Thv>KALX#}8_9Fh`|KbkB=^T`fur`%;)YDJy)Q2p!shfPx z{QcL!Aw|Ltvhm1zu`VrIzbUME&GSj-b;;B21w!RlExNEpW*HBod11r7K^{M}IM3q$HP6JN^3Q4TGJj?p(V^mxGho(vN$WH@}+Zf#aE~}TG?4dkdP+5QEWx?~=}vU}`$d9rQ2tYPsgCi3$|HMDqeu2=8)>)v zd3SzX)Thn&dy38@&s0ZFQbvwbtEop;kk`_--!Bb&ogW?u@YJ~1`lZNJ*f&Mzz_bDK zDZ{_>xQ4g+a1R#!yzmG=!kxu@-vfP!akSON%Qh$V&2{Qw#QRB@J{Xi&0pA! z1+IG5_BsOtwI7Gyz>uoTn*zy3=ZRS^!&9G zjT9%JCO3Qf-d@>qJ8jS@XYbit1tuSJ{zv}cnAp&pNXl^YbvYq5KdYad_B&|YF~X@t z`~g?XU^et&-4I9lZO#_)*43xx0WMMb?S|0#e$ktnhi2ksdbH@z;+fBwTHc+#kKDiA z|LL~n?1S@m=~HD% zq3ZC9#FAZuoFa$fp3lPY(37MwrEA~LEdCxE4AWN(q{MnW$~4y;DLVMw=Utz2l-B5> z&d$Ax_<`^54gE#Caj_put0E)(j`~gq8;Be($H+d`e}#V*z-e~)Ub*X51P5*_Si!VB zTZ~+>tTAy3F*4x|ZgRvE)UQ(1G5o8hN+-2BLKzc}G6%KVrKWvC(zD zMUbgobx&8?%cN#cRl>a z-;<>8&J_--;j4D0SEPgFNDZy$JhG=Rw7+>jJo0w0>(2={SEH5luHHcvSrW2jt59$1 zMNx0vVSnPb@+s@$q%#xn@w$aQQ%1Uxne~}RyAB=J{aDozbeNsoe%t2Q>hz-}yI)UV zw^Hf7LVx3M$3A);W>)Sw>sPCz&z-8c_BnC>hR2~b;)L^)Ao^^BTAGB*$lC+)=Y9*9 zey*UTMQI*zacOyel(BDc_>_mx&{l)dE9JFcU;3U z;=`U>VnNysJG*q5)B2wtZn1$uDK2UMoKk6f{22d_TS ztt1nSR!!9=DB54b+8Hi`$%oKfH2KY&FB@9Nlg9e8E3>d7tm~^Yu%e zk-lMmd5N2%O(?$mM#kI@!bZo}m_?3e-48El=Y3HB6sn}GIBC|)cy-*&dqJxD{^IvP zBB|uorb)_ZCBgI$^NHN>*-+kwY0kixOpSPES69Kp$s8&B0CN}a%WK;W4(Etm?g%if zJuhZrT%SU{r>9ujm`IHZo!4wAXw$RjShp*40K99bDCETu98;FNgEN$w*skKLGl2>}A zpRQ$UHmVLsDdqJqi!5dF9BZbMgFjzT)wUJ#zWrkbLs$@tLV;_n*?`*=WDJlHnWU zt{0Llwhd`qxqRSiZ%MPar-uy9EsW(QOfsv*CJfRH3S&}lH_o*D?vJA0Sitp&-p$Q_ z+A>;oe5UGZpsGZnTkP?GEuS8o+w9fehvrf8Q)dd&4LAR6-t)l5>cFd}zX3wL@ zoa{pJFAdorc2xwP9*cWHZ;5rXR>U*%T!hVyTIj}ZA`!31IqJi!ku~gk9&2A)n~%MG zTEz7<#YB8|BI?xCZ0+t-9oMzaa&A}dHr+v!tu!@2c~J%w4gOS_3~*Mz;vyf+pE8vg zJN8H1U@rH+EXpeZj@}_L4Pm>>wi+oZB2VI$mOu_NxRF+ z{(dST&bO*sn}kb1PdAx%;P81x-1(ak-~cV>6-Ruz~8LlM!r$&ntRVL@MiH{|SG_|n_ST8Q$lU+lhhu{9EZ*r0sRDcdtX zw|{NzI~o4*y`}jJH@&MePn4UIQ@$D8yZ6xG-qa7jEu%~y=0WIBX4LYNeeMM6)5Qe09GkJ&omaf_x^YX6=YB?C znI-9URp7}ps{#qlC zwQ0eqaEV^t=is)Y-LwbsOfB=Qd-Pyc)$PNxsiPZuZkLVLemK0x?9sKyNxC2P{57mz zyn7OozcecS=ceS*@XDX=4O9~kTDN0VtiaH-gH-b~y`$5981>02L+7HN))!Ovq6g0G zKDw4b=%Ry9r^CzbSS5;V^@9KFnuOI47>VDEBx0wj@`t`bE0$l zcu7q;ePaen^S@M&Za%~*+$(=G!ds{-p}1jM$PL}A^}$$8y?a+gqIUCxSRbMCkL!=c z>D|-27A5qhS>0vm*Ut8@$1X;PxV?W#-q*f!QAdCS71a(L`Av5CHnJxz-12=}v}g(W zf$%_`m7|};U`jYD z7$fqbHzlZ*ho2wr_!X?DH^F=O=uQj0`_IPFFDHv`DBXXpw0twb{jgQ}+|TE4%O&0! z8OHg2^{$As?-OZn6gp~DY5TC4DlXJ1hdH{IX}?=Ga_ObW!rEaCjiRn6Cmom!eW9oO zmvpXw+V-t9N}NhTxZR%VUti>YxjDVq5O+}eu27N{{Zue{W`t_2D6tlW{pL6H(%}Z7 zFG{XJB;>{NWvUr{2i5ev2w~iexvG6c{qB$8+q_w#+YU3Sn%ucFH(KK7<_?f~c%N+{ z*~bm6ynpCJn+_e0USANomg^(?LEA%L+v~HQ$1C>eUh^M5;nN~M`(;LK{W{W;PHD;O zXu;RQLZ6^+VGnyH$Gq42uP(x9kN*9hHJz!Mz+1HInaIV;x<9+FrRL^-_pKc8{oHqZ ztfBLpR-Ms}PuW_RYSA)RH(J>*Sdw>S8@{Xby}OX#U}Dg$Jo|MMq2ub^CDa$XUEe6H z3K#C*wEO{f6p7i?@Z(eLW}|uYO3il*3Kw%@3Y&*CUyFB?KMWlW82(SG#`}d6I?gP2 zM3rf)J3YMcshJ_|k~5ckLaWnEZ~4R>Gp)*wjG*?`X7{d?Hn+a#gqHp7@3y}`L0-C_ zSbk#n`sv>9qJ;OISe?&imA%XaC*q=+^ZhTMTOOQz8@rACi&%d8eSW23CU3^n?zV{@ z?NOKYFDSL}NQQ~(a<*lsY7Tl|<_ycl&3)14b6uhRyVF65hUg<9q!ypF24hy6nfp&~ zE$)pp+s4oDV}ABJ%2mz1$^X7?sMx_#RXa6f*MmApSILfX@(@|qJn4R`>dKSnl~h-o zh+4(JPF*)Y20-JTkw1r7mw4+M4I(bS&s?w#lb5HR>aW%2nO_@8**0ys^XToBS?O01 z&GP#MC|3n{(c$AMntq4yL9Kp)yE)UuAl6%kPt&FM7(qAT$?Ev8=cTU?786!=c9RL} zjUs5dSfeA^i_m0WA(EG}-Ys-py>ZXc%j1^}=o3wW4%-&ha4IbQrP0?S^sMH`Z95{> zt`obYPQqUQFy^Gl8G-B*2?7uA;`}w9M4@A`hw`Uq^vCC8C-TwfLX7Fso0h*x=p^Dd zK6l`hB?c?G@CK6bTdt|`3|sLhuCn~^1}5Q!2jPRCKA#%7ElQw!IB`NZ4H3m!NkZ?G zkX|*MOg^V3i8a&|K1p%w@4dA&NBL1i%yZ`bbl}Q2g|-ueT4_pyoDVNp7vt{}O_qPu z+o=4gF4?}ytR!1`Ih3-)_tVuadV*WTU`>G`s-&M>!vND7-r8ApOz7L-MAwI4t zy$kpGf132OcT8Z%J&D(?1d05MpDzWg$~Ny<{_5;gz(uio&o;G% zRPu*O5htTnGCN+YC=m#wy43x^t?C)CoVp=!a4&Anu1Zgi>wXRm%MGeZ98I$?8wju3DAGQkMzUWQoKywADQX2CPwx&&0{MCtn*w1p8lQn%x6oU_nWi=n z?AA>t3&CZCd$VN_MieaoJHCVsCnik$8%3jpoq1_n%pI^Y>1}NhNqWj&gy32mC4%N4 zogOpZwml$*BS2SgE#ukD!e6BVu(*vll@(19&WvBHt8@Uuea^aMK}DU#K+4qYjP7Tz z6P%mzpDJ2r*WWO#qsj6~IoXn=lHy=#^HqaZl5ekQ{ak&v+Wt_}oo!4jf zvfDWCgUz3W#!N5ULQ=}p=n1RQRa@U6kBvoa(!#-p&<(1Djs4u2nCWW?jb6k@tX)by zP?SD-^7X}w-UHm3i&pFm3wPA?x$PLMdM`|Ey(93$B#88L{cau!K04_QEPjI@ zM3GAae<&-q!`vBOQs7h>cw1yV@XjG*>Le63qco5q_h61PxIKm$yo)8j6nsLhta19q z39Mm0$0&<+NwV&%E0QA7lb)vHg7_wY1MC@NzO6Z!X^2eqWPjYm*usA%03Cw;-0Yfk zmK1Qp|E6@~>hql2+Af0wEHIzaRc?%rtR|)`2Jg%N>SxT-m^BjS_!-iN;z!Vdi6{~9 zXr$+~pu{Vy3)b;-2nXy15*qq=P*ZzIJqN{ zf9l!6hbKk+;jTK1kz8dxjX}5pY(i;pu{xSwOXlKT?do3NWC? zeZAP{xnE~?jeFeLs&W|Jvy&*!xEFsLA>ZdOs){@4j7L3XHS*z(pf<>jx|G;BLd;-w z6cM=gdtq?Iu)QMt!-Ew}EZ1P|yhSOKHZ*1!{MeP}kwFPn#w&ba`wWW{BnF8u;oN3Y zoF~5dD)LK%daFG_D4cg`I4?Ns7TyX&rJUm0zI;b;z>e;xzDQ3KxC}2dmc_^01T<}~ z;Tq#u8LjiodtG&M3=zv7QiLR0xRlh^&7){h&C-n|18|L|$1wzC&$yr9%<*eD0BC#K zXQTq)dNjx?6f3T6zbouzgB*ZR9LPK}?lXL74MB=xb})9moKE zY-1vAMKMy04;E&~As%vLyiKUY8G-Jy{`}iP^mfMU_z?uJg4-3c=QmPPY_`GJh>9o0 z18bp_a7O@Ozf{F_qvK(>O`}RACrrkwVSCpmCfuk^y_C>6g6yJCS^UO4TSq=u1zVbD zI@1sZWljK+`*41lL%;NkqVnBkkh3PV)S{B(mk&};2)sPBOH|Kuc$ue!oaNSUycu`; z7x6VdZI2m;K$S#ZY?k<$h{&*f_(THnSn6ulOJk8BGgxBmpkI_N&1uq1+-v6TV*vyf z^qP5oE_NQh84g=2v8fovx37z`hQ`Dms=)F~flUhCn($s$^%&WAer6^PBZ_-L-I}H+ zNFe~R ze)GXb1_(HzR`#PG(K8aNIH~+U#a@$SDYSg)ywQ(@N&B95Lg>;Q1jw0cEB6ulE=#^C zYLaqwTyFath}o(qs6y`HKGs`%juvN3#g|I3y#ewgH(Cj5t(>EnVSVcjGGRmiG8>y^rLdcGo2S+4D&!A63BG(tk#XCS3P~4+i^o)>6}dSX-5grC z?J(O7r%IQJzi;%}k)N>s!anF1=&g)D;B*c6Nycr=s2LrnK1uN$?*c#;)#|!|RLo8P zv=36AoB^3>D~K=Ry)n{VNF*Tx>H@+_DXgoB+vwbePr?uW#GaKVoxtr4fZuKDb45-` ztPtmPi2Ku_V>ls3^wvp{zgQK_yKKl{@=N?P$WIz0Sj6;zef>R@2^xnu8Z}dET&g$# zN*%gk(}ycit`)TJ@78Q+|KvohJIXQWNCN9*{QYdWm!ce066d;ZdJ@vlU#0A~!CpW- z#}zhJ!=6giSGLnHx>a62bdpAri>UYOGeIV~>~C}gz)u~Yt7xug+x`@aQp_JAx{MeC z>kt!Ik`{x0!Pv#eA0-$v@h0Zpz&9Zjz3h&{Ui7NH=_a;3*sW;M#c2>OqDhoZzwB0} z#d+mLkUYC4)tN@SIbI_aaR!^&|LzV}E`KiK9v?(D5XE@jAre2(YE--SH4v72#QR*= zT}3z;=3}A=+h_8iWi?W7fzu{%jFga`2AS=bWAY%?NJXR0GT8A@ItE$*6Q) zVSrd;8!ate=R9tQR(ZV+SWTb4`IoU9wjC#+IVV2$73=MpcDC}u1~xF%R% zgX_)s@bRY>J}^#e8lWXEsnUozM0wz?dDpK={>kYMHpyd`0|p7Ka!1XVErBwJCTGOW!X0= z6oDOCjp+$P{%V2XGxep?lY1C2wQOwdu959JksEx6{7h==ETq6SUlM7PMlLRuk9i2K zLL2rRTnjOyqXki-!Rc#hXkj9%{+JgU@f;~kCkrsSErZz@+mX@fZ)9%NBi3n)il?tf3D8=Mka$w^sFR1Idn2ikTx;IDgS8Pq}eRJDaWNZ=(!ih=)A4Vv$O%cxhUZQ^)kfJ;%+^iS&>R2*c;~pBz>OzY8I=|R7n{}EU8OH` z6F z56)qcGJMB>NOlZrS~DRpKzh+=XUGM^{u0ZPo6Ig~)KE6Eo!Kf};ip|I$+&FULmWub z6A0iL)~}pvc5g4+bPmHQ_+KS>AP;-2VR{~V$J?xr0*s9K3wSwOU9Io4AR-hJQNl&b z_`3$XIbr7iJ3=Au1>lhLa6LJ*F+_OAVsyjcjH~y(pO)N1mMys>I)Qz6gT%e4!Kys@ z_&Lfg8{`9Q+-f9s8Mi;ZI-r@E(Y=O!a3>5t9gFIGY*Pd`>*30Tu?(`IlR0 zF+3nFXXRy6y6R(g@!JI0ft)m2Fp*A6b#il(^W4HV0dbZc4~iuFB+7=Ozau8vZ$`XF}>VClN+ zMve{hG3^+l5T%emg5J@NnmUJQPx?iuou%wZZCpTYeJ4zaDzBfzII%HfW5LIH4p!IG zS1H1JPgc)>Tx^KOs8G*?TnuT^rSUISanSp;ZY?jZR?^46y8dBT~E|wZD4`qn<542v1Wce zrNfP5s$&FUC`c_3!i1n%^Gz7JjuE^Zb~l$fCe{zvjh!6}-T+xyAOcGyQG{zd4DMJS z!)$-n->PoGo#GxHvt05GzS`@wCw%EPl9mapSeEOhNfP6o z$(lsj(G>Z!aa-?NGYu&MYCj3?80YTvxS)K{0_ZPnC=8r215i^<^b=k zMx4f(CeJ7u=F`|oOnRE;AQ!6v03AtYC0UrEvm$qFk)4Y%lKiic-C}qu=DX2nbF}o4 z+=blpB2fm)jGFg{z{T0d<75Zy<3i*q;TW!7u$~V|c8*$}LQw~mIfM{3pl{3}mam!j znCY(4Z{Vx`S-UxN7&E$|#clY^krN|F_z8*omG9&?9?lQIq!9R@x^a5kM!KMJN2gc$ zx1$n-O;D65GC?y;Cu(H=Mk=0Y9TfsK*BWtw zy+y(V7+M}ss=wUNA%#_r>10sB;p_0qL*olVtnZD-+a#%A^90G{W>9i5#9B1q=pMA)KDr8qoC1R>wpW_5$)GBcYN}0#zPMtPqtv4$S zvaRPlGFTT}wneEslktpCDsBd{x*@YIXkb1=i{FqPPg|e7Hx8?&46Yk&Du@DLggL?8 zI~G|HjCCPuZhsTwCM`E)3<`90`Yuf>W}>dOz?S#yR3fbNhR=-+AI>{zHa=Vo76CYP zbtB!eNjMDA&)$)G538I%O6*p*2ATaf?*k~4-rbD}g(iyr#=H>s6f@WU6**4cV3ZC? zCt{BCg`NVDb`l^(nv%o}=Lt~YO40*}v5PbZ%OU)DA}I%G4CBNk=k<~dR%NW|UZ~se znKI>MDH*R=Kp+R-N|Cs29#H$7-oX`2LDFK83n0ipUDaZx#Q};^W35wEez$((zgNZO z;ehJ9N@B#1qQ}W?Djly248^^e^?qlL+Y*%_VYie1p074J2YeRc%N*0n{)si#J7q^BKb%kJG9TZ|CZ(G@zT`@ zKB0KgO4`W3bmd&%y>ugRDQ0^CVO1}zHLW*WEPDXzrX9m!x6~gqf%az_p6x4B;tMy) zpx&B3WHQP58>qK zg_}RXdo!!OGK`mzFg1hg=87F);W+_Bo{!oeX5Uw-rn_$7zq0c;0_TgU{=>{qv)cY- z3-f2r>utYUQh_%>VsxejxbyZNde&w!y=81)b*mHqk^92?uixP#_CIZkCX#@JHD0R) zj1`jR%*5LIV)s6q&y9}OiljyW*5k{^Y(zVSNIG^sy^T`dj}weK2)IfhV2t9)-?>pQ zs*Ehmcb87Iy$$lxqw0c+k|x%gwsCHq{t6o1OYKQ_0&Ffy8pw+QNYBH014OnzO=^yH zJGTmTLLaiJ+Elt+^9iZ3j~1P*wz%Dh2}`I-Pz0VAu^laFJF{j@dvr^4k_p5$TIOmH z$VEhHZd@%Qs_F*pNaF+eLwsE#O5h4IszbdFRDmJl+R614z+LMUxN<5vpBciPr~zbc zJ314@@CJHe?CCkYgdLE1I^s=;IM5T|)yZ+KmT38jF}O?Ib*PmLD(^LEv{dQ3<^;v2 zILCMy>O((xMnOq{05_LRB+Tg`@+`vy}U7;UuS;|~r`!AO_<=Pi4@(G8W9+^ld z3xXFXSNr3NjWlR$!wuV&O-r~Uu^p?%xSo4$IO-k!) zkE$U4bujPZN*Vv_(M?@P7;}Zk-cg*S`R0zk&+6k>Cuk(|)(&8AcOnde9##}a#8_*o zw&!{K_p3EN+z!jgNus4}nROSwx_|hv?zobs!liy23H;rvJtDq+O_5k1N6!8BCxZ*JqxSWY$IQiY(UwI>0u*y>S|IYb_wvb1v?S zo*Gx>>*!dIL_i*qXWU%XpgofDE)>y^b%Xe6cSsR?Ptg`4Xfa_L6r?Q9t z08&xb3`wjZ4YsQ+PkU~Nr6PX*pN9|In+gG?i5VPMmCU!d{Fu*+@u4=FO~qUUJ*PEI z%>_9uiQcud7&f*a@dYJk0M7)dmb{9tRMViXn2x3*k9l2ZQ&sD_u9(ky5h7Ptja)#i_4Qh;(3Lu_6_%>9GXom2+7S0CSM z0-#nSiBcbAwJLIcL84=nG{E^vN;ygYJlj-o!l+&IGzz!7W6;&Qobr3D`rTdv$N!&W zZ<mU&yv~vVaD@z{@D07H9j+-+V+9Vj)()V7*Kx z>_rD6y%OkhDFZ!R5t@dBAKj7$M9`3k*#SK4_eR)niS)nC><9z84|v|LBp}?aM}Hxt zuiAkuFhum=D{HQI5o4~w1uMAW3CkUDfbdqbfMz;G$X-3st_k`_!J9z9k8XnHmPlg< z7_`IV3801#R>f`HP9sJ#nYa9>vG%L6Ua{%T8CA zWDc19=V}QWl@k*}AZiWGHDJ`ETYA8Y>T#U(Dg>k=XiW;Oe;Fy?nMz40j@iiAoBx$& z&PZbp!8W_eelTQ9jCCeEp=3s9d0W_Y9R-vg4wb!X)^5Mq5QkT4+*Zo=9$!1@kH3%I z=u8&Xz~M7jjX{qG@drH~V;nwX)vdh{^yfs|`TaS3IOxxjGTSv!0;Gu72ZS={1ow#2XfT~Yc}_9g!ro^20)+$p%2kwl zd5Vm!Am;Fyw+6v?BB!|4O%0PoV|4T`#?Jh&GkCse z?%&4rZbg0JJ`JgEAp4#1lVUbANe}w)XyY zDIFy-`c_hQJAotpm)GGgiZ)(?L(|EXwP?%h&TE>}aPgBzhmLuP4KmAr1t%rZ0pbS` zlUVLbtD4iixy*qg-Yy2;ng&TI`%*gInj2~PHrUSB-hiYB$FmTv%xbibf5u@s5NTAB z-L&ocD=Q2wct3pzD0T{nkGPiG26l*L1Cia|xCk(4YZH1;M~YtDbL7lc`zID&CrVd~ zpFDZX=T7Q?R8>Pg-!Ix>b(L2|)w>c2Lj9b#S3XrePPf@yjhqLp3DQx7ZIUon^c;so zHsOjcOYg zn;*)VuzLThg?SR-S^l6lt!ev%p7GQ9qG?x34-`V=W)b!6#Q3T%W&SD`HFT-`MNaV- zA)mtP=6@x`fz2H1r#%`LW`|j(V)Q|X&);M@%{#TQ={d(r>GJ}J>nvipomf_MQHcLc zq;3HtKB|+qW5g#}W@!LbQYqD7D}Q+(GUtnaZ(uusH<)~=6%$AP@(%D9vM!}ZOR|W^ zkld&x|7l%t3jKOIn^!DJBU!o zasce2VGlR!8>GJ-2-tOe;W?E86zHG%1Try|byKO07Su|X3gBMXY!q*2uif&e{bPZ& zhx~FxQ5-QwMjiieI})stmxzlJj+VVDz*n)c&n!k|2zl9{W^ACM} zA3eNDq#t=+C8`V<3Vj{R@6x|)fgaN2BvC&!qvu1dtC1<&RgOCl#rFrspeFAvuSz&s z!C^`;WM6^MVkC~mqR_> zF~kjob!~p*6++I6J|b?0T$FXHUuqvBDz>3NiFTjJx7eAt+11!C2j zQ8#>cxULwm_dMry8mX0k4#+)zqr@msA}7sW;zT{<_bZurY!qYPu#aszIb;D|neF{O z^^Lr~Zc49ik_4^NOp>~j6~8e@!xs=+06yS*m%OrN1dI_xow|3@M>b|%Dy{zvAiHQg z;E!`@>{1fheUNkZnGIGk4J>4~+H-B%`z?y*e%XZE+t z6Jw&tI0)`?+zPVhD^ua|c@WZO8^lUe9i|fcu-~`Yf(5HMz z!T#HhhXvS9BgXZ!^T_4)HjkBw8a^}kSXjcZir!zIFlYwfE;F_@AG0%&vde#7Nw0FY zEx5d@hyPe6{~n9^Z#y-yEEA;{#K(EcREPFLu$>?vp(DLvEKryljC&9J3IpSMRL;oZ zU3rQ>mOaFYItI|QPyG}2g7klrLi`Ov8C5ZSzHtzUwh=cM$G`n%Oms1l@ZdhmJnZJ(Bzx);8NmEoKA?j|YRsxWc+k z(M7SSBx{7@dNMj6KsGI4+&-3r%aDfv&Up8sh$PTRXkVsP_rFWPzO_bR0{;ZwMX*TA zw5^h;i&^JI7_={AhK+#Y+H1irySZILY{^N`O&@r!U%LyP`)SS4CJmlC!FKcZJ#dmc z-y{*&{XbKB@J52+z}UG~qHz79*0x0zIB2X|V<&biJ>m}8u%>xE36hJ9nfdZRV>%en z*`gm;m=Sa7z7}h9Dyn@IK}EjE5__ivdyfrGv+lr} z&GV)~BH7ZQvc2t#KKK$bUNR|>_q5{5HQ6W|T(ITJH9Ot!emVW@b@0C)cY=VSt3%BC zn`<@iZp{Y5xKx(O%dh=|HH58@%;dyA4s%MOHBEGmUFT+s%VNpPnvK3yBwAyoohj%{ zlXILPf69%eIV8ds`e+UDmJiT%n|vlIYe|?(eQPw)R)XL=WdygEDk(~U%`7<}Mqpkd zi`it*LBvZzPpm0KL=|TcqQG7=@JC00IW^Krs1Ce~*-jLSJjhe_tj`vcwOrw(KJDk z(NFQ)YdE6=-K!rdAO!ci=JChQATapXK3_vT8NG$?gRGAmV-5Po{>m` za#noYv?dF61CU4Zr~pO4C>}*NNR&^VY3uAv#9up2w(*(b%LdD-`k!VhwA=mM2U*$Y zfYuD10~w5c|If|wHmoAY&*ZBpAgMluO@W9Gfx~=+0+p2DSBz?8+#$HLpx=CJ)CX%K zq#=#D+&3ZSck``0C7-%u-k3mnL%|NrT5V#_w!>Zt z=4O~f&uJ7FV4}s)Unnz*q34gObSKd{&c!*09|GgK`)=3g*vw(k=f$lksu7_!v!(vf6)-U)_t7$IJrAy7H|Ti zGsq5OR`&m}BmuN$CX-vc;OkGggFWIrU-Oy-<`A=iuur_p%CImCJiv=iu5vwQ{(ZgnW`(!v z$l{Y*5|iN;zbQ|-(Zs5C5C~w@$3~zelP6)jzI_r{kIs8pCBbI`35j^|Ys%|uCMzDc znA!>?7W)u@^O_`DuANPn9Ji4KwM3gFYMN%low3$u3eS?W)8fd?GR7Od=fpjb!p!mh$JZlP^wwp)dpfzx@02t{|e_lGK=UM$GFcgWm zkKV!1PsVX^%G3yct}O(qpL8$A0DV-0>Y9%C>mw2|{LT}`_#MdS#1>u-txD#?N*fK| z6o>^{i{@S&(6COXP}Il77%W~wtr?1O8LS7N#BNS+BN!u~Aw47;!}B{%{KhW`lfhVF z8U>oTk(2?Rwhg(OXuhEE`}zC;%g{4QcT)P!%D#g7MAlx=>$?d}NtH3qwEJBcT-VDE zd;&UkOippDk>Jb-_}_Cs@U=Yf8(Ibf@IMTgusAv%I15jEtWYIFSJ3lhDdIPPA2)O6 zyLvv~(qd4QQo%pVo%727s6|!PU+}m%Gr#~0RA82FcJ)>Ek#-~ZLFPsFcd#H@oU6Yi zlARHgIqAkHh`|iV`Y8#cSf_y1l_ivR$;^WEn_YV5m-P>Dp`;=L6P^ zo~z4YSI4r-WqEYXPhQuD*Gg20+vX~-3?R8p1d-tMUcIg_*$mxuSN9d39HtZOz|hHVW%h2o;iQT_H9t$!h zEX*&RErFDf+>Y26QDdM%)C$&I|jHlPK^i)+Ulj**Wqu3Z<_A}wZf{p7$k0}5z_^U6Q>*!&C(|k+G*`Dp1NvNCtc(ioBM}}|@tdJUx-DXCRYz*S{Pf$9}HRiXFvL%T>3uu&{ zR?L=PHgJRIh;Dj~`$}7z4qs35*v%fY;|A_#_izIb;)Y|5N@)@~+?cp3qhnr|0PYys zMc45$FNtM07Sf3~vX9o839AFYPs0!&$0;kjw;{KMuAZL3a3a$LK)3ib{ zDvnWLfb0VEp3LI^0uh0tfjbH!$SVaoOIiL37~pdp2< zNN3Ol+zBK=FCxsByiwQtY7iZNCq7IQk_bZJYNLLfD9E5V1m>SM@{rZRzxd>cbULX5 zqz^E7Ohq5V8iZ)E5hH)6V~yP)1v&8%tzDok(n?7GoGaA)puE*r>LlpW)1 zZY#W8JFLQz5?eDg{L8j>Y1weBivz!l;d_LkbBya?gEK%YO!)CT%eeBVnn41SGO|2w zo?g%=;HKc{rH*7W{PL^7&nGHoDM$f;J>vHpF}I0*d@nUFYbtn$(K#hbSk7g{z7gi(t+wFBZJk3?@{kK4p z0zU@;vz@tr(em~(GghNl`_f2JdxQ}{yx1fsEU5~NoKk7Z zJTOlnM>mt;MrFKZ23qgtvcarr$$Zy^^4e{Tq^6UT#FQ@&Z20Y@u0;H{q8SC5Z2S2; z*!uvweirk91i5OO7`(Ap6EQ6gKi1e(fI(-0uO%k4teYN#Uv!LCRpBifI}2!#1ieJa z=on>i*s2xp101xaHTYvGa0Au;?&X24Dmk1>>B9sWSN<^GF?Ztmr@L+{0vZ~F5FW2; zBs6}VF&Qtr1B_hjHM0EAOQE_=Twgi_h0`%M8)g`0TQf+)90_Jm$9nf z?&r2bCGrPFh$$>zB{RzFTI-QACCv3c8sf@#R9EBiws{iy4D$9paOf9>kZU@en+|>| zMBJO%fNK!rei;*ca+(XKo)tmkFazVotF;=j;zOz_nD=;?&%IqzWvYw{1c(ZLu>rF1 zDoqWy+j&0MEw?_G)gjAc_5oxSoMcb!_ov0x_Z8sLH2$>3s14V@N%#sP)Nj6#Z3*C9 zg1;LHN#f4hXN#aKNvH-+gKQ`6seQp%M^3Kqot(L_!m9)G(!BM(bko?n4x*D3^cs{< zPLf2q?$ZoRlh9p~|B)dWO zZL(*Z$vXCZFUGzzwlR!Z&U^Zv^E>DD`-ic;=b86&&-GmQb=~*vNtcs29!LHJL@D_R z3R@ox{&*%Gr2lf*3Q0QGGynmzU;h7r(zw4+D*S(;6da8mw*31XoO_agKLt@U#kD6Qz+) zcR)ahlEaP|xCmetw9#JK=TcmZNbE%)UriX zUx}b7I;w!LbQp1B>X3@RR#QkcFjXg2AZ8@I0rCFI2m~PHZI3p-KbodlO-VI8|8az< z6CzT=2RWx(K{UJV6x~xnK_Qv_(GWZ-EDHLm`b8I;>Cu_yYg1DU0LnG(612O#1PJ*Rkr1xH^hG;D8n4Th<xF2ivcpar7_-0w!5ZI@F3E}0UE2?kD& zFVIS%@^l)%tA$(Atmx|w6fp$Kv~LjNaT{^qu@cy|2Bmj7$fWJhzy1Y=4)7hjCUz@)&m#NOyrpzq94gZop3h>0cB$abIC^#5-Lpm zZZCTP?91W`KsGEcB4|in>Xjs7agPy2;v&_sSkdLK(Il=CdYZsvQH1qeg3Qv|eL(E= zxjRHIh6gGg^mZ`-hm8{TX%SFb7)sDh`r5`c~S0sMenu1MD6 zE)GoC1BX!$xCmLLPbwckp>d)PTZ7XYgQ@J~H<$k&E6o|%m| zHa)_kg?GLKF-!)E$Z2D<2)@W_=KHE6Id!YOJw~2rV z-H`(&1_0fMxrcc@%&b;Vnr4u3yU1bSKnJXPi6Xe>iw}({ylB6fC^+peusPizJC% zI2=J>0KWjN1qia2rsXiqfRkn&y7Vipd0g#p>UKufA@~B3hrG3;15~LX)$y!i{`-<1 zxTGkU{qP!ccQ0;ou+=b_6Cemg0#RuH8Sfw(eG?JuTO(n<;=>1X0jLZ&K%qUlNo_z) z0>GNK)~!~NafJHoO)ZZq$&u9{HHsPZsT&5;4gj={W9Z{R@uzFT31odUQNT~Tm8FXQ z>}y2&dg9MJfNaK}0;LUC;FE0S2YQrDa&i*J3g+p;?ojwtV6+A_=)^t}Ksm`p0LOxy z2Xy;RXE3}lhhaD~lEU1P{D^NyiR6Uu&}1kz5o#eIjie%Ce_ORU5rG;8n0inH(=p>F zNs~|%7=7X%6HLTM?B%QKA%?N1Zue8s#TU8o_mJ%6#pe$m#*nZvkq~NqVv&dtv22Ki-R5SqF=S#tm^N{!nn4#3eV#IkM zRLdZS29#9&^g&|Of@fEWI3BO>QM^iNh}>wOJg50AP%x<^l^5hPi~)OmN&!SC>PGDoav9CEp&IF`dc-gto`Bz_FP^)P#eTpRQ?+)&Xs}j?V_= zijA&-xg!tFq{)V5UuroHC~8*jaa5OJ zpH+^0O!mAC9>_t1Gurjejo=$ZZtq;A2%6XcLt_Aip%(xZNqia5;wd1`eKT0~E#Ik+ z$qWnS@FX@;>_4S{30LOb4={f1@apw5A8~*b4t|`%^KN}B=Hx}&06E?krLTuGQi3mN z1(Hip=KL9cV7BQsKKm8@fSC3RdFj~ddKZM9*-%c+l75*Ge4FCFLQ@HW>_A&6(0*3p zFR=`SDj`?_Fa-RYqKKlov-GF(^Xd&G@e#`lKq7-68b1ms0Lq&JuI)dY-}nlJfAbcl zB>ktjRN%~oitamwZgn9Ppuw!mTfkw0rtnF$I)QRC0eIethg7ext^KCt6FaddE&`;C zF|geWfMNCyBTJ#e`+`8%UBGSVAhl~g0Sc?8yG0Je`-CCck0DIUWB_~5`w7M$0!E-Q zjUS1~``@d^-}}3reiA?@boUq(^zE3XixZG1+^i^w!r!iUVL<2%lz8mc1&9$uTm^-r zxl!5^Pxa{V!d(nWHL45;t$thJBGx%P>pC0^IuGB{{Et2NY`*p^Z}3*!J$ACU#yV}B{spTxHXb1bqQ_lnm8R5>U> zeDPMAfh-%M;M7I%OiLX7GBh3(vyW8-(!1coZ#8eQHeJ1dW-1*tMrjsF0Br1$Q~#Q= z+|8o+xSDm8-6LR+V1SR7VZ}bsh@4zXy`KRpwhNua1A8QA5X7-Gzec<16R#=W7XWw& z{=wF}(2D9jpjfR2ky61I(6G5i$?^3UHO~QU%$;*m4nPs&9Kh9kqhRO8@HVd--<=4> zD1x8E1%ibxZ;48SpX)p+RGkGjrywCX22i7b7dn|Uz&u`QT=*^M`CQr%nRe?If}-~W zM!;kppuMvdBKiQVa}hxB6k;&RC*3ca@M2xUfafV1Fur+6k%+|>g{jiIpwq0z5Q@$O zJRjsgRiv;B=Gw=DpKDnU3<31TP%I23KqZg%T_|RahtrmKVEYMgsZQiHrLg_HgUoE& zjpq;CDGyk!=$B7l$?a>jmq1$$K_P*;z*_GJ0$gV;LZuzJb%V^n)B@(^Q$C@xYX=UL zi&NY_GhiEoR+yg>wzvat#*-hAq&~vy-pO?xs_D;M`)j&R3hq1tm@Xi=yJvL{F56Lh zcmdCBUz?)fS1;k0B9T{cKAh$BM{5yIzy6--@I3sz))w^TLi7XDX$t7jl@1ypuyuwT zuf&uD(=UGyO@-zMcOxo=Hi0dc;0qSJd`1Ha_AS~=;D!8H$Dx6gn|ch3OaZwH1tWlr z+ftYCFTe}F5zRk!Zvn;#?Z&BQO<&1)-M;{jIUW6jEOjbIZv#d-Ao9O>`E?sySdB-QU??_@4-1%fN{jTL-d-T(AiFwXDDxF?m!TZ-1JGiO zJ8)RXE)=7g{i*;r+aAYfxr2o5LK8KhBt*msAY7l}C^HCT0Bscu)IT(n zf?`RgY6M~|;U&qs3#rt?DJD!-72l!i5)Dm-36WL#cZ4aJbq?s>$V7rgcMjv@06a#o zrm(>;W8SxI_wm8ls5(e3m~q zL29bZKhI6dZIwVmliZfem@ggRH~$VJP>7#226!<2GU@cre+Lwp`cw;`4neS!XF%u> zLQZZjj+6L3$udkNR49Sr0Q?{reSuW2HGxYg0frDrc8LPE5m;mgM_KfZ{lxoou)npX zpjX34vK1wCtbkUBeYv#&y$7lnwbtUsp}fJD;lW#B^`D*#%z zr=hLc$gfA?B_z&e03RmF4Ew#IC0!(|D3BUJ!Z~N5pnSi z-H%64Q{E;9-`)GX=y`D9Ti{&Y_ga7IW3|m}t+<)2^K7`4=62KD-WiuPT)Nxtgjb`i z%kKue+0_0tEQd6Q-8`!GB++8lQSSR9w6Gh?M^bp(op3n0-B>);wD8d)U}F1Ey~C0b ziaXKqr#ru48P#m9b#H3F5|O!2v6J~LL|*vTwEymEf4~HAS&yW)4pvCKRQAixalADt zU)nv)^JeA1&OpTxp10{vM9+Z7(C zY$k0c_eFsl+XbDi-SMHqai0yW`Q}eAU$FVbYCbs5`1$zeryVKA^3*<h07luZf30|Szqcba##TS zjof&*^muZAzQ{BrnDgD4QfQ6mp6$+im)8-S&Vj#1BS*K$Uli()qs!WNGcxZLxjF8h zL14GyRx;gb``t`cReuXEonTUQRRmQ&h$wwkN=)!tkb7Q&GSGA{2o7?6Y)cbVy>A|X zTAfx<0L+5Q9! zfRQWxUENtD&DH8Mu1Qj+tN29Tz+Kd9La(xMLp%qf4qsxleiYWE(JLxUYXz zL$zhAg54cy6=i7teqB(ki7ZBSk4P6-LVaDETKCKYspf4?WG9LuQiYe)`rol9!156P zXeTUryvCNo$c!74`_%OTn@V&j|8ELcMUxX&yb@WDJ35w>R%55Rt+JtL2`*qH{sun1V3_UhC3Gwduao`F3%+CGq=fW>9C7BH~=r5xraR&NpLPxDP)s} zjl*|TBapw`oOOP)F8t)DI;*phZ*d4J4$Kg-D(IOkI`I16hgd611jztx49-tjFc@H* zsLwt%6DJ!&N|+a5-5YsYM4YnbF6z-*uHV(MxKUhou%)M19H%VYV(a87h{-~VABTFN zZX3dUht@M~a0ThiWh5l)JXhaB?sD@(KyzR!8Cq+!Ccb*9??xtZgSt78tC>^k;1A0i zD=}(=BjEMB$wMl_yNmVMI+$Ld7Q-9!vu$M**9ce{zuoD%PJY@w1j))2#ass`k>BkP zEo<|l^)mI9*Drw+5d|Z-=Xh105uAMPlG`8WjP+M{?`g`S)-W9zBJ>M_3j%>Z$JV4% zv%xq0zPVQ6PIq(5%BRVWp05;xZ56)|ArA+Wugt!+ZBUv>)%E0wtnC=7SSRh;XnI;p z`DLl_Sd6kefxqZh=ikHrW<6`Lp4HrQvg8krw0GH!9rX)sR{?NQJdnaRH8 z>=fsti4$)8?oMYp=0qCYSYH8aM6rS(R&_X}|hXgRjl z8})lJV?(`EWu>V7h1pWk*LeTg%&+u^wK6IjVg19+lA$&}i^aayZB%g(`x%B&owALf z?ppqUCXhj<^(w9q7og0I(G5S5?d7=ahWospY9DYjQ5LZs6XP#(4I+*774Upf%JgwZ z8?+IvDc3E|b@Z>;HF3vX(FS0gmqmfc-W6_(?%gAHp-#Fy^7pdt0b&%5<@tt@fIB+c zb&4Iz1lfs*w5$W zUjYV3OKKe$_c5))SD)T(iD_ao+7RQ8{3778j_f{5p4a%20P;_VC1HBfkHlWy_`aS?`=Y&tIDjyJN;N z*1fA>b(?ppF{A~Gpnys7lug7Nt z_9L#|toTN)JnN0jF&=lJwL6tn0%Jvl?qKL(nzXRJnDCXN{J;#e2$3%iFn z)Rmq6T3g1Jm&bF@1gCqG5v~%#b{u^oO&G~UkFXX5J6N!xS!tt8!ZuG-E^&8b_v4#8 znHK|V75V29cKo4q>rwUA9T!g=jY_g(zim27qNYcF9Y~_ge8ljcCGxEEKlDeNMTHW!}3$!Rfp<$bHEZugA8XNTvT z5agas-TtB8n|&(IeegTfH$uRep>t#)Gd9-5=Vs=`3`74WK2mNUro;Ncd zqh@^Mg&7)|`nJf`W+QyxO^2Iw!tgP z>}keNjt*jt_g4S76|rBf4(;n+Y5g#pk-h38Te=ae87B~sQ^Z4fd)K?`nSNtKf`vY( z0M!)P!p?Puymf6_mR@(w!?Lv5BFtAH z)xJ%Dul3KgXyT(e$FBOP=MU|F7-zDO{H>7j!fb10v^K3Nocp$E{%6I2JP6b= zXJ@fK>8H@f3>O0pthjeaChVHixhmg*joI#d*Ccu~qOD!HqXy;!1MOW)o3~z;c6^Yo zj*Lj}JNTkf(6FOdT|n&kI%|wmO=E<_FNn0+ro2(pDb$P2+$IOU<@)xDXrgnq4yDAL z%lu*1*(yQDPd6uGZm6o_z+O_6Sp>CPL+4+VbU{l(Z3JzBt8Ja~<}$kBovAQT5;J%G z9IAqRW-cK1>t;58W~|?!hy&y0>$kY~gxzlsITSc!TOTu5YFaKaq<&9J=p2gdlwHOCT!fqtX_hlQ40{svO1UyKf=13INT<5#Jzeycf2&puG-f1S@}$= zGbFA~BxQ@!6dn`G9T`6Z$Wi<&oVS7P7qer>e6n0d5jWWer_02&LtAR^Ytgqc8+J}!o^GzM8 zQdOM0r+v!{>kBq|0cm!U9#xCsb(LMuBl>>Zwq_30vB${z8hs=)4hN~+`~!;oKU1um zIo8FCrMxsIlyWA9xRw57?Wf)_QLV*oZ|V9ruY_aA9No^9bo|d8>zkIm1B>l1*j+-R zL0=hPcM6T(7c(uRoouN3vcrYeu&Wn;*g{`f`D!k`p22R=R4QQi-QHndDEL0-Q5rPw zMLcb;p{pAAnCM~S-RALt~;V>WhF z@pqKe#%B|iXJMQBp+~;;pUj6$KIQ3-1j8Ne^*k*R1vM(e{-;H;U{`Ld2hX@$b)n_y ze%RWE-9e>==FAlA;mXMz?W2KriauFKcD5PPk3R(N?nqO80PTL0oZvyRQfH>n2AIni z_87>E4=ae@$e*}toL=?p?rmC=hIi_)Y)3%xWd znG~d|o6SasK5st;am~bBmE(#+eemr|lJSke&Z=?2i$4%mc6Q#ZXZ)U3wNY!S_L?r8BAw@}Kz3UR>~o`U}{Y#Y*5em))bMO)}!&zMOfWNrUFXhY_Mj|8E#J=230W zj-b--xEniaQMEq_uX*_1WD4|$pCckj+t2E=2yy{Dni2CIP<>S+v zG!k>Mli$y9jB3Zu_fx}sIRct8-)8!&kHP)MSn`{?i618=gCIFbEAC|U4}R&{WP0Aa zsPI6wtii&u^K;05c5;R=^jP8RqI5h$9VrF`Y(&xEs)T~?VNS|ZW)v>(%7(AbyhdXv z)ImVtqJkCAEzcn9!=HgJxsym%w-@s|eASzKmU_5J%>}6PYhHQ|+56@lkRO9x&2k~F z>HUy@@?xFzPAzK$^(}TP_$@=}kyRwi#w2+1x~Aycj}+SQ?FO2!vW6c3+BVP>Bf+wOAz`K9DEb9Fk1JGNFh!9wbpr*Hrh*U@U% zHe8PGYiGcg7KHc%1uZHZG_-YV6ni)m_da}R8m9o|eaQnXf+C`90!GS>>~+ z9i7zvPe$Jn*I6{?aLj5!1%jQD&8_!B*zDe^KY7FGpcsoT-aZVC-ZJc4I!Lbr1_h7Qtki5@N|<7+6pz+!w|Bqb#beXS>f@2t3NJx9Qp1D8C#BD$D|FF)+RH`Q zwu(zheNN)TEr^LL`hwB^nB;_-`#;h9ezhX%MwemO#Ub1|WlOIGb%d1M&rjMeP65gj zQE@xj@D>m{aq`@jdpV3>Di{yj9v`=J;+&6QUE-WOXct%6Xf=PvB40t)1-KXljiG<%R+_bOqH5@FaKnkiT z&W=5Jht4Jqh?8svZ0(C~FJsTW6It>yvW&25wg`p%z1fe(I92 zqUGZ`Q|vGt%k%7WNSe<-d)A5#62sHA1xkJ<-=Bh>Vm3E+cVoTVkDfdH8=@yX(;g_| zYL1&^s@KzPLyvI(N@)J^6@vzCnY@O{3LI-Pqom_L?-P&IIpB9*N+zEsfH&+btHz-* ze)CYc$UJ24ZoNnwj6JTVqfu=zA!4`XeVy3G(o1FEdmj5851c&0-s!vpFaH^thx{32 zc`SXV#w~FmW$bVU`Dg0+vBZsUR`pQJnM+?=z@NmHCE^{0s~>kOkL%dzYDyPvG+Vts zIK033v}rVe_qy+8*+_&^?CB)gb-g#@-(nSFrW~D{Bk1TJ4m0+KOGRR0ZZr-I`etB( zk9}X4<$QcShA4hRF<&z+;=QHz0r{=^kyaBKy50m$nZk*m2S4K-ee`z9@9@dM4^f5z zX@xL&mI5xt-IRuh2Ip6BN3W^qHY#D{mwDn=WV`&jmKTkk=DKXzeP=pD297u37kkKVk+?H^Lc z!L+seZ-xpSnDpLpufmnikVUy){l7&|pOY34%S7#2P~+x&d7*^dq**I+W$ri3W-^n7 z1%e>As%@`M2fNiA9QO9yhE!kf4v#6h7@F-c4D*5%*373uk}*{QjN5kca?{xT>lPr> zDM*32=xegtv6<>_c=4*2C6Zi|f-nvWVub&hU*)Ap5mch*?FrmEBwHJ>YDIg2C>_f&h~90`G+^ig84ZL{T^v+>Xqbav*3tc zWai)-Frjy4KmB&nye3O%*U2DA%JH;=eO%ci&TFLD=AL27KvXhYCjV#en3+CIK6`WE zu&DeDO#alU;GgU2eQDu5j?(V@oVnujScX(?4_QxH{Y5*l)YQhqIU)>mJhr; z&4Z4L+w2HGCiUvfz*PEryJ)@L;J5j8)D0?D_yz6>hu7XUV&*lcodt5u*Zq;2bhK<0)FY)t@Q4^kZqG~>@G zAKji7K$|C`N@H_61|FwA&j&F+GwM*ay5il=cTL~!&C=Sx{Sl<=EFKsAVl`amZZDgL z@g%27hYhP`w#eE6?*rFx!!UZ2CpN)yF7ZZnZN)9mekh6nfvfgZeIRQ2`e!QG$+408 z3-fE)P_OUGqGY@X_e!~QM!>x${XT{{gKyzfUq77QZIUm&kskha^rdx_^v@+MBD#f| z(Dasf30cR64=OWlGFO>sagE)VGaFmwUV?$wsXX~;<6ExXxG(yj0A>T8SiZDV`foHR z{VHyGVN%xr_pTl5@YjbCM-ps)7!Ube4A6cH&1YC%4Wad)V9*F--_U7$*wDSIan-@u zjt=*>7|9=`Ey155m;H>O+sM*4>}1VXy7}?$))i ziMH7v4N8BJq5)*Y=o&gM{x_niN(Wi^S(HNP2wal#Au@Sy8jQZi!o}af?{L@G57~AR z4;+slul%TKw-sH(c5c6#zae{Q@kYuDs9<|Du`kV{BK=81nd1+eUE;@W9U?TUA?Z#9 Ig!{{k`D1C95}?HA|V+eVc|IGq-E#P zqYiBhF5(dr)~aZ#+o|%NN@n}Cz-Qf*Ba>YAZdr8XPKZgdMfa!EUq3ZGQrKLvF_LAn TNP~ybA7%!z*t1Jk0xbgoPVOxP literal 0 HcmV?d00001 diff --git a/inst/extdata/example_data/ga_link_clicks.RDS b/inst/extdata/example_data/ga_link_clicks.RDS new file mode 100644 index 0000000000000000000000000000000000000000..7e57e4d7971b3d563c0fe7f116f220bbb9c90554 GIT binary patch literal 116 zcmb2|=3oE==I#ec2?+^l35jVb32CfGk`d0%cS>{{k`D1C95}?HA|V+eVc|IGq-E#P zqYiBhF5(dr)~aZ#+o|%NN@n}Cz-Qf*Ba>YAZdr8XPKZgdMfa!EUq3ZGQrKLvF_LAn TNP~ybA7%!z*t1Jk0xbgoPVOxP literal 0 HcmV?d00001 diff --git a/inst/extdata/example_data/ga_metrics.RDS b/inst/extdata/example_data/ga_metrics.RDS new file mode 100644 index 0000000000000000000000000000000000000000..ec06a6bc229b83716be0a66468dac6e75885453f GIT binary patch literal 137 zcmb2|=3oE==I#ec2?+^l35jVb32CfGk`d0%cS>{{k`6iWDGP8nWQwu*Dd@hMzJ9`_ zh)F9Z%$YxV@~oIivla!+j5>7Sz@e5U9j5w*=36Cv)vNQeJOk5;40qjnGUrHA$<`PJ p?Jt+Q8jdZ{e6Dj!|9Z3JdF~@Vybh1-1sGP=GgxgmJbwo00s!r{H=O_g literal 0 HcmV?d00001 diff --git a/inst/extdata/example_data/ga_properties.RDS b/inst/extdata/example_data/ga_properties.RDS index b57e3e691eb7b276ada178594191ca4ca9b8ee2e..3a8929016145ffd897d7c446512315a798c03c3d 100644 GIT binary patch literal 363 zcmV-x0hIn9iwFP!000001D%o4Zh|lrhU>s>;+B}XEZakEZN0LPyh3^{m<#E1OO0#C^Uf3WGQHF&~r~^T9<9wEIKS2Km;we z-lsH43C%;24aN{EiaJ%N9CPPmoD^}M4d7HAL3J{L9PtQcIPS^A-cUxv@oc2bpxmD* zaQjd0x^h)=x`Yh<#j;I%;c}I(wkI^i{=iH!Pm6aXA{mDWoAsQNo))&fFbz+$G}A>( zeeJE0X3aH+2mcQxfNO zznBs~Cn!ACr6LW^m39zjY2=sI&n2BO4vH+NrOwb@Ld$xrIXkvZ;+ng`1!b?{Qcr@D zmJxagF)_)3M3r_`bzdX(@1Qaze1kahGk)?Cv+VcZ=+WUh-lEBSzn;nMSK&K8J^}V) J7*xjs001QDtJ(kn literal 377 zcmV-<0fzn`iwFP!000001D%r5Zh|lrhO6KV6Y~`8$zHn zBnYH{3opN|DCq3Xl3g_I`Onw(Kc{^c0011|xf zdC;WuV@#qLlhnsaZwR3z$zyrUFb|IFMp>99Jvf#JP#%pSLp(zf4m)DM(-%>HI2lM2 zD0W8@-29WfDP84^E})tErW=|ux0p;@*(!KzUgm}|*Jie& zE1HEC>dIarMV~4r3;x}Lr=@8uXC|_2WG!d5VNMsiVp5xY{TI~;o!!Y;s}%V{EZ0}D zLvWZ@^|}N*DMtP&FJ!TIskA&li32Cs&xhRmp_e5o$yJJOBa&BbP3g8x6jt1=n-O}O zo2!v`(h^MezKb=y$3dxGm(^ED^*ShzF4iwFP!000001MPiVY$VB%7)Luh?>A>=cV}O)zn z4QHq!XIHC*Koq-*-F4I5)vBr{hq4XHKKKRu4)+55ko{x8@cGB^88!?9_Kyu5;In`H zYXko9$DIM+eH?uLFl^`?kBqF$dNi9&&a7720S4^KM`UDXWJF|SWaM{GjE#-GGx0>?Qn*Ip^U2zZ*L|b{zhmb{nn0-|{k4gaCr@m1Oh*L%_p5-X+G``C}-AnfXTeJ}94R=tao_SlcJFwbp7 zZr2OC&82SQM}92rAKi$;?bwf`p@!S(xk0n55k4g;2C(CemK(W^L{~f92$CK3`w0Hl z)sHm7Rv78t^4G(pwdhLuGdgWK>?SQ4Je~T9?>|ke1NPnIUKI9w8W&E}EuK^fYdbyP zEE9Ag(JVs=y4ul3*zYFMPFjUNjdAEl5e)9mLg)$H!=9VS?_>D;J`B5bOTL$bZeN#r z$tjFF-rZP$PblQvK1GAQa8CYltp8ANfi>c?1SG41F$r9T0hS;*4LrCgQ)?+u-~SlXpG zb(wo%*lhcCFRnLeCJaEY7ZmacibkXe4i_=tyj@i2p`UDr(NlwX-pCirb^NXJGDd>~ zO6}5KlxBVUHoD_H^U*J1^UlY=+I(yme_T6^fTi}J2 zqXW)g^P`T+I-6$sUeM1cGt0|S4JTW5Udd#xwB2Mg)GeLd?6=!1Zqt7f3AJBrmXpqf zW*Nm?%;p%iE;k3!$9S`pEH34;45<0cVxZZ%H?p!*ey&+O(`TAR6MVF3jli*17@K2t zxRo-q^Fa)gzr_;h&Q?kUM}s&tSQvhO>bsf;s?Q~>(AgmIJM~RqHv#la^Pl6Gd>f^d za9m8taJSQD+FViMd{N@nqQq+=qA*bp!wLL)5X7luI3+12bCk4|Fb<4PoT2ovK^H-?o^H#C}@FZq@>_&l`%D^@&1h{%s(iJ0V zrQK(0Sxq8HlAb_`W)V(r=>g`>)JXJmNyq4 zd>V`IY}oT77m;1viDCAInPrk~wem_P_md!L*(FW_TLJkFoFmm7!hW&=oK`QG9uR~q z0x5%OE3U@<75OfvGXnA0-}FJ}(&K??YeCYM@i+^(Hh={_6kjfR6RjvTNu1XA0e_qQK z$%wK%jGRP!GhZYx*sf#?<+}PtSqW3^D=RawrL54)m&nS#SF$B+5qK$^ZA!w+DzjM8 zIdA0}Byd_XEQLiSuDbKrXA5)*Gdnk+uNLJN5D@39L9+IfubUXu+aokF@nz&?TWmLvfaVzNcfYq!8nzdYr zVVr$$sf+v$mCbbj8k=pNit~+~iHlk}7AG)0hFZbjrV5e3E*$n%?%klB7%g6~)6H#L z0-`B~eYYv{FZH{@X5f1(il43$1Cy~fGOdgYDuWYAK4+?Mj?wy=K8#DfiSn@lLQwjJ zsrUS@r^oX2R^a&|C?WV)+Z8C$WZuUpB^B%ATS1h-`dU!iY@_9)TR|Lb=$ePOQ<-;o zClz?d@wX`-k6{7{NZT-Trk)yR9Id1suY1m>xs))$n*F1Y0HdQ7sCuvGY+1tbbX{3s zoUXCtbz-FKGG^{^G04aS9KCH>WKIejNHIMoNdjUaYmQk+%`r#ucUt$zf@P0cbc6Pe zW7%lV&=6)*Pvbb|5*4F#nuZ8FA&I)pQ<9PxCYgndN#>MJNGl#)uxv6X)TcQTidv-O zhMnS7{Ox?r!EKB)@hSXUUS3?he}7VxKFX(DQ1~26A$%!S%1**hC){)lqC00KnNxX5 z$#ti}g54UHv#fnM4O{&szy!d=`fJBs@`hIQ&P z=ip*>8aaX*FUVC{j*;aJ=-#_VIhL^w2wBHvnPDRNPDx3R;iXu8b(ew$%=b!*c3G`4 zNv{Nf{II+=xZq$kHMk*KR)RcjxvZd4deVX=!~T?Iv6G~`sMm{2{=g8xZx}^*H-#hE zu#}<8`7(*26q~u0&^IzHo_Zyh;mp`o3rb`Z{L^>)?Y4uv1Lw)(2cm}!rozhuN$=HN z=OHwxWwmtB;sx0`E*(zGKMlW0+@rTV*qO=ayh&>bN-r(g{l6Ff2%?DJ8>k&svGj zTygSzaq`vTWHBWjrQ}rb(Puk|4ViW(O*1)nn%c81O5EGF+D#kk3Z)w$-k^k@7=KdY z7^X~|nwHLI%A`b_$uQt@N4G5d(y0e*N1=r&Ovp?F*-TXC#FmBWfo)+rE#E*^sfp=< zVPZP>~Lwzg%1(OM5C;M$pkt)2=i^%XlctK??0vl{NIvRMJY(hThKDjBVupv&BqG~{%nZyC$Zt`;^c$(iPk zuadDU1$I&;7?Uu*=7X{VTTIv4BtunN`cf{-+{&({My#~Tm6U_yW}L0?EYnqq$ub^a zR^j!Ozn2xcQe4Eq_NBU3M2=Wit}`E_`3kim)68{}Q%#|AoRd<$;?l=#=yJrN#BA^A z$F`}olQNT&K`;R@1A_O4#cIB>+ur z?2v{|=`iRKl*M_h7=YFTuToWw{Mw=qV;5}rF;&}kcYxW%Ah*M(01StAyX8kdX6^uz zkrL^?S9R8QdO-t5gE>YFxHZ~+&vzfpg|1YLQ^ zJjeC6+-}29vHA_{x>(#q=CoWikVIR67ozGKC~#2&g}Mj2n4?Zcu;mBAW)g00;?KKo z!{31ZIoi6HmLsfCC8V(gB`n5?mVnRepfz zjC_&^pwq(0P+eHK&@#;D4%xAKy%+(-t#F$r0DdD^h^66g5Ha+6l~{B!G_yJr^{y9% zfj8*@ImBq-b2dN+gH<8>xQXD=>ZCyVeT344A?KxnTQr~{8HcKa26VJk8q0EBz!c3`4ls=-fM$E2vIcDUEq5ybl)VrjcEbcW zg?=M(VEB?SqB)fof$w!T04?C7hztYQoHId@B?6pvVLjeVURESbq6F+@2e)K$p0E!|p<(FBt6>Z19O`UFVJBVwyqHlV*uqSp zEzsvkIH-y~@f$yEMs5!(p#)RIwijqqutK5$`94*Eq5O&i|Lg-*czsr?t=Fa; zKWWGq{AWJk*u<_yew)QBEHrE|>lI3bA>$qkS`0SBAp)aQ(4W{xmI+9a_DXCCM~?zjvYEnc9etZ8iOKE z*708`wgA3@r|j07zKR1KI8YPIRvn~1o4)vgxMOk7;EcFn9gS2Ko= z#;0K9##;5%2u!LaEbn%}`VG3=#Yn)t{zf~9TfW}U55+&Z=sGb5%|kyfmS}poMrgoL z;|aR%&jG;3y|{+{DRWji~A4Syl*bkL$IN`pO-PgWOs4)5JwsfcC|@NxB9QqFBQ z5o1Mb6+xtu5?mE@M-a9?9suw%6i*q_VME$^g7Ok}eWM?czzv}uUew8LWE`M^(ST)o zLo2-Ab=x~h(1>}{c4-l9#fiF}mFK%}SCE}KXpcEl$_Dt*M% z+}Q}wthD8}L9<|$Bw_mqIZG6RxPM=O+#djBiXcoG{uX7>mX`(I4ep6%M=jWMUV)x zIF*SxaH`4gWH6>RCNiB;s_GAMDMHcjV7(6lKW^t9MNPL0Jc`dNFlEIJ zJZCj*DC3MSpi}O2+<<dxrt4S-SxapW_QW6q%sL(-%moId^A$P{PeU)1x4siw?&35 zXW4h-e#B-wm{h-u-SmbLz=*-RMp2LAzoJc^Hlz-R`3$t*k7s}jcKv7?KdRW77t;rv z1daY}&DBI6!W!7Le4fGvv085zR`*wzhOi2-C}H?+;|G0M4xVoLAot!semKw+@S^ZI z^g4}iAx{~MLZRc^&m|us3u@gslVY)B4E?~7Qcyl5D4wGnYe>)8BZ!YI5D%4|Vvj)n zi0r{02$UosYlUSdFP-jP=1Uk(DQ+_1Kjt}+m+GHQ5D8~dkWKk36ZE1eKYp^2f| z3sD%y)7`X^o^P78iGDXgA&DBDqVY*RgUk7)Z;aYrSDylU&s@R#26yPUc_U^VTNl6{ zl07V#U{GbiV`Drp~IU(%fe8cZ8SX0QyQP;v_2#fME zO_G(&CKy6k=;Z@7(^EnWpb{(d^b}7yA$J7B?(@%s#u{SUl-9+XAw!^I? zrRL1UJMIQB5slO+bZ~usE+v909W63jTd}0mz&tG3Sds=So2eMpM(e_Og+)T; z|Iku7f9Q%khV1 zO)@g}d!*#jS}yQV(_8ost|YZBlJ&K{ysA{tUp5ssEuU0 zAAge?UFSwWN}@gqjlI{B9{$#@N}8C1Z5i6;^Tfp3W{dS|A%uNJ+=Fd7*?xr6RGF3a zkfX1g$3;eK^_3+-ZCL+_Np&u7IDi#QMyz%469ycqPl}N6(l1{roa0?!Pg)~%cwWq@b(cfG(TxpNCUGyJp zHpxfOHkgxfK34Yr>A89836wCs&4h+}AnQ&!>}tV!l571bg`CGLEWK(R?D<` za_unX;T=BpLE5EFJ((5L0+5rKUSQbXhhz_H@@cS4Va2*1g`miXwbz5i*9?`?h5n6I z=TjtgmkZJHKBMN^sF^F-K97ol?&Y*4@39_5hNLip*Z!BgZBMybvt+( z^w8z98b-|-{F;FV))CM;YSS=-Op@tI4|{o{TM0Ukyzg9_7u^0lF?)6G%svBnE;!81 z&dmV=@JFMtTC&|y0hbibQ+TBTU~AW}G2q&b)M?>Y)6))ntUI&X3V_yT)}~|nF(ri%mWC@$ z3Q~XTMI(iog%~;7BlOo^G)|mejMe<-hZX%T#_Fo2V7*AJT8ox}tF3hD(7^xf!stc#&H zKGyn7NzE1VpPvOU7JSx8pTEmoc4=+FAhue6n{LR~8k;S-zSgfnt-j%#4r!&&j z%b;kE*|>^j$a$ami#o#B=X5rd?4_^KfRYD+#4HpmhYYuJC{Xtc9Mbb}P4gg%|Biz6 zpb*joDvj6YZ%qyfv*LHB`*B515!0O@)M1j+8^K}repE#^euvH@xch6%4_I)e_6gZj zO58pNBP?1Xjs0;sAWS=GJdKI}cb__yue%KGqj&MDv>(0ev?71=qjz!Bx5Gv}-5yq!o8>2(zwKo87e92nRTC+I*)H>Y_UtmbUQVHs^4jIu9g zFu1RJEZ)&XVj80hDFmsP9E8MSX!x-SypP_c1>43b3-PE#V}N)ugJqaeXa~}kpbM*1{Qx^CK$OdA7S=tt?LOOaR%^4^;>!H2vl4}q zmikoWvzi5?JXgPqKvtKh@7=mKd)LOJqpmxpL}~6-#GiA@)}NXEUfOyTYd)FnvS-f zmm-#NaItMkstu<$o8HpLk*Lk`Y6~8%I<@)gwQ4PsPia+iE0IGD>+2JZl2#Eb>GKT% z=U{wyy$huf;A+du;hU;sV?V}n|K_n;%J!KGsT6JBCE){`vLMknT3vD(Fm}AessHRskhM#O>6crZeJa+pVFAQsZaATD#CLQ{(B^}xf+wE{$n4<2|UVD%UbpISXRPGq{ z`+B>;!O##7>>?i+<+_NohV-rkcBW2j8aT9&$1JR;40f~yQ2eeE>y37#t|0JtJTw(b z3^z@M?_-Z-8aVKdV!WYJ{>*?7|CXkhqr72x+!N_ZN;OZ~58<^e2?UqD5_4N|0Ws z$aqc`QH=ndG^=3ef}&@kNKQr8)vXNHhCEc_82bUpSB-mGBB5qf8HwXe0gjA&wEUjK z0blf8B;_O<#xG;!1N{o+d`7a?Zq3a;tIgijYRn(${T}aejjbH{mu9x-8hc_fAX#Vd zTz?uVmOL)L`Gb!r0U_r1+@e}n(lXDRaMjM8+fsdQ({3bx86xo5Xrb-22Y9QhThgZH z-p{4gKOstCxW7<0G=Vz@ zYf{FpP_FQHi+9O;ah)aizFqH474o73a*{pF(r2)ftZwz^+c$QSW#Rze*rK!F>_sV6 z+>qLPv$HR1y=yPvicRYu8m4u%OfgU;-8SEikj-mRTJ)p>Q?Y)eDU2<<8$bOh$DBt1 z^34LKJOY^iMI)>U=Ynqj{NoxdCc9)4s97v?#-A1rZwo^0k#E5`) z%&8~=G}@%_AW=M1_(GU!a$e1vYFoJAD5Uk-0fEbOrqOHISNkn4H4363 zL19``Loe6<_JgQl7UC7a(@?P(l6lE;?+oc%q)P36!P0tMOf7Tf!GYyt(+8*$9FwY%7SPLH1}u8wCCwqc zsaLhMw2m!RvKw(h>hJKm3g2|tfjypOREX^u;$-uL^l20%+{a?wKouJ49zbn={?=^f zDz)1B4t+}f^Vys>>2aKPHsG0&1rbuwDVhQy0mgJy zy^|YI>5t8J?I?z>6)zt5`Q)C}#1lb0yTiLMXnQnDagarI)q$8L|KxSR1#$ccArI(R@|QCDPeY>R8E;lfSqc%CdUj3# zm2l*7BSOD30VFh;+KE$3qu~&u;C*Vt%bhjtyMZO$0KXNmZ-DoA0mz;n!^6Aqgv{Gb z@;t%UIrT>P6y-GO=>kdn7kL$h9^=fOawxZ~K}Q`_p|jz8sEZSj_<*d)1Z-X2SI|8B zZh&k{(uHP=`F?5MA z?x~QNXrW*w1;3i;G%?6qxuTj}mnJ#5TbbVSkBVIhWP2a*nJbLP=QF(2wDuYJ6+VVc z-?lTItxCgGUYoBvcM7gMatz3-^DPwjz{hGaUr23lqo)(5oa}?js{OFb%a3rb)W`7^6=Qn%g!9HLX4V6Aho7Xc51YSF zE&3ffWsP?Oa13A1@yMi{F2najYk>hfmxcu^|%fh(;tM zyB2f~;&;KQmQG4(#>;n%cRH;}u|7_dGJ$Vbjs$jJ zlg}g3YITHOWF`7=RQR*N>Ay_fQp~uGcu2Ibp0{h|i|40QrA3WGmsFv`OhH8+7wog&s|+*V%4_(K zk==_#f-zqUKMwqDBlUQrnHg1aU&kgkyBdVzRQO?s`)17 ztVryolIrII>cVEf>sc6`402tgqH!Hhp_p&Tq*M9=lCSSQ>L(^*F*WOZOI<`!mbRUv zQ50&SY#Bn&L@35i>ZJ2VlGVUvE29v8ZnTbdd4A>l-H>mZux~WGyVVt|P*LYp$=3=> zT72@WWd-un@m4|6Jo7u-vu0H)tA!#!51~58$@O?8s}GoWm9?? zbM9zPosnb5w=-hlq9JZRkZBh(E*#cusn_$Uq(y8k=t6Cp-Yq0y$niOZb(!=_1qSA^$enL)Zz%(hOFP~o?41#crMI|&z5JZ?;mEoPcWQ5u!$IBwu~EVHpYFW*_{XIJI=fYp@~Ywj~U zk#S=FmSu8)q z(aYUDU7|d$fra;W*|au0d!sf#dre<2UCHl?$dOjzL|5S1U0I7_iG~?&CO%B^#s)@c zBB$nsl?8!&y_0z28_Y@J+=(Tcty%^bRChw@f(M4jlj14> zEM4B{cRJ`#-SHE;N|inZ#`_ayh;qG9d(;o?b{30$;4DuS?FZSvS2VU}i#WDVCP!+* z!K!@yj(MfB7x9u2lB#>l;f8}3v^unGaEW7#55e%7Ih?ZZcP4;*6ho#U@Wgg2Y3x%~ z`9IPPAYVr!gOXB&$8sC}=J8^mIvH>#CbZ7D9CZ7!ligoWnyXdHPO8{#>Bz9@(D(Qq zsm+KS*iaW|^wp=zhTjaj>jI%-Uy=ROqWMtFjZeaOcJNMbLn1ZkHM312 zK#liH=t^^F*2ZYEpkmG7HHH~=MZyeTCTlPPI4X(`ReYGV=n1QjFuH3^g)B?wuZRRD zEW9TR5T^?wa`oaXKP{3>1tX9n9zC^WKf=O3UCPEjNbD3+C7GO1XA(%a{f3K|H~E`G zxp@92UH(A93~HZ?FA39=!s_`Q)JCVA?73uC)6X4frY}EIP9;{nO+d=#0cS&@iw{sZ zc&W*_-V-)*wW%VgmUK@i+3C@mZczJ z_k^fy=BMUYy36?UQ))$nhQ`RS+uq>_kZPvY8)d?FEcq{^nhlkhmn|7e0*Rl zcww1B3o5n1o&^>~k>6%*2KMnf7OLSyz}K2AtclA5O#sPJu$xekSplH@EKsb??6512 z{L6d+sn2nLvRB&UFCR5L@J9{*xQOT4`W*QWFm0l+%JP-M_>XE02OPg1gFXd5cZB$d z!9XSYeQX{LsHgfU*cK@D7&M--keCI$qUb2VXL3*@tb;<(#nE?RM%Vpk4WGP!-jz=O zr$WIGHBqW@Lwdr>x;>dy0t%V;sIvan+|_v{>u=3fug;sYp5&4~1d%@d9<5~{eG1jf zdKy-mpUoYOrNRg`g#3BiJ`@x~KY6dTb$>w9syF|>q&N9oWoDffP_dBk(@MmL`WxC0F0(C6Y!q$1 z9t~4~VLR+LMQYY&Wye5)lv=K5^Fno(SY^%WhZR(?0J6zT+y64ZukbVb1ZN_J=`7zy$hEqP@R>H~APl@6|-9s}~O&^$9Vx@DeQYpcq)cZDg( zx@BO~(NZ9%)ppdrah%>9M?UH~71A#C)%<^&pS0YX?6{tZ_S#%&;V)Zm+UAhp95Vm(Iw~FVWux0DCRg|2uRMi(#0qK-%b46{C;xA=RWJSf+ zp%gbA46fXZf`&w^cR|}DE2vpbg@9E` z^HstEgf&-^3*vkVtChWZgYv9_&-Gt)~XDs4$A*h%=?m8z3HD72q$)KMpBsbyZlfo^LW}g`U~RiAR2UL?G1dDQ$UqHf&422M4PhM%Ui9moyK1{ zi9T&AvoEv`Wj^3--#S9f*be@n%$B1=mT_wnaZYg?#(JZwj%=H%UA-o@sao~wwKCHU zc{`9kq0zQbkluES1|Cc5$8+fjhT-mIfUyP*?>Yj>jPd^-VvM!h!H5HG)*o39o!7cs zZN7SQkfBU@3FL=7_txn@$UBnQ7H7`F>V_S@l);xnGZ=iHx$7N#h3jT!=+8<-i@{OJ z5<3#EUjQ+rhG8p1s9i+Sa_W&V>CjT!dcZfoO7zex21|Uk0?^iza2;V)GK$}JQR{}^ z(7xL)G@`~OR$SG*?VYr56!MH%xBr)SY2SV$_yCa50JE6qoSTLa+b9Sj~6Dm~Dkg0|W`q~g-w zqqNMh;lEZUC?z(TuO5N6{4+S@rFdIP)4o*Hn@XhVkwsQ~2Az3o z0nK^b0=?HHnKx80P!8!u1vuVT*7eZY4*P8nZTZ)niTSVHn3>C{3K1HoBcI+UP#ua6s5ZKQ|{iR_h%5HzqFpZ8#zlf*EPA6q-sDdlOH^L?-%h9Cq zX!yoQ)c?y`e#>sz_0~<-ZYlNET&Z+QGDUx9STmCGHmk^3^YY86RD882`-H2t>Ri^w zo--gFS{cG(vAcdXWK_&d6Z><+(wWJfGyLwp5H@(~^F+Gn@at}F z699UhX*rUs3G}d~KJdLY`<{-S>ZXp*_ww>WpVI^qb{dc9vu9D6SKt-6PX--(!6w`7 z7P@!mRd7{voxG%WJFJPb7b%?7PcDD{^Tay;`7CqU0$5gz&!XuKg=>W`AMgZ2X%yq{ zq!%~#4n1+7l>)w-@;LJ)I!lhyL!EW66K<$rxp`9}d%zLLJUV zK;Mzq2FgPK+buugK+3*-mBFQzgUmy{jU{r9ZXP!oI8IU{>!Q+3wzC6V`J z)RrkHuid2nGgHnWWu-J%DLg!nJn+(++061)>UQbHBB}Rbo=K&%qM1e^n-UNk#!~6% z@ucm)>8xAuG>%LdSq-4KO?t=0aO>sgY-4N6M)4ZSJ41|oHZp%zjz;FISBLN#DQH^t zX3`=+SCr94=|DdmN}!a(DV8g@hopc(_}SOIPOW{qa%X+%;o6;hcOE~dwDXjfr z8-4VO!7H@jRE~ zk~*Jf%-b_oc*D4$#A!3Y;ci_pD_*4dhiW7K9pxl2p!{)(3s}~OS$I4jTrnF6_JUPp z80??Je+)o-=+6{pM})5S7#a64zQC-04;>}ou vxw&iCZr-|iYo_BTE`rN+@Fm@pemhmVtnL*_kGh1#*iZjI&0Y0Q6!icA$xr(B literal 0 HcmV?d00001 diff --git a/man/auth_from_secret.Rd b/man/auth_from_secret.Rd index 1903fa0..500560a 100644 --- a/man/auth_from_secret.Rd +++ b/man/auth_from_secret.Rd @@ -47,8 +47,8 @@ auth_from_secret("github", token = "ghp_a_github_pat_here") token <- authorize("google") auth_from_secret( app_name = "google", - refresh_token = token$credentials$access_token, - access_token = token$credentials$refresh_token + access_token = token$credentials$access_token, + refresh_token = token$credentials$refresh_token ) } diff --git a/man/clean_metric_data.Rd b/man/clean_ga_metrics.Rd similarity index 80% rename from man/clean_metric_data.Rd rename to man/clean_ga_metrics.Rd index 28a7693..6035faf 100644 --- a/man/clean_metric_data.Rd +++ b/man/clean_ga_metrics.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/google-analytics.R -\name{clean_metric_data} -\alias{clean_metric_data} +\name{clean_ga_metrics} +\alias{clean_ga_metrics} \title{Handle Google Analytics Lists} \usage{ -clean_metric_data(metrics = NULL) +clean_ga_metrics(metrics = NULL) } \arguments{ \item{metrics}{a metrics object from `get_ga_stats()` function} diff --git a/man/all_ga_metrics.Rd b/man/get_all_ga_metrics.Rd similarity index 66% rename from man/all_ga_metrics.Rd rename to man/get_all_ga_metrics.Rd index b0aefef..81decf0 100644 --- a/man/all_ga_metrics.Rd +++ b/man/get_all_ga_metrics.Rd @@ -1,24 +1,19 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/google-analytics.R -\name{all_ga_metrics} -\alias{all_ga_metrics} +\name{get_all_ga_metrics} +\alias{get_all_ga_metrics} \title{Get all metrics for all properties associated with an account} \usage{ -all_ga_metrics( - account_id = NULL, - property_names = NULL, - token = NULL, - dataformat = "dataframe" -) +get_all_ga_metrics(account_id = NULL, token = NULL, dataformat = "dataframe") } \arguments{ \item{account_id}{the account id that you'd like to retrieve stats for all properties associated with it.} -\item{property_names}{a vector of property names for stats to be retrieved for. Note you can only provide one or the other.} - \item{token}{credentials for access to Google using OAuth. `authorize("google")`} \item{dataformat}{How would you like the data returned to you? Default is a "dataframe" but if you'd like to see the original API list result, put "raw".} + +\item{property_ids}{a vector of property ids for stats to be retrieved for. Note you can only provide one or the other.} } \value{ Either a list of dataframes where `metrics`, `dimensions` and `link clicks` are reported. But if `format` is set to "raw" then the original raw API results will be returned @@ -32,10 +27,6 @@ This is a function to gets metrics and dimensions for all properties associated authorize("google") accounts <- get_ga_user() -stats_list <- all_ga_metrics(account_id = accounts$id[5]) - -property_names <- c("358228687", "377543643", "377952717") - -some_stats_list <- all_ga_metrics(property_names = property_names) +some_stats_list <- get_all_ga_metrics(property_ids = property_ids) } } diff --git a/man/get_ga_metadata.Rd b/man/get_ga_metadata.Rd index e61a5e9..c632777 100644 --- a/man/get_ga_metadata.Rd +++ b/man/get_ga_metadata.Rd @@ -22,7 +22,7 @@ accounts <- get_ga_user() properties_list <- get_ga_properties(account_id = accounts$id[1]) -property_id <- gsub("properties/", "", properties_list$properties$name[1]) +property_id <- gsub("properties/", "", properties_list$name[1]) property_metadata <- get_ga_metadata(property_id = property_id) } } diff --git a/man/get_ga_stats.Rd b/man/get_ga_stats.Rd index 96e1493..bb65271 100644 --- a/man/get_ga_stats.Rd +++ b/man/get_ga_stats.Rd @@ -40,7 +40,7 @@ accounts <- get_ga_user() properties_list <- get_ga_properties(account_id = accounts$id[1]) -property_id <- gsub("properties/", "", properties_list$properties$name[1]) +property_id <- gsub("properties/", "", properties_list$name[1]) metrics <- get_ga_stats(property_id, stats_type = "metrics") dimensions <- get_ga_stats(property_id, stats_type = "dimensions") } diff --git a/vignettes/data_dictionary.Rmd b/vignettes/data_dictionary.Rmd new file mode 100644 index 0000000..bbbd413 --- /dev/null +++ b/vignettes/data_dictionary.Rmd @@ -0,0 +1,330 @@ +--- +title: "getting-started" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{getting-started} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>" +) +``` + +# metricminer + +`metricminer` is an R package that helps you mine metrics on common places on the web through the power of their APIs. + +It also helps make the data in a format that is easily used for a dashboard or other purposes. +It will have an associated [dashboard template](https://github.com/fhdsl/metricminer-dashboard) and tutorials to help you fully use the data you retrieve with `metricminer` (but these are still under development!) + +You can [read the metricminer package documentation here](https://hutchdatascience.org/metricminer/). + +## Apps supported + +Currently `metricminer` supports mining data from: + +- [Calendly](https://calendly.com/) +- [GitHub](https://github.com/) +- [Google Analytics](https://analytics.google.com/analytics/academy/course/6) +- [Google Forms](https://www.google.com/forms/about/) +- [Slido](https://admin.sli.do/events) export files stored on Googledrive + +## Data format options + +`metricminer` attempts to retrieve API data for you and give you it to you in a format that is a tidy data.frame. +this means metricminer has to be opinionated about what metrics it returns so it fits in a useful and human ready to read data frame. + +If you find that the data returned is not what you need you have two options (these options can be pursued concurrently): + +1. You can set the `dataformat` argument to `"raw"` to see the original, unedited JSON formatted data as it was returned from the API. Then you can personally look for the data that you want and extract it. +2. You can post a GitHub issue to explain why the metric missing from the data frame formatted data should be included. And if possible and reasonable, we can work on including that data in the next version of `metricminer`. + +## How to install + +If you want the development version (not advised) you can install using the `remotes` package to install from GitHub. +``` +if (!("remotes" %in% installed.packages())) { + install.packages("remotes") +} +remotes::install_github("fhdsl/metricminer") +``` + +``` +library(metricminer) +``` +## Basic Usage + +To start, you need to `authorize()` the package to access your data. If you run `authorize()` you will be asked which app you'd like to authorize and whether you'd like to cache that auth information. If you already know which app you'd like to authorize, like `google` for example, you can run `authorize("google")`. + +Then follow the instructions on the upcoming screens and select the scopes you feel comfortable sharing (you generally just need read permissions for metricminer to be able to collect data). + +``` +authorize() +``` + +If you want to clear out authorizations and caches stored by `metricminer` you can run: + +``` +delete_creds() +``` + +### GitHub + +You can retrieve metrics from a repository on GitHub doing this: +``` +authorize("github") +metrics <- get_github_metrics(repo = "fhdsl/metricminer") +``` + +### Calendly + +You can retrieve calendly events information using this type of workflow: +``` +authorize("calendly") +user <- get_calendly_user() +events <- list_calendly_events(user = user$resource$uri) +``` + +### Google Analytics + +You can retrieve Google Analytics data for websites like this. + +First you have to retrieve your account information after you've authorized. + +``` +authorize("google") +accounts <- get_ga_user() +``` + +Then you need to retrieve the properties (aka usually the websites you are tracking) +underneath that account. + +``` +properties_list <- get_ga_properties(account_id = accounts$id[1]) +``` + +Just need to shave off the `properties/` bit from this string. + +``` +property_id <- gsub("properties/", "", properties_list$properties$name[1]) +``` + +Now we can collect some stats. + +In Google Analytics `metrics` are your basic numbers (how many visits to your website, etc.). +``` +metrics <- get_ga_stats(property_id, stats_type = "metrics") +``` +Whereas `dimensions` are more a list of events that have happened. So here's a list of people that have logged on. +``` +dimensions <- get_ga_stats(property_id, stats_type = "dimensions") +``` +Lastly, we have a third option of collecting `link_clicks` and the links they have clicked. This is also known as a dimension according to Google analytics, but often it isn't compatible for us to download link click data at the same time as other dimension data so in `metricminer` we collect them separately. +``` +link_clicks <- get_ga_stats(property_id, stats_type = "link_clicks") +``` + +### Google Forms + +You can retrieve Google form information and responses like this: +``` +authorize("google") +form_url <- "https://docs.google.com/forms/d/1Z-lMMdUyubUqIvaSXeDu1tlB7_QpNTzOk3kfzjP2Uuo/edit" +form_info <- get_google_form(form_url) +``` + +### Slido + +If you have used Slido for interactive slide sessions and collected that info and exported it to your googledrive you can use `metricminer` to collect that data as well. + +``` +drive_id <- "https://drive.google.com/drive/folders/0AJb5Zemj0AAkUk9PVA" +slido_data <- get_slido_files(drive_id) +``` + +### Youtube + +If you have a channel and the URL is https://www.youtube.com/channel/a_bunch_of_letters_here + +Then you can extract stats for the videos on that youtube channel using that URL. +``` +authorize("google") +youtube_stats <- get_get_youtube_stats("a_bunch_of_letters_here") +``` + +## Bulk Retrievals + +Maybe you just want to retrieval it ALL. We have som wrapper functions that will attempt to do this for you. +These functions are a bit more precarious/risky in that there may be reasons certain websites/repos/events/data may not be able to be collected. So collecting repositories one by one will allow you more insight into what is happening. + +However, these bulk retrieval functions may help you if you want to grab ALL of your accounts data in one swoop. Just make sure to carefully look over and curate that data after it is attempted to be collected. You may find some retrievals are empty for potentially good reasons (for example if a google form has no responses to collect it will show up with "no responses" in the respective part of the list). + +### GitHub bulk + +From GitHub you can attempt to collect repository metrics from all repositories from an account. + +``` +authorize("github") +all_repos_metrics <- get_repos_metrics(owner = "fhdsl") +``` + +If you want to do this by giving a list of specific repositories you want data from you can just provide a vector of those repository's names like this: +``` +repo_names <- c("fhdsl/metricminer", "jhudsl/OTTR_Template") +some_repos_metrics <- get_repos_metrics(repo_names = repo_names) +``` + +### Google Analytics bulk + +Similar to single website retrieval we need to authorize the package. +``` +authorize("google") +accounts <- get_ga_user() +``` + +Then we can provide the account id to `all_ga_metrics` and it will attempt to grab all stats for all website properties underneath the provided account. + +``` +stats_list <- all_ga_metrics(account_id = accounts$id[5]) +``` + + +### Google Forms bulk + +As always, we need to authorize the app. +``` +authorize("google") +``` + +We can retrieve a list of form ids using `googledrive` R package. +``` +form_list <- googledrive::drive_find( + shared_drive = googledrive::as_id("0AJb5Zemj0AAkUk9PVA"), + type = "form") +``` + +Now we can provide this vector of form ids to `get_multiple_forms` +``` +multiple_forms <- get_multiple_forms(form_ids = form_list$id) +``` + +## Non-interactive authorizing from secrets + +If you'd like to authorize non-interactively (whether on GitHub actions or locally) you can set your tokens using `Sys.setenv()` + +### Setting Calendly auth from secret + +You can [go here to get an API key](https://calendly.com/integrations/api_webhooks). You likely will have to login first. + +Then you can store this by putting your API key in this type of command: +``` +Sys.setenv(METRICMINER_CALENDLY = "Put calendly token here") +``` + +Now in your script if you run the following, you will have authorization to Calendly. +``` +auth_from_secret("calendly", token = Sys.getenv("METRICMINER_CALENDLY")) +``` + +### Setting GitHub auth from secret + +Similar steps can be done for the GitHub personal access token. + +First [go here to get a GitHub PAT](https://github.com/settings/tokens/new?description=metricminer&scopes=repo,read:packages,read:org). You will likely have to login first. + +Then you can run this command but put your GitHub PAT there. +``` +Sys.setenv(METRICMINER_GITHUB_PAT = "Put GitHub PAT here") +``` + +Now in your script if you run the following, you will have authorization to GitHub. + +``` +# Authorize GitHub +auth_from_secret("github", token = Sys.getenv("METRICMINER_GITHUB_PAT")) +``` + +### Setting Google auth from secret + +For Google you can authorize from secret by doing the normal interactive way using `authorize("google")` but storing the result like this: + +``` +token <- authorize("google") +``` + +Then you can use this object to extract two secrets by printing them out like this: + +`token$credentials$access_token` +`token$credentials$refresh_token` + +Then you can set these in your environment doing the same steps as before: + +``` +Sys.setenv(METRICMINER_GOOGLE_ACCESS = "Google access token here") + +Sys.setenv(METRICMINER_GOOGLE_REFRESH = "Google refresh token here") +``` +Now in your script if you run the following you will have authorization to Google Apps. + +``` +# Authorize Google +auth_from_secret("google", + refresh_token = Sys.getenv("METRICMINER_GOOGLE_REFRESH"), + access_token = Sys.getenv("METRICMINER_GOOGLE_ACCESS"), + cache = TRUE +) +``` + +## Authorizing on GitHub Actions + + +In GitHub you can run `metricminer` using authorization if you use the above steps to retrieve the necessary keys but then store them each as GitHub Secrets. + +[Read here about how to store GitHub secrets](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository) + +You'll need the secrets to be stored as the respective key name we've referenced above: +``` +METRICMINER_CALENDLY +METRICMINER_GITHUB_PAT +METRICMINER_GOOGLE_REFRESH +METRICMINER_GOOGLE_ACCESS +``` + +Then in your GitHub action yaml you'll need something like this to extract and authorize these secrets in the +environment. + +``` + - name: Authorize metricminer + env: + METRICMINER_CALENDLY: ${{ secrets.METRICMINER_CALENDLY }} + METRICMINER_GITHUB_PAT: ${{ secrets.METRICMINER_GITHUB_PAT }} + METRICMINER_GOOGLE_ACCESS: ${{ secrets.METRICMINER_GOOGLE_ACCESS }} + METRICMINER_GOOGLE_REFRESH: ${{ secrets.METRICMINER_GOOGLE_REFRESH }} + run: | + # Authorize Calendly + auth_from_secret("calendly", token = Sys.getenv("METRICMINER_CALENDLY")) + + # Authorize GitHub + auth_from_secret("github", token = Sys.getenv("METRICMINER_GITHUB_PAT")) + + # Authorize Google + auth_from_secret("google", + refresh_token = Sys.getenv("METRICMINER_GOOGLE_REFRESH"), + access_token = Sys.getenv("METRICMINER_GOOGLE_ACCESS"), + cache = TRUE + ) + + ### Now run the R commands you want here or call an R script in a later step. + shell: Rscript {0} + +``` + +### Session info + +```{r} +sessionInfo() +``` From 209b1c7bc2d13f308e8ac25109820fe65bd9c4b5 Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Wed, 10 Jan 2024 12:00:13 -0500 Subject: [PATCH 22/36] Googl forms dummy data --- R/google-analytics.R | 1 - .../extdata/example_data/example-data-setup.R | 17 +- inst/extdata/example_data/gform_info.RDS | Bin 0 -> 604 bytes inst/extdata/example_data/multiple_gforms.RDS | Bin 0 -> 846 bytes vignettes/data_dictionary.Rmd | 311 +----------------- 5 files changed, 18 insertions(+), 311 deletions(-) create mode 100644 inst/extdata/example_data/gform_info.RDS create mode 100644 inst/extdata/example_data/multiple_gforms.RDS diff --git a/R/google-analytics.R b/R/google-analytics.R index 653503f..c585c5c 100644 --- a/R/google-analytics.R +++ b/R/google-analytics.R @@ -389,7 +389,6 @@ clean_ga_metrics <- function(metrics = NULL) { clean_ga_dimensions <- function(dimensions = NULL) { - if () all_website_dims <- lapply(dimensions, wrangle_ga_dimensions) %>% dplyr::bind_rows(.id = "website") diff --git a/inst/extdata/example_data/example-data-setup.R b/inst/extdata/example_data/example-data-setup.R index a5b15b7..0286f8c 100644 --- a/inst/extdata/example_data/example-data-setup.R +++ b/inst/extdata/example_data/example-data-setup.R @@ -70,4 +70,19 @@ all_ga_metric_list <- get_all_ga_metrics(account_id = ga_user$id[1]) save_example_data(all_ga_metric_list) class(all_ga_metric_list) -## google +####### Google Form Examples + +# Grab a single form and its responses +gform_info <- get_google_form("https://docs.google.com/forms/d/1JjmsiuVoGSxvl-1M_oWittcftO955tijzeNc-cgJlo8/edit") +save_example_data(gform_info) +class(gform_info) + +# Get ids for google forms we want info about +gform_list <- googledrive::drive_find( + #If using a shared_drive you'll do this argument: shared_drive = googledrive::as_id("id_to_drive_folder"), + type = "form") + +# Now grab the info and responses from these forms +multiple_gforms <- get_multiple_forms(gform_list$id) +save_example_data(multiple_gforms) +class(multiple_gforms) diff --git a/inst/extdata/example_data/gform_info.RDS b/inst/extdata/example_data/gform_info.RDS new file mode 100644 index 0000000000000000000000000000000000000000..6e276efb0d3125230d84f147abbf96591f6a9dcf GIT binary patch literal 604 zcmV-i0;ByOiwFP!000001I<*+ZW=KZH7_-!O53PXmz8&21wnuSD>{J?pyiR2B#;d< zGdG!m!JgQLVRrrEsvl4#9l$09wd$%;M;h7pp5x==$8~=nArwRLSOmoqVHYK?dbOh> zLeaM?0HHVGA3<@H4BG<(n~b?w%=jFF5MPoKiWF6(y)1y4vr0J~=d5%gQz({pc<*m) zA~Ex%!zi{G4%iI4&IIrXge+sldRHM7``nR$3HAsl_ndfkIYgGuqhHbLi|lA8E{$rV z6LykM;it)HLykv(Syh6PDF~&IP|24Mv(QFpZRl8o^HbXFH?<-SzB1X=Bc>cWR0??5LE|GLK;;h9 zhArF2<;dkpe^i@O{=VHR&Z9bPeIw#Z_uQxqs;6x`PpChppZeqLtCn$cmJSxwKWp3$ z!XvIu0;A=5lk6mS-{_szY~QM$jXs~8yH<1D?mG9~dQoi4Id}7Vv6iiuh{)!Npg=O|d_E5wV5J$=ntv>; q*psS))iV-46hGEC{kM*`s()_^k}Ty^{9-dRdi)9FNW&ph1polSbsz%( literal 0 HcmV?d00001 diff --git a/inst/extdata/example_data/multiple_gforms.RDS b/inst/extdata/example_data/multiple_gforms.RDS new file mode 100644 index 0000000000000000000000000000000000000000..e2b7e8ab876c7e3413a8194ab80bf91b32a12a4d GIT binary patch literal 846 zcmV-U1F`%ciwFP!000001Km|iZ`wc*c6g{stMs8tJ@inms!F{y5e^~1DK(J9B#=N} zm|U#Io`Au4on3=*PyNMJe?V2bhBXats`k)SRV`Wjm~TGM&dz++9!ipwlv2qRDVgR| zDSdIizfq8+#B;t#h%w3Er~F-!Qqme9*A-+CVk0)9V+goC1f>NiAwa8H0At%MZb$2F zGn1F|g<=f%s@20DBCcl<2blx~WQ1&M0H_1Z+eHHPqKqBnQ_BN{k&7wrQtbA2xg|V7 z;-fTsR$|=gQ>9Wp;ghu~e5SU;{i_J$jBOV41O}ESl=UJW&VzIV} zMQl7Vdc-n7kNl#Pc`)(5Z^piC*qB9Mjy4~f*u$IGlm``quTkP}M!|j(CyI=Z4Kj4G z;pvv+F}R6YQyt7e?5&H0h2z)IGrZ~dB00OCmq&s5DlR*!h?L+xP|EE}>A07}j^B5V zwEpEqUD?^o1QW;Kt6sJF6;}p# z#^B7-&*y(pNMGL;(%VJwM@4m!w!fjHzj?z#DO1z=n4aFB4tCXRt*#v%=C2BuO(^7H zqe@FAEnzb=EVcS4@`c(yY6dLd)Z`nzy;B^xvT7b(?J zdCofvc?5Yqu1&GBc=Myr&;{GJoGyx2*8TC{t%bkVzPSBA)WRkINJ-YQ9_C&b@+SmR Y{Gs$vAoaOGMhH&z4cEEA9 %\VignetteIndexEntry{getting-started} @@ -14,314 +14,7 @@ knitr::opts_chunk$set( ) ``` -# metricminer - -`metricminer` is an R package that helps you mine metrics on common places on the web through the power of their APIs. - -It also helps make the data in a format that is easily used for a dashboard or other purposes. -It will have an associated [dashboard template](https://github.com/fhdsl/metricminer-dashboard) and tutorials to help you fully use the data you retrieve with `metricminer` (but these are still under development!) - -You can [read the metricminer package documentation here](https://hutchdatascience.org/metricminer/). - -## Apps supported - -Currently `metricminer` supports mining data from: - -- [Calendly](https://calendly.com/) -- [GitHub](https://github.com/) -- [Google Analytics](https://analytics.google.com/analytics/academy/course/6) -- [Google Forms](https://www.google.com/forms/about/) -- [Slido](https://admin.sli.do/events) export files stored on Googledrive - -## Data format options - -`metricminer` attempts to retrieve API data for you and give you it to you in a format that is a tidy data.frame. -this means metricminer has to be opinionated about what metrics it returns so it fits in a useful and human ready to read data frame. - -If you find that the data returned is not what you need you have two options (these options can be pursued concurrently): - -1. You can set the `dataformat` argument to `"raw"` to see the original, unedited JSON formatted data as it was returned from the API. Then you can personally look for the data that you want and extract it. -2. You can post a GitHub issue to explain why the metric missing from the data frame formatted data should be included. And if possible and reasonable, we can work on including that data in the next version of `metricminer`. - -## How to install - -If you want the development version (not advised) you can install using the `remotes` package to install from GitHub. -``` -if (!("remotes" %in% installed.packages())) { - install.packages("remotes") -} -remotes::install_github("fhdsl/metricminer") -``` - -``` -library(metricminer) -``` -## Basic Usage - -To start, you need to `authorize()` the package to access your data. If you run `authorize()` you will be asked which app you'd like to authorize and whether you'd like to cache that auth information. If you already know which app you'd like to authorize, like `google` for example, you can run `authorize("google")`. - -Then follow the instructions on the upcoming screens and select the scopes you feel comfortable sharing (you generally just need read permissions for metricminer to be able to collect data). - -``` -authorize() -``` - -If you want to clear out authorizations and caches stored by `metricminer` you can run: - -``` -delete_creds() -``` - -### GitHub - -You can retrieve metrics from a repository on GitHub doing this: -``` -authorize("github") -metrics <- get_github_metrics(repo = "fhdsl/metricminer") -``` - -### Calendly - -You can retrieve calendly events information using this type of workflow: -``` -authorize("calendly") -user <- get_calendly_user() -events <- list_calendly_events(user = user$resource$uri) -``` - -### Google Analytics - -You can retrieve Google Analytics data for websites like this. - -First you have to retrieve your account information after you've authorized. - -``` -authorize("google") -accounts <- get_ga_user() -``` - -Then you need to retrieve the properties (aka usually the websites you are tracking) -underneath that account. - -``` -properties_list <- get_ga_properties(account_id = accounts$id[1]) -``` - -Just need to shave off the `properties/` bit from this string. - -``` -property_id <- gsub("properties/", "", properties_list$properties$name[1]) -``` - -Now we can collect some stats. - -In Google Analytics `metrics` are your basic numbers (how many visits to your website, etc.). -``` -metrics <- get_ga_stats(property_id, stats_type = "metrics") -``` -Whereas `dimensions` are more a list of events that have happened. So here's a list of people that have logged on. -``` -dimensions <- get_ga_stats(property_id, stats_type = "dimensions") -``` -Lastly, we have a third option of collecting `link_clicks` and the links they have clicked. This is also known as a dimension according to Google analytics, but often it isn't compatible for us to download link click data at the same time as other dimension data so in `metricminer` we collect them separately. -``` -link_clicks <- get_ga_stats(property_id, stats_type = "link_clicks") -``` - -### Google Forms - -You can retrieve Google form information and responses like this: -``` -authorize("google") -form_url <- "https://docs.google.com/forms/d/1Z-lMMdUyubUqIvaSXeDu1tlB7_QpNTzOk3kfzjP2Uuo/edit" -form_info <- get_google_form(form_url) -``` - -### Slido - -If you have used Slido for interactive slide sessions and collected that info and exported it to your googledrive you can use `metricminer` to collect that data as well. - -``` -drive_id <- "https://drive.google.com/drive/folders/0AJb5Zemj0AAkUk9PVA" -slido_data <- get_slido_files(drive_id) -``` - -### Youtube - -If you have a channel and the URL is https://www.youtube.com/channel/a_bunch_of_letters_here - -Then you can extract stats for the videos on that youtube channel using that URL. -``` -authorize("google") -youtube_stats <- get_get_youtube_stats("a_bunch_of_letters_here") -``` - -## Bulk Retrievals - -Maybe you just want to retrieval it ALL. We have som wrapper functions that will attempt to do this for you. -These functions are a bit more precarious/risky in that there may be reasons certain websites/repos/events/data may not be able to be collected. So collecting repositories one by one will allow you more insight into what is happening. - -However, these bulk retrieval functions may help you if you want to grab ALL of your accounts data in one swoop. Just make sure to carefully look over and curate that data after it is attempted to be collected. You may find some retrievals are empty for potentially good reasons (for example if a google form has no responses to collect it will show up with "no responses" in the respective part of the list). - -### GitHub bulk - -From GitHub you can attempt to collect repository metrics from all repositories from an account. - -``` -authorize("github") -all_repos_metrics <- get_repos_metrics(owner = "fhdsl") -``` - -If you want to do this by giving a list of specific repositories you want data from you can just provide a vector of those repository's names like this: -``` -repo_names <- c("fhdsl/metricminer", "jhudsl/OTTR_Template") -some_repos_metrics <- get_repos_metrics(repo_names = repo_names) -``` - -### Google Analytics bulk - -Similar to single website retrieval we need to authorize the package. -``` -authorize("google") -accounts <- get_ga_user() -``` - -Then we can provide the account id to `all_ga_metrics` and it will attempt to grab all stats for all website properties underneath the provided account. - -``` -stats_list <- all_ga_metrics(account_id = accounts$id[5]) -``` - - -### Google Forms bulk - -As always, we need to authorize the app. -``` -authorize("google") -``` - -We can retrieve a list of form ids using `googledrive` R package. -``` -form_list <- googledrive::drive_find( - shared_drive = googledrive::as_id("0AJb5Zemj0AAkUk9PVA"), - type = "form") -``` - -Now we can provide this vector of form ids to `get_multiple_forms` -``` -multiple_forms <- get_multiple_forms(form_ids = form_list$id) -``` - -## Non-interactive authorizing from secrets - -If you'd like to authorize non-interactively (whether on GitHub actions or locally) you can set your tokens using `Sys.setenv()` - -### Setting Calendly auth from secret - -You can [go here to get an API key](https://calendly.com/integrations/api_webhooks). You likely will have to login first. - -Then you can store this by putting your API key in this type of command: -``` -Sys.setenv(METRICMINER_CALENDLY = "Put calendly token here") -``` - -Now in your script if you run the following, you will have authorization to Calendly. -``` -auth_from_secret("calendly", token = Sys.getenv("METRICMINER_CALENDLY")) -``` - -### Setting GitHub auth from secret - -Similar steps can be done for the GitHub personal access token. - -First [go here to get a GitHub PAT](https://github.com/settings/tokens/new?description=metricminer&scopes=repo,read:packages,read:org). You will likely have to login first. - -Then you can run this command but put your GitHub PAT there. -``` -Sys.setenv(METRICMINER_GITHUB_PAT = "Put GitHub PAT here") -``` - -Now in your script if you run the following, you will have authorization to GitHub. - -``` -# Authorize GitHub -auth_from_secret("github", token = Sys.getenv("METRICMINER_GITHUB_PAT")) -``` - -### Setting Google auth from secret - -For Google you can authorize from secret by doing the normal interactive way using `authorize("google")` but storing the result like this: - -``` -token <- authorize("google") -``` - -Then you can use this object to extract two secrets by printing them out like this: - -`token$credentials$access_token` -`token$credentials$refresh_token` - -Then you can set these in your environment doing the same steps as before: - -``` -Sys.setenv(METRICMINER_GOOGLE_ACCESS = "Google access token here") - -Sys.setenv(METRICMINER_GOOGLE_REFRESH = "Google refresh token here") -``` -Now in your script if you run the following you will have authorization to Google Apps. - -``` -# Authorize Google -auth_from_secret("google", - refresh_token = Sys.getenv("METRICMINER_GOOGLE_REFRESH"), - access_token = Sys.getenv("METRICMINER_GOOGLE_ACCESS"), - cache = TRUE -) -``` - -## Authorizing on GitHub Actions - - -In GitHub you can run `metricminer` using authorization if you use the above steps to retrieve the necessary keys but then store them each as GitHub Secrets. - -[Read here about how to store GitHub secrets](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository) - -You'll need the secrets to be stored as the respective key name we've referenced above: -``` -METRICMINER_CALENDLY -METRICMINER_GITHUB_PAT -METRICMINER_GOOGLE_REFRESH -METRICMINER_GOOGLE_ACCESS -``` - -Then in your GitHub action yaml you'll need something like this to extract and authorize these secrets in the -environment. - -``` - - name: Authorize metricminer - env: - METRICMINER_CALENDLY: ${{ secrets.METRICMINER_CALENDLY }} - METRICMINER_GITHUB_PAT: ${{ secrets.METRICMINER_GITHUB_PAT }} - METRICMINER_GOOGLE_ACCESS: ${{ secrets.METRICMINER_GOOGLE_ACCESS }} - METRICMINER_GOOGLE_REFRESH: ${{ secrets.METRICMINER_GOOGLE_REFRESH }} - run: | - # Authorize Calendly - auth_from_secret("calendly", token = Sys.getenv("METRICMINER_CALENDLY")) - - # Authorize GitHub - auth_from_secret("github", token = Sys.getenv("METRICMINER_GITHUB_PAT")) - - # Authorize Google - auth_from_secret("google", - refresh_token = Sys.getenv("METRICMINER_GOOGLE_REFRESH"), - access_token = Sys.getenv("METRICMINER_GOOGLE_ACCESS"), - cache = TRUE - ) - - ### Now run the R commands you want here or call an R script in a later step. - shell: Rscript {0} - -``` +# Data Dictionary ### Session info From dfe4015ff1c5285a4d72ac8ea91dbc0cd7524c51 Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Wed, 10 Jan 2024 21:32:18 -0500 Subject: [PATCH 23/36] a couple more bug fixes --- R/google-analytics.R | 6 +++--- R/slido.R | 10 ++++++++-- inst/extdata/example_data/example-data-setup.R | 7 +++++++ inst/extdata/example_data/slido_data.RDS | Bin 0 -> 1396 bytes 4 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 inst/extdata/example_data/slido_data.RDS diff --git a/R/google-analytics.R b/R/google-analytics.R index c585c5c..2e86e60 100644 --- a/R/google-analytics.R +++ b/R/google-analytics.R @@ -335,9 +335,9 @@ get_all_ga_metrics <- function(account_id = NULL, token = NULL, dataformat = "da if (length(display_names) > 1) { # Save the names - names(all_google_analytics_metrics) <- display_names - names(all_google_analytics_dimensions) <- display_names - names(all_google_analytics_links) <- display_names + names(all_ga_metrics) <- display_names + names(all_ga_dimensions) <- display_names + names(all_ga_links) <- display_names } if (dataformat == "dataframe") { diff --git a/R/slido.R b/R/slido.R index bf972cb..7be5525 100644 --- a/R/slido.R +++ b/R/slido.R @@ -2,7 +2,7 @@ #' Get Slido Files -#' @description This is a function to get slido response output files. +#' @description This is a function to get slido response output files. The slido files must be saved as googlesheets and cannot be xlsx. #' @param drive_id a URL or drive id that has the slido response output files you are looking to get (will recursively search for files by default). #' @param token credentials for access to Google using OAuth. `authorize("google")` #' @param recursive Should slido files be looked for recursively in this folder? default is TRUE. @@ -15,6 +15,7 @@ #' @examples \dontrun{ #' #' drive_id <- "https://drive.google.com/drive/folders/0AJb5Zemj0AAkUk9PVA" +#' drive_id <-"https://drive.google.com/drive/u/0/folders/1XWXHHyj32Uw_UyaUJrqp6S--hHnM0-7l" #' slido_data <- get_slido_files(drive_id) #' } get_slido_files <- function(drive_id, token = NULL, recursive = TRUE, keep_duplicates = FALSE) { @@ -30,6 +31,10 @@ get_slido_files <- function(drive_id, token = NULL, recursive = TRUE, keep_dupli recursive = recursive ) + if (length(spreadsheet_list)== 0 ){ + stop("No spreadsheets found in this drive Id provided") + } + file_info <- data.frame( file_name = spreadsheet_list$name, id = spreadsheet_list$id @@ -40,7 +45,8 @@ get_slido_files <- function(drive_id, token = NULL, recursive = TRUE, keep_dupli "^Leaderboard-", "^Polls-overall-", "^Replies-", - "^Polls-per-user-" + "^Polls-per-user-", + "^Questions-" ) # Extract slido file names diff --git a/inst/extdata/example_data/example-data-setup.R b/inst/extdata/example_data/example-data-setup.R index 0286f8c..eb206f3 100644 --- a/inst/extdata/example_data/example-data-setup.R +++ b/inst/extdata/example_data/example-data-setup.R @@ -86,3 +86,10 @@ gform_list <- googledrive::drive_find( multiple_gforms <- get_multiple_forms(gform_list$id) save_example_data(multiple_gforms) class(multiple_gforms) + +####### Slido Results Examples + +drive_id <- "https://drive.google.com/drive/u/0/folders/1XWXHHyj32Uw_UyaUJrqp6S--hHnM0-7l" +slido_data <- get_slido_files(drive_id) +save_example_data(slido_data) +class(slido_data) diff --git a/inst/extdata/example_data/slido_data.RDS b/inst/extdata/example_data/slido_data.RDS new file mode 100644 index 0000000000000000000000000000000000000000..3e2399e7576525e95a8140768d3909a4f27428a5 GIT binary patch literal 1396 zcmV-)1&jJ0iwFP!000001MOMeZW~1uUfb!9lP08;8;~kZ#RWeiO?KB#9CML|l#oVM znEnQvxxkLTy)`lMx9Icp-9v2sV?(K>RqvOZU`tn4YsOaLcQGP7{cfF%z& zy~bEDca8Dg$Y-rXqiK|H3hvb}F!~4{B;Hc^vBFOjeyZ>@g|`)cuJDe+FBN{J@N0$N zDE!V!uJl*RuZAq_G+7)SY{TKGE$h@mao%2-cNQEM*`>1WlSMLA`sG=^F%yCbP$*>%eW_YtYZa&f6#D2$@OzAU?8 zO=N+}s3J#d0gm)%$tmMeQ4MZ*b(PCOPCZU~Sm3jDj+?~L$#d2|8-iVGO~akthosx> z{%JO-3(Y1ow9+MeBosF!r7SaBmfWrmdF-rB9~q?=~^j9nNnu8WIm- z0h#-1UC5}`tzOqM-Nzn&wyQ>~q;gs+Gh$$a4&+s-)6e57aA-V8#@?EKdiBFw-~R#+ zBk+eiqw#L5hd=!`j6d%`BHkUs+w-$e5se?#iES)s*g&t_bHz9Q2e$m>770;>d>{@D zhGEz-);Cxi)mT`k9?Z|kW2nPoR3{`rAuxiWmR?Z=K{SN29zmS!QPM^&j%&b?Xyrek z_SUc(<|L6*N^O`Y5DU?JRBXi6XI~x=HySQVJFJI_Q972|9$>}!~iPxMl z3ZAK$C17!g0}oXR{0W#RjS8>zo0T(BmB7R_R2!yDeg-b1+A9yXBQ@%9>N84CZTU0^ zI!J;+<=h7I<_I>+H| z2PI%~=Sj5+H37I;Z}K9&c@sDQgE_g*iRS?&5tgQ!DLc8Dyc3zq9Nee(A$iufrH(1u zgU?yRXxdYrK3x9zF#o)wrN(>iCDm^eXGf#H#@2gI(fU_?>q!N9vQn?4H`(lxRhR5!mtf`C~)SI$}wUg>HR7wH}u zdLHKIZO1LxC412+6_)KK$1T~;QpsI%;c^PD=g0M^;l;FWFWDu>)j{1?p;V!?>_>I4 z)OI_|4pdooI=;(e>NnanCiQl|CHMgF^}71)Tq3dHI50XgIS4MNExjlzyv1a&tM?i| zCja`l*ZB6=eeGj|7^k?&9#}DQS1W6VFJ}90a(U;%^_u>aN0Y3ISUzdzy>B@0@v+W3 zt^E>-!HIfmgEssCZb3ZVnH^Yo`?3CGS0lnxF24h+GVJEIOX>t-m=i$6G!d#&` zS~}Fz3B6nE#dK34U)}c9MS@|Ot1`@W=aOGoGzU9a_|*NVupU|R)A$Sjm&+7Q8~^~L CPpmos literal 0 HcmV?d00001 From d0ad1cd9f179302b7e79a018f7d5f86849914465 Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Thu, 11 Jan 2024 09:40:24 -0500 Subject: [PATCH 24/36] Example datasets are here! --- NAMESPACE | 3 +- R/google-analytics.R | 5 -- R/youtube.R | 70 +++++++++++++++++- .../example_data/all_ga_metric_list.RDS | Bin 0 -> 419 bytes .../extdata/example_data/example-data-setup.R | 11 +++ inst/extdata/example_data/ga_dimensions.RDS | Bin 116 -> 242 bytes inst/extdata/example_data/ga_link_clicks.RDS | Bin 116 -> 154 bytes .../example_data/youtube_channel_stats.RDS | Bin 0 -> 161 bytes .../example_data/youtube_video_stats.RDS | Bin 0 -> 155 bytes man/get_slido_files.Rd | 3 +- ..._stats.Rd => get_youtube_channel_stats.Rd} | 13 ++-- man/get_youtube_video_stats.Rd | 25 +++++++ 12 files changed, 113 insertions(+), 17 deletions(-) create mode 100644 inst/extdata/example_data/all_ga_metric_list.RDS create mode 100644 inst/extdata/example_data/youtube_channel_stats.RDS create mode 100644 inst/extdata/example_data/youtube_video_stats.RDS rename man/{get_youtube_stats.Rd => get_youtube_channel_stats.Rd} (54%) create mode 100644 man/get_youtube_video_stats.Rd diff --git a/NAMESPACE b/NAMESPACE index 8b27c44..bc17d1b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -19,7 +19,8 @@ export(get_org_repo_list) export(get_repos_metrics) export(get_slido_files) export(get_user_repo_list) -export(get_youtube_stats) +export(get_youtube_channel_stats) +export(get_youtube_video_stats) export(gh_repo_wrapper) export(list_calendly_events) export(request_ga) diff --git a/R/google-analytics.R b/R/google-analytics.R index 2e86e60..1ab9acd 100644 --- a/R/google-analytics.R +++ b/R/google-analytics.R @@ -373,16 +373,11 @@ clean_ga_metrics <- function(metrics = NULL) { stat_names <- metrics$metricHeaders$name clean_df <- metrics$rows } - - if (is.null(clean_df$metricValues)) { - clean_df <- data.frame(metrics = "No data collected yet") - } else{ clean_df <- clean_df %>% dplyr::bind_rows(.id = "website") %>% tidyr::separate(col = "metricValues", sep = ",", into = stat_names) %>% dplyr::mutate_all(~ gsub("list\\(value = c\\(|\\)\\)|\"|", "", .)) %>% dplyr::mutate_at(stat_names, as.numeric) - } return(clean_df) } diff --git a/R/youtube.R b/R/youtube.R index 30cf6bb..8205aab 100644 --- a/R/youtube.R +++ b/R/youtube.R @@ -1,7 +1,7 @@ # Extracting stats from Youtube -#' Get Youtube stats -#' @description This is a function to get a list of files from a Googledrive location +#' Get Youtube channel stats +#' @description This is a function to retrieve statistics for a Youtube channel #' @param channel_id ID of the youtube channel to retrieve stats from. #' https://www.youtube.com/channel/UCBbHCj7kUogAMFyBAzzzfUw or just the "UCBbHCj7kUogAMFyBAzzzfUw" part #' @importFrom httr config accept_json content @@ -11,9 +11,10 @@ #' @examples \dontrun{ #' #' authorize("google") -#' get_get_youtube_stats("UCBbHCj7kUogAMFyBAzzzfUw") +#' youtube_channel_stats <- get_youtube_channel_stats("UCr73I9ZEPbn-3_1CBM57QgQ") +#' #' } -get_youtube_stats <- function(channel_id) { +get_youtube_channel_stats <- function(channel_id, dataformat = "dataframe") { # Get endpoint url url <- "https://youtube.googleapis.com/youtube/v3/channels" @@ -44,5 +45,66 @@ get_youtube_stats <- function(channel_id) { # Process and return results result_content <- httr::content(result, "text") result_list <- jsonlite::fromJSON(result_content) + + if (dataformat == "dataframe") { + result_list <- result_list$items$statistics + } + + return(result_list) +} + + +#' Get Youtube video stats +#' @description This is a function to get a statistics on a Youtube video +#' @param channel_id ID of the youtube video to retrieve stats from. +#' https://www.youtube.com/watch?v=YkYnni-WuaQor just the "YkYnni-WuaQor" part that comes after the `v=` bit. +#' @param dataformat How would you like the data returned to you? Default is a "dataframe" but if you'd like to see the original API list result, put "raw". +#' @importFrom httr config accept_json content +#' @importFrom jsonlite fromJSON +#' @importFrom assertthat assert_that is.string +#' @export +#' @examples \dontrun{ +#' +#' authorize("google") +#' youtube_video_stats <- get_youtube_video_stats("YkYnni-WuaQ") +#' +#' } +get_youtube_video_stats <- function(video_id, dataformat = "dataframe") { + # Get endpoint url + url <- "https://www.googleapis.com/youtube/v3/videos" + + # Get auth token + token <- get_token("google") + config <- httr::config(token = token) + + # Wrapping body parameters in a requests list + if (!is.null(video_id)) { + # If a URL is supplied, only take the ID from it. + if (grepl("https:", video_id)) video_id <- gsub("https://www.youtube.com/watch?v=", "", video_id, fixed = TRUE) + + query <- list( + part = "snippet,contentDetails,statistics", + id = video_id + ) + } else { + stop(paste0("No video_id was given")) + } + + # Get list of topics + result <- httr::GET(url, config = config, query = query, httr::accept_json()) + + if (httr::status_code(result) != 200) { + httr::stop_for_status(result) + } + + # Process and return results + result_content <- httr::content(result, "text") + result_list <- jsonlite::fromJSON(result_content) + + if (dataformat == "dataframe") { + result_list <- result_list$items$statistics + } return(result_list) } + + diff --git a/inst/extdata/example_data/all_ga_metric_list.RDS b/inst/extdata/example_data/all_ga_metric_list.RDS new file mode 100644 index 0000000000000000000000000000000000000000..44cef38bde716d1ec3a3827eec6af8721a645611 GIT binary patch literal 419 zcmV;U0bKqciwFP!000001I1F!N&_(vPItH1Zn0X#;=x0Yg1DvrfOt_*JW5elJSlNI z*3jLoBxx6KK8UX)o_zsdL}*RA*}9DtiWdXfnfboiWai6Uy9gm0Id%crMd_lVe|fsK zkC3%8CXlVHN?Mk?fE-j-wk1DRdX}Ais zE1^ghaE^u9a+$@6m$khpxAiPPBe|p*>UX!aB*pJ{*Uon4>}1Yv=Ij|it)9gVrF=ioyHT0e<8;bSR-AaHT$8R4vBc0TF6p=9U*&U$oDxzKI?~kErUVYu0IIz$Pb#$ zBuTso1S3I2C@?Q(-R6AdFNB2ld=~z>gJIgi)LnLVcv{nB;H6YR^OIUpx8DXK33_U< NhF=rqMqFS7002qz!g>Gz literal 0 HcmV?d00001 diff --git a/inst/extdata/example_data/example-data-setup.R b/inst/extdata/example_data/example-data-setup.R index eb206f3..348ff43 100644 --- a/inst/extdata/example_data/example-data-setup.R +++ b/inst/extdata/example_data/example-data-setup.R @@ -93,3 +93,14 @@ drive_id <- "https://drive.google.com/drive/u/0/folders/1XWXHHyj32Uw_UyaUJrqp6S- slido_data <- get_slido_files(drive_id) save_example_data(slido_data) class(slido_data) + + +####### Youtube Examples + +youtube_channel_stats <- get_youtube_channel_stats("UCr73I9ZEPbn-3_1CBM57QgQ") +save_example_data(youtube_channel_stats) +class(youtube_channel_stats) + +youtube_video_stats <- get_youtube_video_stats("YkYnni-WuaQ") +save_example_data(youtube_video_stats) +class(youtube_video_stats) diff --git a/inst/extdata/example_data/ga_dimensions.RDS b/inst/extdata/example_data/ga_dimensions.RDS index 7e57e4d7971b3d563c0fe7f116f220bbb9c90554..2db9c174fee369f84b06ddafe5ac685c6e78fb51 100644 GIT binary patch literal 242 zcmV2q+o*vih5SLf z`QlnnYy^vr4#|CY{66k3Idlmj9`U^f@hD0{qy1{K#M`QoSkWZP?|1jwRFTU)C&hbrqwk{gzeb_NX7HRo6`qj(~?WGXmubGC87^J!{@ zC?*#cYqU=P#uC4YY@_l}3F18ZY?TuY0mB$3I@uppX62=<+$F_X4wW#EK*s$ sMUsJbEnKmif`Q{{k`D1C95}?HA|V+eVc|IGq-E#P zqYiBhF5(dr)~aZ#+o|%NN@n}Cz-Qf*Ba>YAZdr8XPKZgdMfa!EUq3ZGQrKLvF_LAn TNP~ybA7%!z*t1Jk0xbgoPVOxP diff --git a/inst/extdata/example_data/ga_link_clicks.RDS b/inst/extdata/example_data/ga_link_clicks.RDS index 7e57e4d7971b3d563c0fe7f116f220bbb9c90554..affcf320ff061b0554010797fe33fbe851916249 100644 GIT binary patch delta 125 zcmV-@0D}K?ngNg_cR&s!5DNe?69WqaCrB1b%Vd<46ck(O>z9|8>*c1F6lEsoX6B_9 z>E#!t>jM=rf%QVvvgRe`rWQjDf(f$cWaedu7UiI;P0mRyMpw&~l30?cmsSKc22D9< fQGU4|%y?dqI~#z+|NsC00jYlgV`Dt#+W-Inw~#in delta 87 zcmV-d0I2_(0d$ZfQ4C{{k`6iWaSL!W7%ERV%DISl17|@a zs{(`TC5{Xx;YkeHaY=buh9RQdArm(17;n8}x!+_xi`Ieom{}onVy4WT64j!kGi&0^ zNt0%Y_k>Pbtz)ceDzDU(mR{tSRA@Ns^o}4QZ=tW3x*E{{k`5{G32-wQDl>C4t9O{Wi5n<* zH?(qkHkj%gns1d@)g`KRB68ZysCRSXXN5BfpE!BO&sFS!=Sdwm@yEX2I;Ztd={SbY zm@|L&Y@oED^JZ&H^^H3u&bG#AcB*z(Wx6_~FVuXlb4veuv*mPg9ma$I87^~m{PbXC GU;qF#C^rQF literal 0 HcmV?d00001 diff --git a/man/get_slido_files.Rd b/man/get_slido_files.Rd index 59f5e99..d0f7e23 100644 --- a/man/get_slido_files.Rd +++ b/man/get_slido_files.Rd @@ -21,12 +21,13 @@ get_slido_files( \item{keep_duplicates}{By default we won't keep duplicated files if a two files have the same name. But if you set this to true, duplicates will be returned.} } \description{ -This is a function to get slido response output files. +This is a function to get slido response output files. The slido files must be saved as googlesheets and cannot be xlsx. } \examples{ \dontrun{ drive_id <- "https://drive.google.com/drive/folders/0AJb5Zemj0AAkUk9PVA" +drive_id <-"https://drive.google.com/drive/u/0/folders/1XWXHHyj32Uw_UyaUJrqp6S--hHnM0-7l" slido_data <- get_slido_files(drive_id) } } diff --git a/man/get_youtube_stats.Rd b/man/get_youtube_channel_stats.Rd similarity index 54% rename from man/get_youtube_stats.Rd rename to man/get_youtube_channel_stats.Rd index 13f4451..ebd470a 100644 --- a/man/get_youtube_stats.Rd +++ b/man/get_youtube_channel_stats.Rd @@ -1,22 +1,23 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/youtube.R -\name{get_youtube_stats} -\alias{get_youtube_stats} -\title{Get Youtube stats} +\name{get_youtube_channel_stats} +\alias{get_youtube_channel_stats} +\title{Get Youtube channel stats} \usage{ -get_youtube_stats(channel_id) +get_youtube_channel_stats(channel_id) } \arguments{ \item{channel_id}{ID of the youtube channel to retrieve stats from. https://www.youtube.com/channel/UCBbHCj7kUogAMFyBAzzzfUw or just the "UCBbHCj7kUogAMFyBAzzzfUw" part} } \description{ -This is a function to get a list of files from a Googledrive location +This is a function to retrieve statistics for a Youtube channel } \examples{ \dontrun{ authorize("google") -get_get_youtube_stats("UCBbHCj7kUogAMFyBAzzzfUw") +youtube_channel_stats <- get_youtube_channel_stats("UCr73I9ZEPbn-3_1CBM57QgQ") + } } diff --git a/man/get_youtube_video_stats.Rd b/man/get_youtube_video_stats.Rd new file mode 100644 index 0000000..5bf0b33 --- /dev/null +++ b/man/get_youtube_video_stats.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/youtube.R +\name{get_youtube_video_stats} +\alias{get_youtube_video_stats} +\title{Get Youtube video stats} +\usage{ +get_youtube_video_stats(video_id, dataformat = "dataframe") +} +\arguments{ +\item{dataformat}{How would you like the data returned to you? Default is a "dataframe" but if you'd like to see the original API list result, put "raw".} + +\item{channel_id}{ID of the youtube video to retrieve stats from. +https://www.youtube.com/watch?v=YkYnni-WuaQor just the "YkYnni-WuaQor" part that comes after the `v=` bit.} +} +\description{ +This is a function to get a statistics on a Youtube video +} +\examples{ +\dontrun{ + +authorize("google") +youtube_video_stats <- get_youtube_video_stats("YkYnni-WuaQ") + +} +} From d8cc51c2123ce2756191a649b1e449ef4e828e88 Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Thu, 11 Jan 2024 09:41:39 -0500 Subject: [PATCH 25/36] docs --- R/classes.R | 2 -- inst/extdata/example_data/example-data-setup.R | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 R/classes.R diff --git a/R/classes.R b/R/classes.R deleted file mode 100644 index f83b78c..0000000 --- a/R/classes.R +++ /dev/null @@ -1,2 +0,0 @@ -class_gh <- R6::R6Class("github") - diff --git a/inst/extdata/example_data/example-data-setup.R b/inst/extdata/example_data/example-data-setup.R index 348ff43..92736ee 100644 --- a/inst/extdata/example_data/example-data-setup.R +++ b/inst/extdata/example_data/example-data-setup.R @@ -97,10 +97,12 @@ class(slido_data) ####### Youtube Examples +# Channels youtube_channel_stats <- get_youtube_channel_stats("UCr73I9ZEPbn-3_1CBM57QgQ") save_example_data(youtube_channel_stats) class(youtube_channel_stats) +# Videos youtube_video_stats <- get_youtube_video_stats("YkYnni-WuaQ") save_example_data(youtube_video_stats) class(youtube_video_stats) From f0ca0a81bc3571b7e54f05d39415082b7d4893cf Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Thu, 11 Jan 2024 10:23:28 -0500 Subject: [PATCH 26/36] Make examples better --- R/google-analytics.R | 2 +- R/utils.R | 45 ++++ .../example_data/all_ga_metric_list.RDS | Bin 419 -> 499 bytes inst/extdata/example_data/all_ga_metrics.RDS | Bin 29076 -> 0 bytes .../extdata/example_data/example-data-setup.R | 2 + inst/extdata/example_data/ga_metrics.RDS | Bin 137 -> 266 bytes inst/extdata/example_data/ga_properties.RDS | Bin 363 -> 474 bytes inst/extdata/example_data/gh_repo_list.RDS | Bin 0 -> 2976 bytes inst/extdata/example_data/gh_repo_metrics.RDS | Bin 0 -> 252 bytes .../extdata/example_data/gh_repos_metrics.RDS | Bin 0 -> 272 bytes inst/extdata/example_data/gh_user.RDS | Bin 0 -> 558 bytes vignettes/example-data.Rmd | 213 ++++++++++++++++++ 12 files changed, 261 insertions(+), 1 deletion(-) delete mode 100644 inst/extdata/example_data/all_ga_metrics.RDS create mode 100644 inst/extdata/example_data/gh_repo_list.RDS create mode 100644 inst/extdata/example_data/gh_repo_metrics.RDS create mode 100644 inst/extdata/example_data/gh_repos_metrics.RDS create mode 100644 inst/extdata/example_data/gh_user.RDS create mode 100644 vignettes/example-data.Rmd diff --git a/R/google-analytics.R b/R/google-analytics.R index 1ab9acd..967b6b0 100644 --- a/R/google-analytics.R +++ b/R/google-analytics.R @@ -228,7 +228,7 @@ get_ga_stats <- function(property_id, start_date = "2015-08-14", token = NULL, b ) if (dataformat == "dataframe") { - if (stats_type == "metrics") results <- wrangle_ga_metrics(results) + if (stats_type == "metrics") results <- clean_ga_metrics(results) if (stats_type %in% c("dimensions", "link_clicks")) results <- wrangle_ga_dimensions(results) } diff --git a/R/utils.R b/R/utils.R index d6d999f..a48d4ac 100644 --- a/R/utils.R +++ b/R/utils.R @@ -3,12 +3,57 @@ utils::globalVariables(c( "token", "query_params", "file_name", "accounts" )) +#' Get list of example datasets +#' @description This is a function to retrieve a list of the example datasets included with metricminer +#' @export +#' @examples \dontrun{ +#' +#' list_example_data() +#' +#' } +list_example_data <- function() { + data_list <- + list.files(example_data_folder(), + pattern = ".RDS") + + gsub("\\.RDS$", "", data_list) +} + +#' Get retrieve an example dataset +#' @description This is a function to retrieve a list of the example datasets included with metricminer +#' @return an object in the environment of the same example dataset name that was requested. +#' @export +#' @examples \dontrun{ +#' +#' # You can see the list of example datasets by running: +#' list_example_data() +#' +#' # Then use the datasetes of your interest by calling it with this function +#' get_example_data("gform_info") +#' +#' # Then if you check your global environment you will see "gform_info" included +#' ls() +#' +#' } +get_example_data <- function(dataset_name) { + file_path <- file.path(example_data_folder(), paste0(dataset_name, ".RDS")) + + if (!file.exists(file_path)) { + stop(paste(dataset_name, "does not exist in this package, run list_example_data() to see the available example datasets. Be sure to check for typos.")) + } + assign(dataset_name, readRDS(file_path)) +} + save_example_data <- function(data) { data_name <- deparse(substitute(data)) saveRDS(data, file.path(example_data_folder(), paste0(data_name, ".RDS"))) } +#' Default creds path +#' @param app_name What app set up are you looking for? Supported apps are 'google' 'calendly' and 'github' +#' Get file path to an default credentials RDS +#' @export #' Get file path to an key encryption RDS example_data_folder <- function() { file <- list.files( diff --git a/inst/extdata/example_data/all_ga_metric_list.RDS b/inst/extdata/example_data/all_ga_metric_list.RDS index 44cef38bde716d1ec3a3827eec6af8721a645611..bdc124fcce0bc8f2caf0bd9e421ffc2c39cfd83f 100644 GIT binary patch literal 499 zcmV z$*D!LC%YoH93*+o-tr)Mozin(pl{OMj@Hs*3kMQP1@dUV`6SJZKV9_+AwFsQE#h}@ zNjl+|PlqRjcrVuh;%lo(dpKL9O}5eQ#Ce)74hy)JOaTJBI(V)r<#?L^-tqSx|CQr^ zd-w3`=kLkJAJ^k&Kf?7Or0rFb?L-Nc(y5>yQ$>RtK~r$)95GAp8?a|fE!_c)6q`dR zK}Z8_aVV;OUuB9WRb(ILz}1H==V}5%M@-;AMgo9O=oG#(C}cglB|%D-aeK@Gor6H? zWeI=G1yyKG^o4Vlfp>vV5lS(E3##0bw?$S2w(K38-aUR+IM6JO2D+vtezOLi4%UzH z#-qlS!y1hnS9MnVXmoV^=(^7BeFl+IzA$e0BFA=8n{mi7)A-G$!e&Wl=i3yNU{T6A zh+xXpEWZv|Hmnsn{BPX#pYv8Gr*5-8{g~RqI%&q$%r;^H)at4iX(bBF8{FhcqGc0` zq`ahMFM0IM_$x;*e=2(M%x>@5(y%*-v9D)J-O1D8uqcY4+N4ZO-G+G04e2F}-~LNw p%d)cCT^lKtIc;8k%dX}Ais zE1^ghaE^u9a+$@6m$khpxAiPPBe|p*>UX!aB*pJ{*Uon4>}1Yv=Ij|it)9gVrF=ioyHT0e<8;bSR-AaHT$8R4vBc0TF6p=9U*&U$oDxzKI?~kErUVYu0IIz$Pb#$ zBuTso1S3I2C@?Q(-R6AdFNB2ld=~z>gJIgi)LnLVcv{nB;H6YR^OIUpx8DXK33_U< NhF=rqMqFS7002qz!g>Gz diff --git a/inst/extdata/example_data/all_ga_metrics.RDS b/inst/extdata/example_data/all_ga_metrics.RDS deleted file mode 100644 index b9e258207eca72d0de8e05ec2ef71635465d2bc7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29076 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 Ig!46%^t)s zbakfB$Tb%ZZ{mOYB+%nP9|ij1`1RxcGyBuCZU6i&{|$h+-#IWTmdG4joj#%9^x5Ky z!f~Ul9Zx;nY3PwlAuo5akg}$n$+AZSLrqtwQVA}*WK1;~xvbVkGP2I`#)Jz?WHMaP zT}cgZy8|ZXvNG+O3D#tF^k##v))qypclU!`9*@W@6s`1tIZF8Dp{<%}`|Ul={);}} Q8d|=70OIlqG7ka(0MLYhSpWb4 literal 137 zcmb2|=3oE==I#ec2?+^l35jVb32CfGk`d0%cS>{{k`6iWDGP8nWQwu*Dd@hMzJ9`_ zh)F9Z%$YxV@~oIivla!+j5>7Sz@e5U9j5w*=36Cv)vNQeJOk5;40qjnGUrHA$<`PJ p?Jt+Q8jdZ{e6Dj!|9Z3JdF~@Vybh1-1sGP=GgxgmJbwo00s!r{H=O_g diff --git a/inst/extdata/example_data/ga_properties.RDS b/inst/extdata/example_data/ga_properties.RDS index 3a8929016145ffd897d7c446512315a798c03c3d..34c6dc46a0eda171cd33a8ffbd7e87a2d9ce9d99 100644 GIT binary patch literal 474 zcmV<00VVz)iwFP!000001Fe&5Z__XohP^cf)-sqFLhvJ!=F%*eFA%k*Op1~!^(4|S zQRbK_QggvhHswzve!+yhIvpuv6B3aupFHn-9G_$7>i_`I148cr^!h9W{R{Y6G#NW( zFGDs?*f<0NIAZhLk`yH-Rf_3otg5$)WC#=ov}oYYy? zh&T9>rWNM!bq9+3H4NZv^2PIAe;z(Sy53VK!z4xV$Sr8(0?qiyLYji{vLMK z4|Ct2yR*pg95;mLlV!AojyH9J>C=Y)ccSkWfxnmqFpOZhm_>dtJ@=d-d_o1nAyW^y ztDnc&uJ|E`!UppASs4?YS8cly!EuFQy3+@BnRLl8Nol!`8}ELRlQd6iT9IbLtXBnT zIDb*Gv{jL}zL%&b>}#l*6v>WJOs-ReU3`t#oIU5gX)XNMX<$?0{Q}WCrfq>O9I!3N z(l@f%Vb^{{iW{lz$&b5c-;1wMk(Dv3qLdvA{=TOQmodeh9pCZoe7niq+@num`+WS_ Qow?`s7q~1)FH8gg0Cm;ta{vGU literal 363 zcmV-x0hIn9iwFP!000001D%o4Zh|lrhU>s>;+B}XEZakEZN0LPyh3^{m<#E1OO0#C^Uf3WGQHF&~r~^T9<9wEIKS2Km;we z-lsH43C%;24aN{EiaJ%N9CPPmoD^}M4d7HAL3J{L9PtQcIPS^A-cUxv@oc2bpxmD* zaQjd0x^h)=x`Yh<#j;I%;c}I(wkI^i{=iH!Pm6aXA{mDWoAsQNo))&fFbz+$G}A>( zeeJE0X3aH+2mcQxfNO zznBs~Cn!ACr6LW^m39zjY2=sI&n2BO4vH+NrOwb@Ld$xrIXkvZ;+ng`1!b?{Qcr@D zmJxagF)_)3M3r_`bzdX(@1Qaze1kahGk)?Cv+VcZ=+WUh-lEBSzn;nMSK&K8J^}V) J7*xjs001QDtJ(kn diff --git a/inst/extdata/example_data/gh_repo_list.RDS b/inst/extdata/example_data/gh_repo_list.RDS new file mode 100644 index 0000000000000000000000000000000000000000..74e7776944f42e14fc86ab3c9f8f5e13b26fd591 GIT binary patch literal 2976 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 literal 0 HcmV?d00001 diff --git a/inst/extdata/example_data/gh_repo_metrics.RDS b/inst/extdata/example_data/gh_repo_metrics.RDS new file mode 100644 index 0000000000000000000000000000000000000000..9b2d3f856484febae08f33a894dde41baef3461e GIT binary patch literal 252 zcmV;pKi<;oFp;0r+FR)x%C(UQ0% zDHAuo9FbHFWIbWY%JZ}H;-|dS0FXnGXONd3Lizmk*xY$;?Te+)mCp zXf(I#v*ARtG1!yu1)=N<#<(2s04x%mCpg4a!1r5#v-)OMKTL{HERsD+%@o>cx#dJw zVUaF$BB2M>?a3VNM63eANqY~kgCi69wIj#mUq_I(sfKmzclP>#Ou9qYqv5C>dyB8t zqqroEar9!);(fsGL*U8I9dOBPbP&mU#~f?-CRjKhuMC~q)bxLT&7ktal+Ebp4s)RM}DjbAsNZD3CRjPlH#GhyShPbiDZVDBc>!LCGLy1+gfog z!5Q92uE4~?m+eRjyNCj4;&~*e1f=$J=68=;IQ71 zw5sI0|C8r~$MCj7F$p#%nMJWQ&}$khNl;$WR-0EFg?SJftuX47ciJR<>9k|wyTY}y zV-M@tv3Jn{6Rx8@mhV9Cr41f<`c#5+t$OGRIb}2@!s_ Wmqzba2eSs0ssI(w}id` literal 0 HcmV?d00001 diff --git a/inst/extdata/example_data/gh_user.RDS b/inst/extdata/example_data/gh_user.RDS new file mode 100644 index 0000000000000000000000000000000000000000..27a9f2f7734a3c5cfb66965f19aac33a27dd4b1b GIT binary patch literal 558 zcmV+}0@3{+iwFP!000001D#agZ<{a>b_3CttdTl(&r=`wLI_z>rIDs;>a^FsB-6GR zWWXcV03+KFWl{f+{k2sufQjS%5F3((@4LIt_UAi)9wLM+WLsy*>cbTE*Q<}t0-@d| z7*{YZV4NWv4dDMGC6d!Hr5WL1jt;KD{m+#VcE0w%#~<$R*3Oqt?+xF4BV{g@u8T`7 zF&E>Q%1sdzg7A=Kl4NomveYeHZ+<=Xyy;?gTfUnaB*?Jew4^z02x}H#74GS#^}N?A zPs{mp^t9_L?l+bsYzwHtaQlx8G>i8xlN-YQL&$?cx9Ch4Qz6B^`uQ|6dAe^)z?E?l z!KSugKI~mia%R$9cc&`?5ptSK$}&?cr!x!Vv6(#XoP0QdnSIkao0L$nAZ*TQiNoFh zUl1oDp(Tm@n$h5~*P^P%VTIM-V>8UYc>K*6HF-U4j{Y*&IbTgD)0s2z9B;DnX3Mve z<=p$)EynI?G5rjuL>=r(feDLg))+k+HM3_Ki-_Nv!}_i83!bQlDCP~;23be%^Nmas zEqc`UZ;wfI`F>{eex#peiyA7}mF}A(uu0Xf9jsa-tFi zmBhzU+KOf$WL2_3ZRI%IDOUjMDoGL+R{K)IlN8gVUX&9YB#OrhC~Kw{a!aLb-XCqL w^SlTW8v1qnoMM%R>gdO(@B{$a)saG2Tf)JyR7jCW3W5540_&(_8lwgP0Hux*$N&HU literal 0 HcmV?d00001 diff --git a/vignettes/example-data.Rmd b/vignettes/example-data.Rmd new file mode 100644 index 0000000..777245d --- /dev/null +++ b/vignettes/example-data.Rmd @@ -0,0 +1,213 @@ +--- +title: "R Notebook" +output: html_notebook + toc: true + toc_depth: 2 + toc_float: true +--- + +# Data example creations + +Authorize from secrets + +```{r} +library("metricminer") +``` + +## Calendly Data Examples + +### User + +`get_calendly_user()` returns this type of data. + +```{r} +get_example_data("calendly_user") +class(calendly_user) +str(calendly_user) +``` + +### Events + +`list_calendly_events()` returns this type of data. + +```{r} +get_example_data("calendly_events") +class(calendly_events) +str(calendly_events) +``` + +## GitHub Data Examples + +### Github user + +`get_github_user()` returns this type of data. + +```{r} +get_example_data("gh_user") +class(gh_user) +str(gh_user) +``` + +### Github repository list + +`get_user_repo_list()` returns this type of data. + +```{r} +get_example_data("gh_repo_list") +class(gh_repo_list) +str(gh_repo_list) +``` + +### GitHub repository metrics + +`get_github_metrics()` returns this kind of data. + +```{r} +get_example_data("gh_repo_metrics") +class(gh_repo_metrics) +str(gh_repo_metrics) +``` + +### Multiple GitHub repositories metrics + +`get_repos_metrics()` returns this kind of data. + +```{r} +get_example_data("gh_repos_metrics") +class(gh_repos_metrics) +str(gh_repos_metrics) +``` + +## Google Analytics Examples + +[Read more about the Google Analytics API here](https://developers.google.com/analytics/devguides/reporting/data/v1). + +### GA user info + +`get_ga_user()` returns this kind of data. + +```{r} +get_example_data("ga_user") +class(ga_user) +str(ga_user) +``` + +### GA properties + +`get_ga_properties()` returns this kind of data. + +```{r} +get_example_data("ga_properties") +class(ga_properties) +str(ga_properties) +``` + +### GA property metadata + +`get_ga_metadata()` returns this kind of data. + +```{r} +get_example_data("ga_property_metadata") +class(ga_property_metadata) +str(ga_property_metadata) +``` + +### GA property metrics + +`get_ga_stats()` when `stats_type = "metrics"` returns this kind of data. + +```{r} +get_example_data("ga_metrics") +class(ga_metrics) +str(ga_metrics) +``` + +### GA property dimensions + +`get_ga_stats()` when `stats_type = "dimensions"` returns this kind of data. + +```{r} +get_example_data("ga_dimensions") +class(ga_dimensions) +str(ga_dimensions) +``` + +### GA property link clicks + +`get_ga_stats()` when `stats_type = "link_clicks"` returns this kind of data. + +```{r} +get_example_data("ga_link_clicks") +class(ga_link_clicks) +str(ga_link_clicks) +``` + +### Bulk retrieval of GA property metrics/dims/link clicks + +`get_all_ga_metrics()` returns this kind of data. + +```{r} +get_example_data("all_ga_metric_list") +class(all_ga_metric_list) +str(all_ga_metric_list) +``` + +## Google Form Examples + +### Single google form + +`get_google_form()` returns this kind of data. + +```{r} +get_example_data("gform_info") +class(gform_info) +str(gform_info) +``` + +### Multiple forms info and responses + +`get_multiple_forms()` returns this kind of data. + +```{r} +get_example_data("multiple_gforms") +class(multiple_gforms) +str(multiple_gforms) +``` + +## Slido + +`get_slido_files()` returns this kind of data. + +```{r} +get_example_data("slido_data") +class(slido_data) +str(slido_data) +``` + +## Youtube + +### Channels + +`get_youtube_channel_stats()` returns this kind of data. + +```{r} +get_example_data("youtube_channel_stats") +class(youtube_channel_stats) +str(youtube_channel_stats) +``` + +### Videos + +`get_youtube_video_stats()` returns this kind of data. + +```{r} +get_example_data("youtube_video_stats") +class(youtube_video_stats) +str(youtube_video_stats) +``` + +### Session Info + +```{r} +sessionInfo() +``` From 7f027f75a484008af65a4e94c75c3ad2a3143c53 Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Thu, 11 Jan 2024 10:26:09 -0500 Subject: [PATCH 27/36] Update docs --- NAMESPACE | 10 ++++++++++ man/example_data_folder.Rd | 8 ++++++-- man/get_example_data.Rd | 28 ++++++++++++++++++++++++++++ man/get_youtube_channel_stats.Rd | 2 +- man/list_example_data.Rd | 18 ++++++++++++++++++ 5 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 man/get_example_data.Rd create mode 100644 man/list_example_data.Rd diff --git a/NAMESPACE b/NAMESPACE index bc17d1b..d17b868 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,13 +1,19 @@ # Generated by roxygen2: do not edit by hand +export(Get) +export(RDS) +export(an) export(auth_from_secret) export(authorize) export(calendly_get) export(clean_ga_metrics) export(clean_repo_metrics) export(delete_creds) +export(encryption) +export(file) export(get_all_ga_metrics) export(get_calendly_user) +export(get_example_data) export(get_ga_metadata) export(get_ga_properties) export(get_ga_stats) @@ -22,9 +28,13 @@ export(get_user_repo_list) export(get_youtube_channel_stats) export(get_youtube_video_stats) export(gh_repo_wrapper) +export(key) export(list_calendly_events) +export(list_example_data) +export(path) export(request_ga) export(request_google_forms) +export(to) export(write_to_gsheet) export(write_to_table) import(dplyr) diff --git a/man/example_data_folder.Rd b/man/example_data_folder.Rd index b093f68..dc5ac56 100644 --- a/man/example_data_folder.Rd +++ b/man/example_data_folder.Rd @@ -2,10 +2,14 @@ % Please edit documentation in R/utils.R \name{example_data_folder} \alias{example_data_folder} -\title{Get file path to an key encryption RDS} +\title{Default creds path} \usage{ example_data_folder() } +\arguments{ +\item{app_name}{What app set up are you looking for? Supported apps are 'google' 'calendly' and 'github' +Get file path to an default credentials RDS} +} \description{ -Get file path to an key encryption RDS +Default creds path } diff --git a/man/get_example_data.Rd b/man/get_example_data.Rd new file mode 100644 index 0000000..a9e61b6 --- /dev/null +++ b/man/get_example_data.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{get_example_data} +\alias{get_example_data} +\title{Get retrieve an example dataset} +\usage{ +get_example_data(dataset_name) +} +\value{ +an object in the environment of the same example dataset name that was requested. +} +\description{ +This is a function to retrieve a list of the example datasets included with metricminer +} +\examples{ +\dontrun{ + +# You can see the list of example datasets by running: +list_example_data() + +# Then use the datasetes of your interest by calling it with this function +get_example_data("gform_info") + +# Then if you check your global environment you will see "gform_info" included +ls() + +} +} diff --git a/man/get_youtube_channel_stats.Rd b/man/get_youtube_channel_stats.Rd index ebd470a..92a4525 100644 --- a/man/get_youtube_channel_stats.Rd +++ b/man/get_youtube_channel_stats.Rd @@ -4,7 +4,7 @@ \alias{get_youtube_channel_stats} \title{Get Youtube channel stats} \usage{ -get_youtube_channel_stats(channel_id) +get_youtube_channel_stats(channel_id, dataformat = "dataframe") } \arguments{ \item{channel_id}{ID of the youtube channel to retrieve stats from. diff --git a/man/list_example_data.Rd b/man/list_example_data.Rd new file mode 100644 index 0000000..144f917 --- /dev/null +++ b/man/list_example_data.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{list_example_data} +\alias{list_example_data} +\title{Get list of example datasets} +\usage{ +list_example_data() +} +\description{ +This is a function to retrieve a list of the example datasets included with metricminer +} +\examples{ +\dontrun{ + +list_example_data() + +} +} From 2f58ca92dab98d668e6ba23dedc0f599e82d435c Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Thu, 11 Jan 2024 10:36:32 -0500 Subject: [PATCH 28/36] Fix export line --- NAMESPACE | 9 +-------- R/utils.R | 1 - vignettes/data_dictionary.Rmd | 23 ----------------------- vignettes/example-data.Rmd | 13 ++++++------- 4 files changed, 7 insertions(+), 39 deletions(-) delete mode 100644 vignettes/data_dictionary.Rmd diff --git a/NAMESPACE b/NAMESPACE index d17b868..ef310b9 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,16 +1,12 @@ # Generated by roxygen2: do not edit by hand -export(Get) -export(RDS) -export(an) export(auth_from_secret) export(authorize) export(calendly_get) export(clean_ga_metrics) export(clean_repo_metrics) export(delete_creds) -export(encryption) -export(file) +export(example_data_folder) export(get_all_ga_metrics) export(get_calendly_user) export(get_example_data) @@ -28,13 +24,10 @@ export(get_user_repo_list) export(get_youtube_channel_stats) export(get_youtube_video_stats) export(gh_repo_wrapper) -export(key) export(list_calendly_events) export(list_example_data) -export(path) export(request_ga) export(request_google_forms) -export(to) export(write_to_gsheet) export(write_to_table) import(dplyr) diff --git a/R/utils.R b/R/utils.R index a48d4ac..5992de6 100644 --- a/R/utils.R +++ b/R/utils.R @@ -54,7 +54,6 @@ save_example_data <- function(data) { #' @param app_name What app set up are you looking for? Supported apps are 'google' 'calendly' and 'github' #' Get file path to an default credentials RDS #' @export -#' Get file path to an key encryption RDS example_data_folder <- function() { file <- list.files( pattern = "example_data.md", diff --git a/vignettes/data_dictionary.Rmd b/vignettes/data_dictionary.Rmd deleted file mode 100644 index 48a9f22..0000000 --- a/vignettes/data_dictionary.Rmd +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: "metricminer-data-dictionary" -output: rmarkdown::html_vignette -vignette: > - %\VignetteIndexEntry{getting-started} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} ---- - -```{r, include = FALSE} -knitr::opts_chunk$set( - collapse = TRUE, - comment = "#>" -) -``` - -# Data Dictionary - -### Session info - -```{r} -sessionInfo() -``` diff --git a/vignettes/example-data.Rmd b/vignettes/example-data.Rmd index 777245d..4424784 100644 --- a/vignettes/example-data.Rmd +++ b/vignettes/example-data.Rmd @@ -1,16 +1,15 @@ --- -title: "R Notebook" -output: html_notebook +title: "Example metricminer data" +output: + html_notebook: toc: true - toc_depth: 2 + toc_depth: 3 toc_float: true --- -# Data example creations +# Example metricminer data -Authorize from secrets - -```{r} +``` library("metricminer") ``` From 4ed5f0acc93f6b5aa74274805fcb9bca0d11362a Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Thu, 11 Jan 2024 10:44:06 -0500 Subject: [PATCH 29/36] Bug fixes --- R/google-analytics.R | 1 - R/utils.R | 4 +- R/youtube.R | 3 +- cran-example.R | 21 ---------- tests/testthat/test-google-analytics.R | 4 +- vignettes/example-data.Rmd | 56 +++++++++++++------------- 6 files changed, 34 insertions(+), 55 deletions(-) delete mode 100644 cran-example.R diff --git a/R/google-analytics.R b/R/google-analytics.R index 967b6b0..03c32a8 100644 --- a/R/google-analytics.R +++ b/R/google-analytics.R @@ -270,7 +270,6 @@ link_clicks <- function() { #' Get all metrics for all properties associated with an account #' @description This is a function to gets metrics and dimensions for all properties associated with an account #' @param account_id the account id that you'd like to retrieve stats for all properties associated with it. -#' @param property_ids a vector of property ids for stats to be retrieved for. Note you can only provide one or the other. #' @param token credentials for access to Google using OAuth. `authorize("google")` #' @param dataformat How would you like the data returned to you? Default is a "dataframe" but if you'd like to see the original API list result, put "raw". #' @returns Either a list of dataframes where `metrics`, `dimensions` and `link clicks` are reported. But if `format` is set to "raw" then the original raw API results will be returned diff --git a/R/utils.R b/R/utils.R index 5992de6..9f0a10e 100644 --- a/R/utils.R +++ b/R/utils.R @@ -1,6 +1,6 @@ utils::globalVariables(c( "result", "num", "test_name", "scopes", "set_token", "browseURL", "remove_token", "get_token", "get_github", "get_calendly", "%>%", - "token", "query_params", "file_name", "accounts" + "token", "query_params", "file_name", "accounts", "get_repo_list" )) #' Get list of example datasets @@ -21,6 +21,7 @@ list_example_data <- function() { #' Get retrieve an example dataset #' @description This is a function to retrieve a list of the example datasets included with metricminer +#' @param dataset_name the name of the example dataset to be retrieved from the metricminer package. #' @return an object in the environment of the same example dataset name that was requested. #' @export #' @examples \dontrun{ @@ -51,7 +52,6 @@ save_example_data <- function(data) { } #' Default creds path -#' @param app_name What app set up are you looking for? Supported apps are 'google' 'calendly' and 'github' #' Get file path to an default credentials RDS #' @export example_data_folder <- function() { diff --git a/R/youtube.R b/R/youtube.R index 8205aab..e492708 100644 --- a/R/youtube.R +++ b/R/youtube.R @@ -3,6 +3,7 @@ #' Get Youtube channel stats #' @description This is a function to retrieve statistics for a Youtube channel #' @param channel_id ID of the youtube channel to retrieve stats from. +#' @param dataformat How would you like the data returned to you? Default is a "dataframe" but if you'd like to see the original API list result, put "raw". #' https://www.youtube.com/channel/UCBbHCj7kUogAMFyBAzzzfUw or just the "UCBbHCj7kUogAMFyBAzzzfUw" part #' @importFrom httr config accept_json content #' @importFrom jsonlite fromJSON @@ -56,7 +57,7 @@ get_youtube_channel_stats <- function(channel_id, dataformat = "dataframe") { #' Get Youtube video stats #' @description This is a function to get a statistics on a Youtube video -#' @param channel_id ID of the youtube video to retrieve stats from. +#' @param video_id ID of the youtube video to retrieve stats from. #' https://www.youtube.com/watch?v=YkYnni-WuaQor just the "YkYnni-WuaQor" part that comes after the `v=` bit. #' @param dataformat How would you like the data returned to you? Default is a "dataframe" but if you'd like to see the original API list result, put "raw". #' @importFrom httr config accept_json content diff --git a/cran-example.R b/cran-example.R deleted file mode 100644 index b291148..0000000 --- a/cran-example.R +++ /dev/null @@ -1,21 +0,0 @@ -# CRAN - - library("ggplot2") - library("dlstats") - - download_stats <- cran_stats(c("ottrpal", "metricminer", "conrad", "ari", "text2speech", "mario")) - - download_stats %>% dplyr::summarize(download_total = sum(downloads)) - - download_stats %>% dplyr::group_by(package) %>% - dplyr::summarize(download_total = sum(downloads)) - - - -if (!is.null(download_stats)) { - print(head(download_stats)) - ggplot(download_stats, aes(end, downloads, group=package, color=package)) + - geom_line() + - geom_point() + - scale_y_log10() - } diff --git a/tests/testthat/test-google-analytics.R b/tests/testthat/test-google-analytics.R index cf45a7d..cac866d 100644 --- a/tests/testthat/test-google-analytics.R +++ b/tests/testthat/test-google-analytics.R @@ -27,7 +27,7 @@ test_that("Google Analytics: Stats", { properties_list <- get_ga_properties(account_id = 209776907) - property_id <- gsub("properties/", "", properties_list$properties$name[1]) + property_id <- gsub("properties/", "", properties_list$name[1]) property_metadata <- get_ga_metadata(property_id = property_id) expect_named(property_metadata, c("dimensions", "metrics", "name")) @@ -49,7 +49,7 @@ test_that("Google Analytics: All Stats", { cache = FALSE, in_test = TRUE) - stats_list <- all_ga_metrics(account_id = 209776907) + stats_list <- get_all_ga_metrics(account_id = 209776907) expect_named(stats_list, c("metrics", "dimensions", "link_clicks")) }) diff --git a/vignettes/example-data.Rmd b/vignettes/example-data.Rmd index 4424784..391bade 100644 --- a/vignettes/example-data.Rmd +++ b/vignettes/example-data.Rmd @@ -1,13 +1,13 @@ --- title: "Example metricminer data" -output: - html_notebook: - toc: true - toc_depth: 3 - toc_float: true +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{getting-started} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} --- -# Example metricminer data +# Example metricminer data ``` library("metricminer") @@ -17,7 +17,7 @@ library("metricminer") ### User -`get_calendly_user()` returns this type of data. +`get_calendly_user()` returns this type of data. ```{r} get_example_data("calendly_user") @@ -27,7 +27,7 @@ str(calendly_user) ### Events -`list_calendly_events()` returns this type of data. +`list_calendly_events()` returns this type of data. ```{r} get_example_data("calendly_events") @@ -39,7 +39,7 @@ str(calendly_events) ### Github user -`get_github_user()` returns this type of data. +`get_github_user()` returns this type of data. ```{r} get_example_data("gh_user") @@ -49,7 +49,7 @@ str(gh_user) ### Github repository list -`get_user_repo_list()` returns this type of data. +`get_user_repo_list()` returns this type of data. ```{r} get_example_data("gh_repo_list") @@ -57,9 +57,9 @@ class(gh_repo_list) str(gh_repo_list) ``` -### GitHub repository metrics +### GitHub repository metrics -`get_github_metrics()` returns this kind of data. +`get_github_metrics()` returns this kind of data. ```{r} get_example_data("gh_repo_metrics") @@ -69,7 +69,7 @@ str(gh_repo_metrics) ### Multiple GitHub repositories metrics -`get_repos_metrics()` returns this kind of data. +`get_repos_metrics()` returns this kind of data. ```{r} get_example_data("gh_repos_metrics") @@ -83,7 +83,7 @@ str(gh_repos_metrics) ### GA user info -`get_ga_user()` returns this kind of data. +`get_ga_user()` returns this kind of data. ```{r} get_example_data("ga_user") @@ -93,7 +93,7 @@ str(ga_user) ### GA properties -`get_ga_properties()` returns this kind of data. +`get_ga_properties()` returns this kind of data. ```{r} get_example_data("ga_properties") @@ -103,7 +103,7 @@ str(ga_properties) ### GA property metadata -`get_ga_metadata()` returns this kind of data. +`get_ga_metadata()` returns this kind of data. ```{r} get_example_data("ga_property_metadata") @@ -113,7 +113,7 @@ str(ga_property_metadata) ### GA property metrics -`get_ga_stats()` when `stats_type = "metrics"` returns this kind of data. +`get_ga_stats()` when `stats_type = "metrics"` returns this kind of data. ```{r} get_example_data("ga_metrics") @@ -123,7 +123,7 @@ str(ga_metrics) ### GA property dimensions -`get_ga_stats()` when `stats_type = "dimensions"` returns this kind of data. +`get_ga_stats()` when `stats_type = "dimensions"` returns this kind of data. ```{r} get_example_data("ga_dimensions") @@ -133,7 +133,7 @@ str(ga_dimensions) ### GA property link clicks -`get_ga_stats()` when `stats_type = "link_clicks"` returns this kind of data. +`get_ga_stats()` when `stats_type = "link_clicks"` returns this kind of data. ```{r} get_example_data("ga_link_clicks") @@ -143,7 +143,7 @@ str(ga_link_clicks) ### Bulk retrieval of GA property metrics/dims/link clicks -`get_all_ga_metrics()` returns this kind of data. +`get_all_ga_metrics()` returns this kind of data. ```{r} get_example_data("all_ga_metric_list") @@ -153,9 +153,9 @@ str(all_ga_metric_list) ## Google Form Examples -### Single google form +### Single google form -`get_google_form()` returns this kind of data. +`get_google_form()` returns this kind of data. ```{r} get_example_data("gform_info") @@ -165,7 +165,7 @@ str(gform_info) ### Multiple forms info and responses -`get_multiple_forms()` returns this kind of data. +`get_multiple_forms()` returns this kind of data. ```{r} get_example_data("multiple_gforms") @@ -175,7 +175,7 @@ str(multiple_gforms) ## Slido -`get_slido_files()` returns this kind of data. +`get_slido_files()` returns this kind of data. ```{r} get_example_data("slido_data") @@ -183,11 +183,11 @@ class(slido_data) str(slido_data) ``` -## Youtube +## Youtube ### Channels -`get_youtube_channel_stats()` returns this kind of data. +`get_youtube_channel_stats()` returns this kind of data. ```{r} get_example_data("youtube_channel_stats") @@ -197,7 +197,7 @@ str(youtube_channel_stats) ### Videos -`get_youtube_video_stats()` returns this kind of data. +`get_youtube_video_stats()` returns this kind of data. ```{r} get_example_data("youtube_video_stats") @@ -205,7 +205,7 @@ class(youtube_video_stats) str(youtube_video_stats) ``` -### Session Info +### Session Info ```{r} sessionInfo() From 8f84a2f381dc3e0ab5be919c4194b560e7a22d2b Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Thu, 11 Jan 2024 10:57:27 -0500 Subject: [PATCH 30/36] Update some bugs and add links to API docs --- tests/testthat/test-github.R | 7 ++++++- vignettes/example-data.Rmd | 12 +++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/testthat/test-github.R b/tests/testthat/test-github.R index e0c22e4..d944f80 100644 --- a/tests/testthat/test-github.R +++ b/tests/testthat/test-github.R @@ -3,7 +3,12 @@ test_that("GitHub: get repo list", { auth_from_secret("github", token = Sys.getenv("METRICMINER_GITHUB_PAT"), in_test = TRUE) - repo_list <- get_repo_list(owner = "fhdsl") + repo_list <- get_org_repo_list(owner = "fhdsl") + + expect_type(repo_list[[1]]$id, "integer") + expect_type(repo_list[[1]]$name, "character") + + repo_list <- get_user_repo_list(owner = "cansavvy") expect_type(repo_list[[1]]$id, "integer") expect_type(repo_list[[1]]$name, "character") diff --git a/vignettes/example-data.Rmd b/vignettes/example-data.Rmd index 391bade..f76c5fd 100644 --- a/vignettes/example-data.Rmd +++ b/vignettes/example-data.Rmd @@ -9,12 +9,14 @@ vignette: > # Example metricminer data -``` +```{r} library("metricminer") ``` ## Calendly Data Examples +[Read more about the calendly API docs here](https://developer.calendly.com/api-docs). + ### User `get_calendly_user()` returns this type of data. @@ -37,6 +39,8 @@ str(calendly_events) ## GitHub Data Examples +[Read more about the GitHub API here](https://docs.github.com/en/rest). + ### Github user `get_github_user()` returns this type of data. @@ -153,6 +157,8 @@ str(all_ga_metric_list) ## Google Form Examples +[Read more about the Google Form API here](https://developers.google.com/forms/api/reference/rest). + ### Single google form `get_google_form()` returns this kind of data. @@ -175,6 +181,8 @@ str(multiple_gforms) ## Slido +[You can read more about Slido data here](https://community.slido.com/analytics-and-exports-44). + `get_slido_files()` returns this kind of data. ```{r} @@ -185,6 +193,8 @@ str(slido_data) ## Youtube +[You can read more about the Youtube API here](https://developers.google.com/youtube/v3). + ### Channels `get_youtube_channel_stats()` returns this kind of data. From 1025399a34c5c82c85d6697f42e92ada774d7ca2 Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Thu, 11 Jan 2024 11:07:55 -0500 Subject: [PATCH 31/36] Another bug fix --- R/utils.R | 2 +- man/example_data_folder.Rd | 8 +++----- man/get_all_ga_metrics.Rd | 2 -- man/get_example_data.Rd | 3 +++ man/get_youtube_channel_stats.Rd | 4 +++- man/get_youtube_video_stats.Rd | 6 +++--- vignettes/example-data.Rmd | 11 +++++++++-- 7 files changed, 22 insertions(+), 14 deletions(-) diff --git a/R/utils.R b/R/utils.R index 9f0a10e..995466f 100644 --- a/R/utils.R +++ b/R/utils.R @@ -42,7 +42,7 @@ get_example_data <- function(dataset_name) { if (!file.exists(file_path)) { stop(paste(dataset_name, "does not exist in this package, run list_example_data() to see the available example datasets. Be sure to check for typos.")) } - assign(dataset_name, readRDS(file_path)) + assign(dataset_name, readRDS(file_path), envir = .GlobalEnv) } save_example_data <- function(data) { diff --git a/man/example_data_folder.Rd b/man/example_data_folder.Rd index dc5ac56..b8fa2ae 100644 --- a/man/example_data_folder.Rd +++ b/man/example_data_folder.Rd @@ -2,14 +2,12 @@ % Please edit documentation in R/utils.R \name{example_data_folder} \alias{example_data_folder} -\title{Default creds path} +\title{Default creds path +Get file path to an default credentials RDS} \usage{ example_data_folder() } -\arguments{ -\item{app_name}{What app set up are you looking for? Supported apps are 'google' 'calendly' and 'github' -Get file path to an default credentials RDS} -} \description{ Default creds path +Get file path to an default credentials RDS } diff --git a/man/get_all_ga_metrics.Rd b/man/get_all_ga_metrics.Rd index 81decf0..53517be 100644 --- a/man/get_all_ga_metrics.Rd +++ b/man/get_all_ga_metrics.Rd @@ -12,8 +12,6 @@ get_all_ga_metrics(account_id = NULL, token = NULL, dataformat = "dataframe") \item{token}{credentials for access to Google using OAuth. `authorize("google")`} \item{dataformat}{How would you like the data returned to you? Default is a "dataframe" but if you'd like to see the original API list result, put "raw".} - -\item{property_ids}{a vector of property ids for stats to be retrieved for. Note you can only provide one or the other.} } \value{ Either a list of dataframes where `metrics`, `dimensions` and `link clicks` are reported. But if `format` is set to "raw" then the original raw API results will be returned diff --git a/man/get_example_data.Rd b/man/get_example_data.Rd index a9e61b6..f668415 100644 --- a/man/get_example_data.Rd +++ b/man/get_example_data.Rd @@ -6,6 +6,9 @@ \usage{ get_example_data(dataset_name) } +\arguments{ +\item{dataset_name}{the name of the example dataset to be retrieved from the metricminer package.} +} \value{ an object in the environment of the same example dataset name that was requested. } diff --git a/man/get_youtube_channel_stats.Rd b/man/get_youtube_channel_stats.Rd index 92a4525..c9a3367 100644 --- a/man/get_youtube_channel_stats.Rd +++ b/man/get_youtube_channel_stats.Rd @@ -7,7 +7,9 @@ get_youtube_channel_stats(channel_id, dataformat = "dataframe") } \arguments{ -\item{channel_id}{ID of the youtube channel to retrieve stats from. +\item{channel_id}{ID of the youtube channel to retrieve stats from.} + +\item{dataformat}{How would you like the data returned to you? Default is a "dataframe" but if you'd like to see the original API list result, put "raw". https://www.youtube.com/channel/UCBbHCj7kUogAMFyBAzzzfUw or just the "UCBbHCj7kUogAMFyBAzzzfUw" part} } \description{ diff --git a/man/get_youtube_video_stats.Rd b/man/get_youtube_video_stats.Rd index 5bf0b33..507720e 100644 --- a/man/get_youtube_video_stats.Rd +++ b/man/get_youtube_video_stats.Rd @@ -7,10 +7,10 @@ get_youtube_video_stats(video_id, dataformat = "dataframe") } \arguments{ -\item{dataformat}{How would you like the data returned to you? Default is a "dataframe" but if you'd like to see the original API list result, put "raw".} - -\item{channel_id}{ID of the youtube video to retrieve stats from. +\item{video_id}{ID of the youtube video to retrieve stats from. https://www.youtube.com/watch?v=YkYnni-WuaQor just the "YkYnni-WuaQor" part that comes after the `v=` bit.} + +\item{dataformat}{How would you like the data returned to you? Default is a "dataframe" but if you'd like to see the original API list result, put "raw".} } \description{ This is a function to get a statistics on a Youtube video diff --git a/vignettes/example-data.Rmd b/vignettes/example-data.Rmd index f76c5fd..dcc3f66 100644 --- a/vignettes/example-data.Rmd +++ b/vignettes/example-data.Rmd @@ -1,12 +1,19 @@ --- -title: "Example metricminer data" +title: "example-data" output: rmarkdown::html_vignette vignette: > - %\VignetteIndexEntry{getting-started} + %\VignetteIndexEntry{example-data} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- +```{r, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>" +) +``` + # Example metricminer data ```{r} From 4af87dbbd03cfad77de1f76051ce8e7884132bbb Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Thu, 11 Jan 2024 11:11:55 -0500 Subject: [PATCH 32/36] More bug fixes --- R/utils.R | 6 ++++-- man/get_example_data.Rd | 4 +++- tests/testthat/test-google-analytics.R | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/R/utils.R b/R/utils.R index 995466f..46aa9df 100644 --- a/R/utils.R +++ b/R/utils.R @@ -22,6 +22,7 @@ list_example_data <- function() { #' Get retrieve an example dataset #' @description This is a function to retrieve a list of the example datasets included with metricminer #' @param dataset_name the name of the example dataset to be retrieved from the metricminer package. +#' @param envir By default the example data is saved in the global environment but this parameter allows you to change that if desired. #' @return an object in the environment of the same example dataset name that was requested. #' @export #' @examples \dontrun{ @@ -36,13 +37,14 @@ list_example_data <- function() { #' ls() #' #' } -get_example_data <- function(dataset_name) { +get_example_data <- function(dataset_name, envir = 1) { file_path <- file.path(example_data_folder(), paste0(dataset_name, ".RDS")) if (!file.exists(file_path)) { stop(paste(dataset_name, "does not exist in this package, run list_example_data() to see the available example datasets. Be sure to check for typos.")) } - assign(dataset_name, readRDS(file_path), envir = .GlobalEnv) + assign(dataset_name, readRDS(file_path), envir = as.environment(envir)) + } save_example_data <- function(data) { diff --git a/man/get_example_data.Rd b/man/get_example_data.Rd index f668415..758ef5a 100644 --- a/man/get_example_data.Rd +++ b/man/get_example_data.Rd @@ -4,10 +4,12 @@ \alias{get_example_data} \title{Get retrieve an example dataset} \usage{ -get_example_data(dataset_name) +get_example_data(dataset_name, envir = 1) } \arguments{ \item{dataset_name}{the name of the example dataset to be retrieved from the metricminer package.} + +\item{envir}{By default the example data is saved in the global environment but this parameter allows you to change that if desired.} } \value{ an object in the environment of the same example dataset name that was requested. diff --git a/tests/testthat/test-google-analytics.R b/tests/testthat/test-google-analytics.R index cac866d..0ac7d51 100644 --- a/tests/testthat/test-google-analytics.R +++ b/tests/testthat/test-google-analytics.R @@ -9,7 +9,7 @@ test_that("Google Analytics: Properties", { ga_user <- get_ga_user() properties_list <- get_ga_properties(account_id = 209776907) - expect_named(properties_list$properties, c( + expect_named(properties_list, c( "name", "parent", "createTime", "updateTime", "displayName", "timeZone", "currencyCode", "serviceLevel", "account", "propertyType", "industryCategory" From 5c276e2bfa419b69ec00d16d19f746e6dd731b78 Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Thu, 11 Jan 2024 11:16:20 -0500 Subject: [PATCH 33/36] Add to build ignore --- .Rbuildignore | 3 ++- R/write-data.R | 4 ++-- man/write_to_gsheet.Rd | 2 +- man/write_to_table.Rd | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.Rbuildignore b/.Rbuildignore index 236912a..4b386fa 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -10,4 +10,5 @@ ^docs$ ^pkgdown$ ^inst/extdata/cached-secrets/* -.Rprofile \ No newline at end of file +.Rprofile +resources/* \ No newline at end of file diff --git a/R/write-data.R b/R/write-data.R index 5c63b8d..c97755f 100644 --- a/R/write-data.R +++ b/R/write-data.R @@ -16,7 +16,7 @@ #' } #' -write_to_gsheet <- function() { +write_to_gsheet <- function(input, gsheet, overwrite = FALSE) { } @@ -38,6 +38,6 @@ write_to_gsheet <- function() { #' #' write_to_table(repo_list) #' } -write_to_table <- function() { +write_to_table <- function(input, file_path, overwrite, table_type) { } diff --git a/man/write_to_gsheet.Rd b/man/write_to_gsheet.Rd index 1673926..a6b732f 100644 --- a/man/write_to_gsheet.Rd +++ b/man/write_to_gsheet.Rd @@ -4,7 +4,7 @@ \alias{write_to_gsheet} \title{Writes data to a Googlesheet} \usage{ -write_to_gsheet() +write_to_gsheet(input, gsheet, overwrite = FALSE) } \arguments{ \item{input}{input data to write to a googlesheet} diff --git a/man/write_to_table.Rd b/man/write_to_table.Rd index b1d9616..32f858a 100644 --- a/man/write_to_table.Rd +++ b/man/write_to_table.Rd @@ -4,7 +4,7 @@ \alias{write_to_table} \title{Writes data to a tabular file} \usage{ -write_to_table() +write_to_table(input, file_path, overwrite, table_type) } \arguments{ \item{input}{input data to write to a googlesheet} From c04ebb12e22f1fddcba5fc04049d59ae62457c55 Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Thu, 11 Jan 2024 11:24:45 -0500 Subject: [PATCH 34/36] update example-data.md --- inst/extdata/example_data/example_data.md | 38 +++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/inst/extdata/example_data/example_data.md b/inst/extdata/example_data/example_data.md index e6d52a6..74b37bd 100644 --- a/inst/extdata/example_data/example_data.md +++ b/inst/extdata/example_data/example_data.md @@ -1,3 +1,41 @@ # README for example data This folder includes example data for use with testing and practice tutorials for metricminer. + +These example datasets can be accessed using: + +You can see the list of example datasets by running: +``` +list_example_data() +``` + +Then use the datasetes of your interest by calling it with this function +``` +get_example_data("gform_info") +``` + +Then if you check your global environment you will see "gform_info" included +``` +ls() +``` + +See the `example-data` vignette for more info. + +all_ga_metric_list.RDS +calendly_events.RDS +calendly_user.RDS +ga_dimensions.RDS +ga_link_clicks.RDS +ga_metrics.RDS +ga_properties.RDS +ga_property_metadata.RDS +ga_user.RDS +gform_info.RDS +gh_repo_list.RDS +gh_repo_metrics.RDS +gh_repos_metrics.RDS +gh_user.RDS +multiple_gforms.RDS +slido_data.RDS +youtube_channel_stats.RDS +youtube_video_stats.RDS From 0248fdc4aaadb397052b785d76c845334f4e3810 Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Thu, 11 Jan 2024 11:33:06 -0500 Subject: [PATCH 35/36] Just template GHA added here --- .github/workflows/write-data.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/write-data.yaml b/.github/workflows/write-data.yaml index ac23a40..b530acf 100644 --- a/.github/workflows/write-data.yaml +++ b/.github/workflows/write-data.yaml @@ -1,6 +1,6 @@ on: schedule: - - cron: '*/2 * * * *' + # - cron: '*/2 * * * *' jobs: write-data: @@ -47,4 +47,4 @@ jobs: ) - name: Write data - run: Rscript -e 'source("R/write-data.R")' + run: # Rscript -e 'source("R/write-data.R")' From 586d71720c542ae2f5683119de591dbc06cfed5d Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Tue, 16 Jan 2024 09:53:34 -0500 Subject: [PATCH 36/36] Add GA --- .gitignore | 3 ++- _pkgdown.yml | 10 ++++++++++ .../extdata/example_data/example-data-setup.R | 12 ++++++------ ..._ga_metric_list.RDS => ga_all_metrics.RDS} | Bin ...ultiple_gforms.RDS => gforms_multiple.RDS} | Bin inst/extdata/example_data/gh_repo_list.RDS | Bin 2976 -> 2973 bytes inst/extdata/example_data/gh_repo_metrics.RDS | Bin 252 -> 252 bytes vignettes/example-data.Rmd | 12 ++++++------ 8 files changed, 24 insertions(+), 13 deletions(-) rename inst/extdata/example_data/{all_ga_metric_list.RDS => ga_all_metrics.RDS} (100%) rename inst/extdata/example_data/{multiple_gforms.RDS => gforms_multiple.RDS} (100%) diff --git a/.gitignore b/.gitignore index 7d87576..22abd26 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ inst/extdata/cached-secrets/* testthat-problems.rds .Renviron inst/doc -local_auth.R \ No newline at end of file +local_auth.R +local_auth_2.R \ No newline at end of file diff --git a/_pkgdown.yml b/_pkgdown.yml index d71acfb..29a254d 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -1,4 +1,14 @@ url: ~ template: bootstrap: 5 + includes: + in_header: | + + + diff --git a/inst/extdata/example_data/example-data-setup.R b/inst/extdata/example_data/example-data-setup.R index fcd7e44..f11a667 100644 --- a/inst/extdata/example_data/example-data-setup.R +++ b/inst/extdata/example_data/example-data-setup.R @@ -68,9 +68,9 @@ save_example_data(ga_link_clicks) class(ga_link_clicks) # Bulk retrieval of GA property metrics/dims/link clicks -all_ga_metric_list <- get_all_ga_metrics(account_id = ga_user$id[1]) -save_example_data(all_ga_metric_list) -class(all_ga_metric_list) +ga_all_metrics <- get_all_ga_metrics(account_id = ga_user$id[1]) +save_example_data(ga_all_metrics ) +class(ga_all_metrics ) ####### Google Form Examples @@ -85,9 +85,9 @@ gform_list <- googledrive::drive_find( type = "form") # Now grab the info and responses from these forms -multiple_gforms <- get_multiple_forms(gform_list$id) -save_example_data(multiple_gforms) -class(multiple_gforms) +gforms_multiple <- get_multiple_forms(gform_list$id) +save_example_data(gforms_multiple) +class(gforms_multiple) ####### Slido Results Examples diff --git a/inst/extdata/example_data/all_ga_metric_list.RDS b/inst/extdata/example_data/ga_all_metrics.RDS similarity index 100% rename from inst/extdata/example_data/all_ga_metric_list.RDS rename to inst/extdata/example_data/ga_all_metrics.RDS diff --git a/inst/extdata/example_data/multiple_gforms.RDS b/inst/extdata/example_data/gforms_multiple.RDS similarity index 100% rename from inst/extdata/example_data/multiple_gforms.RDS rename to inst/extdata/example_data/gforms_multiple.RDS diff --git a/inst/extdata/example_data/gh_repo_list.RDS b/inst/extdata/example_data/gh_repo_list.RDS index 74e7776944f42e14fc86ab3c9f8f5e13b26fd591..c9265e7215657223f7ff1322db14592fc4a149f5 100644 GIT binary patch delta 2934 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 JRilDI005H8n{5C9 diff --git a/inst/extdata/example_data/gh_repo_metrics.RDS b/inst/extdata/example_data/gh_repo_metrics.RDS index 9b2d3f856484febae08f33a894dde41baef3461e..ff9ba49dff1e3a3a34f1f4d009165f484e196990 100644 GIT binary patch literal 252 zcmVl)i=VQs0U(DW&mb>7g!1*}xmkN|?u)U{sm}}wsQme$&>6v% z&}dfbv*ARtF?b^13qsi!^l{hU0T?AXPjHC)fS(osSM}pfy%`jt7$tj@nklrya^r!l z!XjuzwvVsX zy|^TeadgsY@zG&>7I?9@4!C4CI*4Tb&Kz6rOt5f0UKzS9Q`7(Xk?$W44UYBQ0RR9l CE_dVr literal 252 zcmV;pKi<;oFp;0r+FR)x%C(UQ0% zDHAuo9FbHFWIbWY%JZ}H;-|dS0FXnGXONd3Lizmk*xY$;?Te+)mCp zXf(I#v*ARtG1!yu1)=N<#<(2s04x%mCpg4a!1r5#v-)OMKTL{HERsD+%@o>cx#dJw zVUaF$BB2M>?a3VNM63eANqY~kgCi69wIj#mUq_I(sfKmzclP>#Ou9qYqv5C>dyB8t zqqroEar9!);(fsGL*U8I9dOBPbP&mU#~f?-CRjKhuMC~q)bxLT