Nothing
#' @title Set local (in-memory) storage
#' @name set_local_storage
#' @description Reset storage to the default in-memory backend.
#' @param gitstats A GitStats object.
#' @return A `GitStats` object (invisibly).
#' @examples
#' \dontrun{
#' my_gitstats <- create_gitstats() |>
#' set_local_storage()
#' }
#' @export
set_local_storage <- function(gitstats) {
gitstats$set_local_storage()
}
#' @title Set PostgreSQL storage
#' @name set_postgres_storage
#' @description Persist GitStats data in a PostgreSQL database. R classes,
#' custom attributes, and column types are preserved via a `_metadata` table.
#' Requires `DBI`, `RPostgres`, and `jsonlite` packages.
#' @param gitstats A GitStats object.
#' @param host A character, database host.
#' @param port An integer, database port.
#' @param dbname A character, database name.
#' @param user A character, database user.
#' @param password A character, database password.
#' @param schema A character, database schema (default `"git_stats"`).
#' @param ... Additional arguments passed to
#' `DBI::dbConnect(RPostgres::Postgres(), ...)`.
#' @return A `GitStats` object (invisibly).
#' @examples
#' \dontrun{
#' my_gitstats <- create_gitstats() |>
#' set_postgres_storage(
#' dbname = "my_database",
#' host = "localhost",
#' user = "postgres",
#' password = "secret"
#' )
#' }
#' @export
set_postgres_storage <- function(gitstats,
host = NULL,
port = NULL,
dbname = NULL,
user = NULL,
password = NULL,
schema = "git_stats",
...) {
gitstats$set_postgres_storage(
host = host,
port = port,
dbname = dbname,
user = user,
password = password,
schema = schema,
...
)
}
#' @title Set SQLite storage
#' @name set_sqlite_storage
#' @description Persist GitStats data in a SQLite database. R classes,
#' custom attributes, and column types are preserved via a `_metadata` table.
#' Requires `DBI`, `RSQLite`, and `jsonlite` packages.
#' @param gitstats A GitStats object.
#' @param dbname A character, path to SQLite file. Defaults to `":memory:"`
#' for an in-memory database.
#' @return A `GitStats` object (invisibly).
#' @examples
#' \dontrun{
#' # File-based
#' my_gitstats <- create_gitstats() |>
#' set_sqlite_storage(dbname = "gitstats.sqlite")
#'
#' # In-memory
#' my_gitstats <- create_gitstats() |>
#' set_sqlite_storage()
#' }
#' @export
set_sqlite_storage <- function(gitstats, dbname = ":memory:") {
gitstats$set_sqlite_storage(dbname = dbname)
}
#' @title Get data from `GitStats` storage
#' @name get_storage
#' @description Retrieves whole or particular data (see `storage` parameter)
#' pulled earlier with `GitStats`.
#' @param gitstats A GitStats object.
#' @param storage A character, type of the data you want to get from storage:
#' `commits`, `repositories`, `release_logs`, `users`, `files`,
#' `files_structure`, `R_package_usage` or `release_logs`.
#' @return A list of tibbles (if `storage` set to `NULL`) or a tibble (if
#' `storage` defined).
#' @examples
#' \dontrun{
#' my_gitstats <- create_gitstats() |>
#' set_github_host(
#' token = Sys.getenv("GITHUB_PAT"),
#' orgs = c("r-world-devs", "openpharma")
#' )
#' get_release_logs(my_gistats, since = "2024-01-01")
#' get_repos(my_gitstats)
#'
#' release_logs <- get_storage(
#' gitstats = my_gitstats,
#' storage = "release_logs"
#' )
#' }
#' @export
get_storage <- function(gitstats,
storage = NULL) {
gitstats$get_storage(
storage = storage
)
}
#' @title Remove a table from `GitStats` storage
#' @name remove_from_storage
#' @description Removes a named table from the active storage backend.
#' @param gitstats A GitStats object.
#' @param storage A character, name of the table to remove (e.g. `"commits"`,
#' `"repositories"`).
#' @return A `GitStats` object (invisibly).
#' @examples
#' \dontrun{
#' my_gitstats <- create_gitstats() |>
#' set_github_host(
#' token = Sys.getenv("GITHUB_PAT"),
#' orgs = "r-world-devs"
#' )
#' get_commits(my_gitstats, since = "2024-01-01")
#' remove_from_storage(my_gitstats, storage = "commits")
#' }
#' @export
remove_from_storage <- function(gitstats, storage) {
gitstats$remove_from_storage(storage = storage)
}
#' @title Remove PostgreSQL storage
#' @name remove_postgres_storage
#' @description Drops the GitStats schema (with all tables and metadata) from
#' the PostgreSQL database, closes the connection, and reverts storage to the
#' default in-memory local backend. Errors if no PostgreSQL backend is
#' currently set.
#' @param gitstats A GitStats object.
#' @return A `GitStats` object (invisibly).
#' @examples
#' \dontrun{
#' my_gitstats <- create_gitstats() |>
#' set_postgres_storage(
#' dbname = "my_database",
#' host = "localhost",
#' user = "postgres",
#' password = "secret"
#' )
#' remove_postgres_storage(my_gitstats)
#' }
#' @export
remove_postgres_storage <- function(gitstats) {
gitstats$remove_postgres_storage()
}
#' @title Remove SQLite storage
#' @name remove_sqlite_storage
#' @description Closes the SQLite connection and, for file-based databases,
#' deletes the database file. For in-memory databases the data is simply
#' discarded. Storage is reverted to the default in-memory local backend.
#' Errors if no SQLite backend is currently set.
#' @param gitstats A GitStats object.
#' @return A `GitStats` object (invisibly).
#' @examples
#' \dontrun{
#' my_gitstats <- create_gitstats() |>
#' set_sqlite_storage(dbname = "gitstats.sqlite")
#' remove_sqlite_storage(my_gitstats)
#' }
#' @export
remove_sqlite_storage <- function(gitstats) {
gitstats$remove_sqlite_storage()
}
#' @title Get metadata for a storage table
#' @name get_storage_metadata
#' @description Retrieves metadata (R classes, custom attributes, column types)
#' for a table stored in the active storage backend.
#' @param gitstats A GitStats object.
#' @param storage A character, name of the table (e.g. `"commits"`,
#' `"repositories"`). If `NULL` (default), metadata for all tables in storage
#' will be returned.
#' @return A list with metadata fields: `class`, `attributes`, and (for
#' database backends) `column_types`.
#' @examples
#' \dontrun{
#' my_gitstats <- create_gitstats() |>
#' set_github_host(
#' token = Sys.getenv("GITHUB_PAT"),
#' orgs = "r-world-devs"
#' )
#' get_commits(my_gitstats, since = "2024-01-01")
#' get_storage_metadata(my_gitstats, storage = "commits")
#' }
#' @export
get_storage_metadata <- function(gitstats, storage = NULL) {
gitstats$get_storage_metadata(storage = storage)
}
#' @noRd
Storage <- R6::R6Class(
classname = "Storage",
public = list(
save = function(name, data) {
stop("Not implemented")
},
load = function(name) {
stop("Not implemented")
},
exists = function(name) {
stop("Not implemented")
},
remove = function(name) {
stop("Not implemented")
},
drop_storage = function() {
stop("Not implemented")
},
list = function() {
stop("Not implemented")
},
get_metadata = function(name = NULL) {
stop("Not implemented")
},
is_db = function() {
FALSE
}
)
)
#' @noRd
StorageLocal <- R6::R6Class(
classname = "StorageLocal",
inherit = Storage,
public = list(
initialize = function() {
private$data <- list()
},
save = function(name, data) {
private$data[[name]] <- data
},
load = function(name) {
private$data[[name]]
},
exists = function(name) {
!is.null(private$data[[name]])
},
remove = function(name) {
private$data[[name]] <- NULL
},
drop_storage = function() {
private$data <- list()
},
list = function() {
names(purrr::discard(private$data, is.null))
},
get_metadata = function(name = NULL) {
if (is.null(name)) {
names_list <- self$list()
if (length(names_list) == 0) {
return(private$empty_metadata_tibble())
}
build_row <- private$build_metadata_row
rows <- purrr::map(names_list, function(n) build_row(n))
return(dplyr::bind_rows(rows))
}
data <- private$data[[name]]
if (is.null(data)) {
return(private$empty_metadata_tibble())
}
private$build_metadata_row(name)
}
),
private = list(
data = list(),
build_metadata_row = function(name) {
data <- private$data[[name]]
custom_attrs <- setdiff(
names(attributes(data)),
c("names", "row.names", "class")
)
attrs <- list()
for (attr_name in custom_attrs) {
attrs[[attr_name]] <- attr(data, attr_name)
}
dplyr::tibble(
table_name = name,
class = list(class(data)),
attributes = list(attrs)
)
},
empty_metadata_tibble = function() {
dplyr::tibble(
table_name = character(),
class = list(),
attributes = list()
)
}
)
)
#' @noRd
StoragePostgres <- R6::R6Class(
classname = "StoragePostgres",
inherit = Storage,
public = list(
initialize = function(schema = "git_stats", ...) {
check_if_package_installed("DBI")
check_if_package_installed("RPostgres")
check_if_package_installed("jsonlite")
private$conn <- DBI::dbConnect(RPostgres::Postgres(), ...)
if (!DBI::dbIsValid(private$conn)) {
cli::cli_abort("Failed to connect to the database.")
}
private$schema <- schema
private$ensure_schema()
private$ensure_metadata_table()
},
save = function(name, data) {
qualified <- private$qualified_name(name)
meta <- list(
class = class(data),
attributes = list()
)
custom_attrs <- setdiff(
names(attributes(data)),
c("names", "row.names", "class")
)
for (attr_name in custom_attrs) {
meta$attributes[[attr_name]] <- attr(data, attr_name)
}
plain_data <- data
class(plain_data) <- class(plain_data)[!grepl("^gitstats_", class(plain_data))]
serialized <- private$serialize_columns(plain_data)
plain_data <- serialized$data
meta$column_types <- serialized$column_types
DBI::dbWriteTable(
private$conn,
DBI::Id(schema = private$schema, table = name),
plain_data,
overwrite = TRUE
)
private$save_metadata(name, meta)
},
load = function(name) {
if (!self$exists(name)) {
return(NULL)
}
data <- DBI::dbReadTable(
private$conn,
DBI::Id(schema = private$schema, table = name)
) |>
dplyr::as_tibble()
meta <- private$load_metadata(name)
if (!is.null(meta)) {
if (!is.null(meta$column_types)) {
data <- private$restore_columns(data, meta$column_types)
}
if (!is.null(meta$class)) {
class(data) <- meta$class
}
if (!is.null(meta$attributes)) {
for (attr_name in names(meta$attributes)) {
attr(data, attr_name) <- meta$attributes[[attr_name]]
}
}
}
return(data)
},
exists = function(name) {
DBI::dbExistsTable(
private$conn,
DBI::Id(schema = private$schema, table = name)
)
},
remove = function(name) {
if (self$exists(name)) {
DBI::dbRemoveTable(
private$conn,
DBI::Id(schema = private$schema, table = name)
)
DBI::dbExecute(
private$conn,
glue::glue(
"DELETE FROM {private$schema}._metadata
WHERE dataset_name = $1"
),
params = list(name)
)
}
invisible(NULL)
},
drop_storage = function() {
DBI::dbExecute(
private$conn,
glue::glue("DROP SCHEMA IF EXISTS {private$schema} CASCADE")
)
DBI::dbDisconnect(private$conn)
private$conn <- NULL
},
list = function() {
result <- DBI::dbGetQuery(
private$conn,
glue::glue(
"SELECT table_name FROM information_schema.tables
WHERE table_schema = $1 AND table_name != '_metadata'"
),
params = list(private$schema)
)
result$table_name
},
get_metadata = function(name = NULL) {
if (is.null(name)) {
names_list <- self$list()
if (length(names_list) == 0) {
return(private$empty_metadata_tibble())
}
build_row <- private$build_metadata_row
rows <- purrr::map(names_list, function(n) build_row(n))
return(dplyr::bind_rows(rows))
}
if (!self$exists(name)) {
return(private$empty_metadata_tibble())
}
private$build_metadata_row(name)
},
is_db = function() {
TRUE
}
),
private = list(
conn = NULL,
schema = NULL,
finalize = function() {
if (!is.null(private$conn) && DBI::dbIsValid(private$conn)) {
DBI::dbDisconnect(private$conn)
}
},
ensure_schema = function() {
DBI::dbExecute(
private$conn,
glue::glue("CREATE SCHEMA IF NOT EXISTS {private$schema}")
)
},
ensure_metadata_table = function() {
meta_table <- DBI::Id(schema = private$schema, table = "_metadata")
if (!DBI::dbExistsTable(private$conn, meta_table)) {
DBI::dbExecute(
private$conn,
glue::glue(
"CREATE TABLE {private$schema}._metadata (
dataset_name TEXT PRIMARY KEY,
metadata JSONB NOT NULL
)"
)
)
}
},
qualified_name = function(name) {
paste0(private$schema, ".", name)
},
save_metadata = function(name, meta) {
meta_json <- jsonlite::toJSON(meta, auto_unbox = TRUE)
DBI::dbExecute(
private$conn,
glue::glue(
"INSERT INTO {private$schema}._metadata (dataset_name, metadata)
VALUES ($1, $2)
ON CONFLICT (dataset_name) DO UPDATE SET metadata = $2"
),
params = list(name, as.character(meta_json))
)
},
load_metadata = function(name) {
result <- DBI::dbGetQuery(
private$conn,
glue::glue(
"SELECT metadata FROM {private$schema}._metadata
WHERE dataset_name = $1"
),
params = list(name)
)
if (nrow(result) == 0) {
return(NULL)
}
jsonlite::fromJSON(result$metadata[[1]])
},
serialize_columns = function(data) {
column_types <- list()
for (col in names(data)) {
if (inherits(data[[col]], "difftime")) {
column_types[[col]] <- list(
type = "difftime",
units = attr(data[[col]], "units")
)
data[[col]] <- as.numeric(data[[col]])
} else if (inherits(data[[col]], "POSIXct")) {
column_types[[col]] <- list(type = "POSIXct")
data[[col]] <- format(data[[col]], "%Y-%m-%dT%H:%M:%S")
}
}
list(data = data, column_types = column_types)
},
restore_columns = function(data, column_types) {
for (col in names(column_types)) {
if (!(col %in% names(data))) next
col_type <- column_types[[col]]
type <- if (is.list(col_type)) col_type$type else col_type
if (type == "difftime") {
units <- if (is.list(col_type)) col_type$units else "days"
data[[col]] <- as.difftime(
as.numeric(data[[col]]),
units = units
)
} else if (type == "POSIXct") {
data[[col]] <- as.POSIXct(data[[col]], format = "%Y-%m-%dT%H:%M:%S")
}
}
data
},
build_metadata_row = function(name) {
meta <- private$load_metadata(name)
if (is.null(meta)) {
return(dplyr::tibble(
table_name = name,
class = list(NULL),
attributes = list(list())
))
}
dplyr::tibble(
table_name = name,
class = list(meta$class),
attributes = list(meta$attributes %||% list())
)
},
empty_metadata_tibble = function() {
dplyr::tibble(
table_name = character(),
class = list(),
attributes = list()
)
}
)
)
#' @noRd
StorageSQLite <- R6::R6Class(
classname = "StorageSQLite",
inherit = Storage,
public = list(
initialize = function(dbname = ":memory:") {
check_if_package_installed("DBI")
check_if_package_installed("RSQLite")
check_if_package_installed("jsonlite")
private$dbname <- dbname
private$conn <- DBI::dbConnect(RSQLite::SQLite(), dbname = dbname)
private$ensure_metadata_table()
},
save = function(name, data) {
meta <- list(
class = class(data),
attributes = list()
)
custom_attrs <- setdiff(
names(attributes(data)),
c("names", "row.names", "class")
)
for (attr_name in custom_attrs) {
meta$attributes[[attr_name]] <- attr(data, attr_name)
}
plain_data <- data
class(plain_data) <- class(plain_data)[!grepl("^gitstats_", class(plain_data))]
serialized <- private$serialize_columns(plain_data)
plain_data <- serialized$data
meta$column_types <- serialized$column_types
DBI::dbWriteTable(private$conn, name, plain_data, overwrite = TRUE)
private$save_metadata(name, meta)
},
load = function(name) {
if (!self$exists(name)) {
return(NULL)
}
data <- DBI::dbReadTable(private$conn, name) |>
dplyr::as_tibble()
meta <- private$load_metadata(name)
if (!is.null(meta)) {
if (!is.null(meta$column_types)) {
data <- private$restore_columns(data, meta$column_types)
}
if (!is.null(meta$class)) {
class(data) <- meta$class
}
if (!is.null(meta$attributes)) {
for (attr_name in names(meta$attributes)) {
attr(data, attr_name) <- meta$attributes[[attr_name]]
}
}
}
return(data)
},
exists = function(name) {
DBI::dbExistsTable(private$conn, name)
},
remove = function(name) {
if (self$exists(name)) {
DBI::dbRemoveTable(private$conn, name)
DBI::dbExecute(
private$conn,
"DELETE FROM _metadata WHERE dataset_name = ?",
params = list(name)
)
}
invisible(NULL)
},
drop_storage = function() {
if (!is.null(private$conn) && DBI::dbIsValid(private$conn)) {
DBI::dbDisconnect(private$conn)
private$conn <- NULL
}
if (!is.null(private$dbname) && private$dbname != ":memory:" &&
file.exists(private$dbname)) {
file.remove(private$dbname)
}
},
list = function() {
tables <- DBI::dbListTables(private$conn)
tables[tables != "_metadata"]
},
get_metadata = function(name = NULL) {
if (is.null(name)) {
names_list <- self$list()
if (length(names_list) == 0) {
return(private$empty_metadata_tibble())
}
build_row <- private$build_metadata_row
rows <- purrr::map(names_list, function(n) build_row(n))
return(dplyr::bind_rows(rows))
}
if (!self$exists(name)) {
return(private$empty_metadata_tibble())
}
private$build_metadata_row(name)
},
is_db = function() {
TRUE
}
),
private = list(
conn = NULL,
dbname = NULL,
finalize = function() {
if (!is.null(private$conn) && DBI::dbIsValid(private$conn)) {
DBI::dbDisconnect(private$conn)
}
},
ensure_metadata_table = function() {
if (!DBI::dbExistsTable(private$conn, "_metadata")) {
DBI::dbExecute(
private$conn,
"CREATE TABLE _metadata (
dataset_name TEXT PRIMARY KEY,
metadata TEXT NOT NULL
)"
)
}
},
save_metadata = function(name, meta) {
meta_json <- jsonlite::toJSON(meta, auto_unbox = TRUE)
DBI::dbExecute(
private$conn,
"DELETE FROM _metadata WHERE dataset_name = ?",
params = list(name)
)
DBI::dbExecute(
private$conn,
"INSERT INTO _metadata (dataset_name, metadata) VALUES (?, ?)",
params = list(name, as.character(meta_json))
)
},
load_metadata = function(name) {
result <- DBI::dbGetQuery(
private$conn,
"SELECT metadata FROM _metadata WHERE dataset_name = ?",
params = list(name)
)
if (nrow(result) == 0) {
return(NULL)
}
jsonlite::fromJSON(result$metadata[[1]])
},
serialize_columns = function(data) {
column_types <- list()
for (col in names(data)) {
if (inherits(data[[col]], "difftime")) {
column_types[[col]] <- list(
type = "difftime",
units = attr(data[[col]], "units")
)
data[[col]] <- as.numeric(data[[col]])
} else if (inherits(data[[col]], "POSIXct")) {
column_types[[col]] <- list(type = "POSIXct")
data[[col]] <- format(data[[col]], "%Y-%m-%dT%H:%M:%S")
}
}
list(data = data, column_types = column_types)
},
restore_columns = function(data, column_types) {
for (col in names(column_types)) {
if (!(col %in% names(data))) next
col_type <- column_types[[col]]
type <- if (is.list(col_type)) col_type$type else col_type
if (type == "difftime") {
units <- if (is.list(col_type)) col_type$units else "days"
data[[col]] <- as.difftime(
as.numeric(data[[col]]),
units = units
)
} else if (type == "POSIXct") {
data[[col]] <- as.POSIXct(data[[col]], format = "%Y-%m-%dT%H:%M:%S")
}
}
data
},
build_metadata_row = function(name) {
meta <- private$load_metadata(name)
if (is.null(meta)) {
return(dplyr::tibble(
table_name = name,
class = list(NULL),
attributes = list(list())
))
}
dplyr::tibble(
table_name = name,
class = list(meta$class),
attributes = list(meta$attributes %||% list())
)
},
empty_metadata_tibble = function() {
dplyr::tibble(
table_name = character(),
class = list(),
attributes = list()
)
}
)
)
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.