## ----include = FALSE----------------------------------------------------------
knitr::opts_chunk$set(
    collapse = TRUE,
    comment = "#>"
)

## ----eval = FALSE-------------------------------------------------------------
# install.packages("stenographer")

## ----eval = FALSE-------------------------------------------------------------
# remotes::install_github("dereckmezquita/stenographer")

## -----------------------------------------------------------------------------
box::use(stenographer[Stenographer, LogLevel, messageParallel])

# Create a basic logger
steno <- Stenographer$new()

# Log some messages
steno$info("This is an informational message")
steno$warn("This is a warning")
steno$error("This is an error")

## -----------------------------------------------------------------------------
# Create a custom stenographer
custom_steno <- Stenographer$new(
    level = LogLevel$WARNING,
    file_path = "app.log",
    print_fn = message
)

custom_steno$info("This won't be logged")
custom_steno$warn("This will be logged to console and file")
custom_steno$error("This is an error message")

## -----------------------------------------------------------------------------
box::use(RSQLite[ SQLite ])
box::use(DBI[ dbConnect, dbDisconnect, dbGetQuery ])

# Create a database connection
db <- dbConnect(SQLite(), "log.sqlite")

# Create a Stenographer that logs to the database
db_steno <- Stenographer$new(
    db_conn = db,
    table_name = "app_logs"
)

# Log some messages
db_steno$info("This is logged to the database")
db_steno$warn("This is a warning", data = list(code = 101))
db_steno$error("An error occurred", error = "Division by zero")

# Example of querying the logs
query <- "SELECT * FROM app_logs WHERE level = 'ERROR'"
result <- dbGetQuery(db, query)
print(result)

## -----------------------------------------------------------------------------
context_steno <- Stenographer$new(
    db_conn = db,
    table_name = "context_logs",
    context = list(app_name = "MyApp", version = "1.0.0")
)

context_steno$info("Application started")

# Update context
context_steno$update_context(list(user_id = "12345"))
context_steno$info("User logged in")

# Log an error with context
context_steno$error("Operation failed", data = list(operation = "data_fetch"))

# Example of querying logs with context
query <- "SELECT * FROM context_logs WHERE json_extract(context, '$.user_id') = '12345'"
result <- dbGetQuery(db, query)
print(result)

# Clear context
context_steno$clear_context()
context_steno$info("Context cleared")

## -----------------------------------------------------------------------------
# Create a combined Stenographer
combined_steno <- Stenographer$new(
    level = LogLevel$INFO,
    file_path = "combined_app.log",
    db_conn = db,
    table_name = "combined_logs",
    context = list(app_name = "CombinedApp", version = "2.0.0"),
    print_fn = messageParallel,
    format_fn = function(level, msg) {
        # manipulate the message before logging
        msg <- gsub("API_KEY=[^\\s]+", "API_KEY=***", msg)
        return(paste(level, msg))
    }
)

# Log some messages
combined_steno$info("Application started")
combined_steno$warn("Low memory", data = list(available_mb = 100))
combined_steno$error("Database connection failed", error = "Connection timeout")

# Update context
combined_steno$update_context(list(user_id = "67890"))
combined_steno$info("User action", data = list(action = "button_click"))

# Example of a more complex query using context and data
query <- "
SELECT *
FROM combined_logs
WHERE json_extract(context, '$.app_name') = 'CombinedApp'
  AND json_extract(data, '$.available_mb') < 200
"
result <- dbGetQuery(db, query)
print(result)

# Don't forget to close the database connection when you're done
dbDisconnect(db)

## -----------------------------------------------------------------------------
box::use(stenographer[valueCoordinates])

# Create a sample dataset with some issues
df <- data.frame(
    a = c(1, NA, 3, 4, 5),
    b = c(2, 4, NA, 8, 10),
    c = c(3, 6, 9, NA, 15)
)

# Create a Stenographer
steno <- Stenographer$new()

# Find coordinates of NA values
na_coords <- valueCoordinates(df)

if (nrow(na_coords) > 0) {
    steno$warn(
        "NA values found in the dataset",
        data = list(
            na_locations = na_coords
        )
    )
}

## -----------------------------------------------------------------------------
box::use(stenographer[tableToString])

steno <- Stenographer$new()

process_data <- function(df) {
    tryCatch({
        result <- df$a / df$b
        if (any(is.infinite(result))) {
            inf_coords <- valueCoordinates(data.frame(result), Inf)
            steno$error(
                "Division by zero occurred",
                data = list(
                    infinite_values = inf_coords,
                    dataset_preview = tableToString(df)
                )
            )
            cat("Division by zero error")
        }
        return(result)
    }, error = function(e) {
        steno$error(
            paste("An error occurred while processing data:", e$message),
            data = list(dataset_preview = tableToString(df)),
            error = e
        )
        cat(e)
    })
}

# Test the function with problematic data
df <- data.frame(a = c(1, 2, 3), b = c(0, 2, 0))
process_data(df)

## -----------------------------------------------------------------------------
box::use(future)
box::use(future.apply[future_lapply])

steno <- Stenographer$new(print_fn = messageParallel)

future::plan(future::multisession, workers = 2)

result <- future_lapply(1:5, function(i) {
    messageParallel(sprintf("Processing item %d", i))
    if (i == 3) {
        steno$warn(sprintf("Warning for item %d", i))
    }
    return(i * 2)
})

future::plan(future::sequential)