## ----setup, include = FALSE--------------------------------------------------- knitr::opts_chunk$set(collapse = TRUE, comment = "#>", eval = FALSE) library(regulog) ## ----basic-------------------------------------------------------------------- # library(shiny) # library(regulog) # # server <- function(input, output, session) { # log <- regulog_shiny_init( # session = session, # app = "clinical-review-tool", # version = "1.2.0", # path = "logs/review_audit.rlog" # ) # # observeEvent(input$approve, { # req(nzchar(input$justification)) # log_action(log, # action = "approved", # object = input$dataset_id, # reason = input$justification # ) # showNotification("Approval recorded.", type = "message") # }) # # observeEvent(input$reject, { # req(nzchar(input$rejection_reason)) # log_action(log, # action = "rejected", # object = input$dataset_id, # reason = input$rejection_reason # ) # showNotification("Rejection recorded.", type = "warning") # }) # } ## ----user-check--------------------------------------------------------------- # # In a development context, confirm what user is being captured: # server <- function(input, output, session) { # log <- regulog_shiny_init( # session = session, # app = "review-tool", # version = "1.0.0" # ) # # output$debug_user <- renderText({ # paste("Logging as:", log$user) # }) # } ## ----observer----------------------------------------------------------------- # server <- function(input, output, session) { # log <- regulog_shiny_init( # session = session, # app = "data-review", # version = "2.0.0", # path = "logs/review.rlog" # ) # # # Static strings # regulog_observer(log, session, # eventExpr = input$lock_database, # action = "database_locked", # object = "study_database", # reason = "Database lock confirmed by data manager" # ) # # # Reactive strings — evaluated at the time the event fires # regulog_observer(log, session, # eventExpr = input$approve_record, # action = "approved", # object = reactive(paste0("record_", input$record_id)), # reason = reactive(input$approval_reason) # ) # # regulog_observer(log, session, # eventExpr = input$flag_discrepancy, # action = "flagged", # object = reactive(paste0("record_", input$record_id)), # reason = reactive(paste0("Discrepancy: ", input$discrepancy_detail)) # ) # } ## ----notes-------------------------------------------------------------------- # observeEvent(input$retain_outlier, { # req(nzchar(input$retention_rationale)) # # log_note(log, paste0( # "Outlier retained for subject ", input$subject_id, " at ", # input$visit_label, ": ", input$outlier_value, ". ", # input$retention_rationale # )) # # showNotification( # paste("Retention decision logged for subject", input$subject_id), # type = "message" # ) # }) # # observeEvent(input$resolve_query, { # req(nzchar(input$query_resolution)) # # log_note(log, paste0( # "Query Q-", input$query_id, " resolved: ", # input$query_resolution, # " (resolved by: ", session$user, ")" # )) # }) ## ----changes------------------------------------------------------------------ # observeEvent(input$save_correction, { # req(input$original_value, input$corrected_value, input$correction_reason) # # log_change(log, # object = paste0(input$subject_id, "_", input$field_name), # field = input$field_name, # before = input$original_value, # after = input$corrected_value, # reason = paste0( # input$correction_reason, # " — corrected by ", session$user # ) # ) # # showNotification("Correction recorded in audit trail.", type = "message") # }) ## ----signature---------------------------------------------------------------- # observeEvent(input$sign_off, { # req(nzchar(input$signoff_meaning)) # # log_signature(log, meaning = input$signoff_meaning) # # # Export signed audit trail alongside the lock # export_path <- sprintf( # "exports/audit_%s_%s.csv", # gsub("[^a-zA-Z0-9]", "_", input$study_id), # format(Sys.Date(), "%Y%m%d") # ) # # export_audit_trail(log, # format = "csv", # signed = TRUE, # path = export_path # ) # # showModal(modalDialog( # title = "Sign-off complete", # paste("Signature recorded. Audit trail exported to:", export_path), # easyClose = TRUE # )) # }) ## ----download----------------------------------------------------------------- # output$download_audit <- downloadHandler( # filename = function() { # sprintf("audit_%s.csv", format(Sys.Date(), "%Y%m%d")) # }, # content = function(file) { # export_audit_trail(log, format = "csv", signed = TRUE, path = file) # } # ) # # # In UI: # # downloadButton("download_audit", "Download audit trail (CSV)") # nolint: commented_code_linter ## ----shared------------------------------------------------------------------- # # In global.R — outside server(), evaluated once at startup # shared_log <- regulog_init( # app = "multi-user-review", # version = "1.0.0", # user = "system", # will be overridden per action # path = "logs/shared_audit.rlog" # ) # # server <- function(input, output, session) { # # Resolve per-request user # current_user <- reactive({ # u <- session$user # if (is.null(u) || !nzchar(u)) Sys.info()[["user"]] else u # }) # # observeEvent(input$approve, { # req(nzchar(input$reason)) # log_action(shared_log, # action = "approved", # object = input$record_id, # reason = input$reason, # user = current_user() # explicit per-action user # ) # }) # } ## ----offline------------------------------------------------------------------ # # Verify integrity of a session log # result <- verify_log("logs/review_audit.rlog") # result$intact # # # All signatures # filter_log("logs/review_audit.rlog", type = "SIGNATURE") # # # All actions by a specific user # filter_log("logs/review_audit.rlog", # type = "ACTION", # user = "jsmith" # ) # # # Changes made within a date range # filter_log("logs/review_audit.rlog", # type = "CHANGE", # from = "2026-06-01", # to = "2026-06-30" # ) # # # Export signed CSV from file path — no session required # export_audit_trail("logs/review_audit.rlog", # format = "csv", # signed = TRUE, # path = "exports/review_audit_signed.csv" # ) ## ----shiny-reads-------------------------------------------------------------- # server <- function(input, output, session) { # log <- regulog_shiny_init( # session = session, # app = "data-review", # version = "1.0.0", # path = sprintf("logs/review_%s.rlog", session$token) # ) # # observeEvent(input$load_dataset, { # with_log(log, { # dataset <- read(haven::read_sas, input$dataset_path) # nolint: object_usage_linter # }) # showNotification("Dataset loaded and logged.", type = "message") # }) # } ## ----shiny-rl-read------------------------------------------------------------ # observeEvent(input$load_dataset, { # dataset <- rl_read(log, haven::read_sas, input$dataset_path) # }) ## ----complete-example--------------------------------------------------------- # library(shiny) # library(regulog) # # ui <- fluidPage( # titlePanel("Clinical Data Review"), # sidebarLayout( # sidebarPanel( # selectInput("dataset_id", "Dataset", c("ADSL", "ADAE", "ADLB")), # textAreaInput("justification", "Justification", rows = 3), # actionButton("approve", "Approve", class = "btn-success"), # actionButton("reject", "Reject", class = "btn-danger"), # hr(), # textAreaInput("note_text", "Add note", rows = 3), # actionButton("add_note", "Add note"), # hr(), # textAreaInput("signoff_meaning", "Signature meaning", rows = 3), # actionButton("sign_off", "Sign off"), # hr(), # downloadButton("download_audit", "Download audit trail") # ), # mainPanel( # tableOutput("log_table") # ) # ) # ) # # server <- function(input, output, session) { # log <- regulog_shiny_init( # session = session, # app = "data-review", # version = "1.0.0", # path = tempfile(fileext = ".rlog") # ) # # observeEvent(input$approve, { # req(nzchar(input$justification)) # log_action(log, "approved", input$dataset_id, input$justification) # showNotification("Approval recorded.", type = "message") # }) # # observeEvent(input$reject, { # req(nzchar(input$justification)) # log_action(log, "rejected", input$dataset_id, input$justification) # showNotification("Rejection recorded.", type = "warning") # }) # # observeEvent(input$add_note, { # req(nzchar(input$note_text)) # log_note(log, input$note_text) # showNotification("Note recorded.", type = "message") # }) # # observeEvent(input$sign_off, { # req(nzchar(input$signoff_meaning)) # log_signature(log, input$signoff_meaning) # showNotification("Signature recorded.", type = "message") # }) # # output$download_audit <- downloadHandler( # filename = function() sprintf("audit_%s.csv", Sys.Date()), # content = function(file) { # export_audit_trail(log, format = "csv", signed = TRUE, path = file) # } # ) # # output$log_table <- renderTable({ # input$approve # input$reject # input$add_note # input$sign_off # df <- filter_log(log) # if (nrow(df) == 0L) { # return(NULL) # } # df[, c("entry_id", "type", "action", "object", "reason")] # }) # } # # shinyApp(ui, server)