From 10e43fa10fd2590f0b01eab82cb5ca1d83000c9e Mon Sep 17 00:00:00 2001 From: madeliri Date: Mon, 3 Mar 2025 22:03:44 +0300 Subject: [PATCH] redesign work with db (open connection only when action performed) --- CHANGELOG.md | 2 +- app.R | 120 ++++++++++++++++++++++++++------------------------- 2 files changed, 63 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 124050e..cd28a0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ - +number input validation ##### changes - +- redesign work with db (open connection only when action performed) (2024-03-03); - some code refactoring; - replacing NumberImput to TextInput due to correct implement validation; diff --git a/app.R b/app.R index 48defc2..c28b303 100644 --- a/app.R +++ b/app.R @@ -20,7 +20,7 @@ config <- config::get(file = "configs/config.yml") folder_with_schemas <- fs::path("configs/schemas") FILE_SCHEME <- fs::path(folder_with_schemas, "main.xlsx") dbfile <- fs::path("data.sqlite") -DEBUG <- TRUE +DEBUG <- FALSE # TEMP ! NEED TO HANDLE rmarkdown::find_pandoc(dir = "/opt/homebrew/bin/") @@ -53,16 +53,30 @@ inputs_tables_list <- SCHEME_MAIN %>% # SETUP DB ========================== -con <- DBI::dbConnect( - drv = RSQLite::SQLite(), - dbname = dbfile, -) +#' @description Function to open connection to db, disigned to easy dubugging. +make_db_connection <- function(where = "") { + if (DEBUG) message("=== DB CONNECT ", where) + DBI::dbConnect(RSQLite::SQLite(), dbfile) +} -# Init DB (write dummy data to "main" table) +#' @description Function to close connection to db, disigned to easy dubugging and +#' hide warnings. +close_db_connection <- function(where = "") { + tryCatch( + expr = DBI::dbDisconnect(con), + error = function(e) print(e), + warning = function(w) if (DEBUG) message("=!= ALREADY DISCONNECTED ", where), + finally = if (DEBUG) message("=/= DB DISCONNECT ", where) + ) +} + +# establish connection +con <- make_db_connection() + +# init DB (write dummy data to "main" table) if (!"main" %in% dbListTables(con)) { - dummy_df <- get_dummy_df() %>% - mutate(id = "dummy") + dummy_df <- mutate(get_dummy_df(), id = "dummy") # write dummy df into base, then delete dummy row DBI::dbWriteTable(con, "main", dummy_df, append = TRUE) @@ -83,7 +97,7 @@ if (identical(colnames(DBI::dbReadTable(con, "main")), names(inputs_simple_list) if (length(names(inputs_simple_list)) == length(colnames(df_to_rewrite)) && length(form_base_difference) == 0 && length(base_form_difference) == 0) { - warning("changes in scheme file detected: pressuming here simply changed order") + warning("changes in scheme file detected: assuming order changed only") } if (length(names(inputs_simple_list)) == length(colnames(df_to_rewrite)) && @@ -101,6 +115,7 @@ if (identical(colnames(DBI::dbReadTable(con, "main")), names(inputs_simple_list) df_to_rewrite <- df_to_rewrite %>% mutate(!!sym(i) := get_empty_data(inputs_simple_list[i])) } + # reorder due to scheme df_to_rewrite <- df_to_rewrite %>% select(all_of(names(inputs_simple_list))) @@ -112,37 +127,18 @@ if (identical(colnames(DBI::dbReadTable(con, "main")), names(inputs_simple_list) if (length(names(inputs_simple_list)) < length(colnames(df_to_rewrite))) { stop("changes in scheme file detected: some of inputs form was deleted! it may cause data loss!") } - + # cleaning rm(df_to_rewrite, form_base_difference) } - -# write dummies (for test purposes) -# purrr::map( -# .x = 1:300, -# .f = \(x) { -# dummy_df <- purrr::map2( -# .x = purrr::set_names(names(inputs_simple_list)), -# .y = inputs_simple_list, -# .f = \(x_id, y_type) { -# if (y_type %in% c("text", "select_one", "select_multiple")) return("dummy") -# if (y_type %in% c("radio")) return("dummy") -# if (y_type %in% c("date")) return(as.Date("1990-01-01")) -# if (y_type %in% c("number")) return(as.double(999)) -# } -# ) %>% -# as_tibble() %>% -# mutate(id = glue::glue("test{x}")) -# dbWriteTable(con, "main", dummy_df, append = TRUE) -# dbExecute(con, "DELETE FROM main WHERE id = 'dummy'") -# } -# ) +# close connection to prevent data loss +close_db_connection() # INLINE TABLES ===================== # создаем для каждой таблицы объект -inline_tables <- map( +inline_tables <- purrr::map( .x = purrr::set_names(inputs_tables_list), .f = \(x_inline_table_name) { @@ -181,8 +177,8 @@ create_forms <- function(form_id, form_label, form_type) { condition <- filter(SCHEME_MAIN, form_id == {{form_id}}) %>% distinct(condition) %>% pull choices <- filter(SCHEME_MAIN, form_id == {{form_id}}) %>% pull(choices) - # simple text input - if (form_type == "text") { + # simple text or number input + if (form_type %in% c("text", "number")) { form <- shiny::textAreaInput( inputId = form_id, label = tags$span(style = "color: #444444; font-weight: 550;", form_label), @@ -190,16 +186,6 @@ create_forms <- function(form_id, form_label, form_type) { ) } - # simple number input - if (form_type == "number") { - - form <- textAreaInput( - inputId = form_id, - label = tags$span(style = "color: #444444; font-weight: 550;", form_label), - rows = 1 - ) - } - # simple date input if (form_type == "date") { # supress warning while trying keep data form empty by default @@ -223,7 +209,6 @@ create_forms <- function(form_id, form_label, form_type) { choices = choices, selected = NULL, options = list( - # placeholder = "выберите из списка...", create = FALSE, onInitialize = I('function() { this.setValue(""); }') ) @@ -239,7 +224,6 @@ create_forms <- function(form_id, form_label, form_type) { selected = NULL, multiple = TRUE, options = list( - # placeholder = "множественный выбор", create = FALSE, onInitialize = I('function() { this.setValue(""); }') ) @@ -271,7 +255,7 @@ create_forms <- function(form_id, form_label, form_type) { form <- rHandsontableOutput(outputId = form_id) } - # вложенная таблица + # description part if (form_type == "description") { form <- div(HTML(form_label), style = "color:Gray;font-size: 90%;") } @@ -312,8 +296,6 @@ make_cards_fn <- function(sub_group) { # get pages list pages_list <- unique(SCHEME_MAIN$part) -# TODO: replace with unique(SCHEME_MAIN$part) - # get all forms df df_forms <- SCHEME_MAIN %>% distinct(part, subgroup, form_id, form_label, form_type) @@ -383,7 +365,7 @@ modal_clean_all <- modalDialog( # окно для подвтерждения удаления modal_overwrite <- modalDialog( - "Запись с данным id уже существует в базе", + "Запись с данным id уже существует в базе. Это действие перезапишет сохраненные ранее данные.", title = "Перезаписать данные?", footer = tagList( actionButton("cancel_button", "Отмена"), @@ -394,7 +376,7 @@ modal_overwrite <- modalDialog( # окно для подвтерждения удаления modal_load_patients <- modalDialog( - "Загрузить данные пациента", + "Загрузить данные", uiOutput("load_menu"), title = "Загрузить имеющиеся данные", footer = tagList( @@ -500,7 +482,6 @@ server <- function(input, output) { # res_auth$admin }) - # CREATE RHANDSOME TABLES ===================== # записать массив пустых табличек в rhands_tables purrr::walk( @@ -617,6 +598,8 @@ server <- function(input, output) { # сохранить простые данные; observeEvent(input$save_data_button, { req(input$id) + con <- make_db_connection("save_data_button") + on.exit(close_db_connection("save_data_button"), add = TRUE) ## MAIN # собрать все значения по введенным данным; @@ -639,10 +622,10 @@ server <- function(input, output) { values$data <- setNames(result_df, names(inputs_simple_list)) %>% as_tibble() - if (length(dbListTables(con)) == 0) { + if (length(DBI::dbListTables(con)) == 0) { # если база пустая, то просто записываем данные write_all_to_db() - } else if ("main" %in% dbListTables(con)) { + } else if ("main" %in% DBI::dbListTables(con)) { # если главная таблица существует, то проверяем существование id # GET DATA files @@ -653,7 +636,7 @@ server <- function(input, output) { ", .con = con) # получаем список записей с данным id - exist_main_df <- dbGetQuery(con, query) + exist_main_df <- DBI::dbGetQuery(con, query) # проверка по наличию записей с данным ID в базе; if (nrow(exist_main_df) == 0) { @@ -667,13 +650,14 @@ server <- function(input, output) { } }) - ## get list of id's from db ===================== observeEvent(input$load_data_button, { + con <- make_db_connection("load_data_button") + on.exit(close_db_connection("load_data_button")) - if (length(dbListTables(con)) != 0 && "main" %in% dbListTables(con)) { + if (length(dbListTables(con)) != 0 && "main" %in% DBI::dbListTables(con)) { # GET DATA files - ids <- dbGetQuery(con, "SELECT DISTINCT id FROM main") %>% + ids <- DBI::dbGetQuery(con, "SELECT DISTINCT id FROM main") %>% pull output$load_menu <- renderUI({ @@ -699,6 +683,8 @@ server <- function(input, output) { ## load data to input forms ================================== observeEvent(input$read_data, { + con <- make_db_connection("read_data") + on.exit(close_db_connection("read_data"), add = TRUE) # main df read test_read_df <- read_df_from_db_by_id("main", con) @@ -752,6 +738,9 @@ server <- function(input, output) { output$downloadData <- downloadHandler( filename = paste0("d2tra_", format(Sys.time(), "%Y%m%d_%H%M%S"), ".xlsx"), content = function(file) { + con <- make_db_connection("downloadData") + on.exit(close_db_connection("downloadData"), add = TRUE) + # get all data list_of_df <- purrr::map( .x = purrr::set_names(c("main", inputs_tables_list)), @@ -885,6 +874,9 @@ server <- function(input, output) { ## trigger saving function ============= observeEvent(input$data_save, { + con <- make_db_connection("saving data (from modal conf)") + on.exit(close_db_connection("saving data (from modal conf)"), add = TRUE) + # убираем плашку removeModal() @@ -902,6 +894,8 @@ server <- function(input, output) { # FUNCTIONS ============================== ## write all inputs to db ================ write_all_to_db <- function() { + con <- make_db_connection("fn call `write_all_to_db()`") + # on.exit(close_db_connection("fn call `write_all_to_db()`"), add = TRUE) # write main write_df_to_db(values$data, "main", con) @@ -949,6 +943,7 @@ server <- function(input, output) { ## helper function writing dbs ======== write_df_to_db <- function(df, table_name, con) { + # disconnecting on parent function # delete exists data for this id if (table_name %in% dbListTables(con)) { @@ -962,6 +957,9 @@ server <- function(input, output) { ## reading tables from db by name and id ======== read_df_from_db_by_id <- function(table_name, con) { + # DBI::dbConnect(RSQLite::SQLite(), dbfile) + # on.exit(DBI::dbDisconnect(con), add = TRUE) + # check if this table exist if (table_name %in% dbListTables(con)) { # prepare query @@ -977,6 +975,9 @@ server <- function(input, output) { ## reading tables from db all ======== read_df_from_db_all <- function(table_name, con) { + # DBI::dbConnect(RSQLite::SQLite(), dbfile) + # on.exit(DBI::dbDisconnect(con), add = TRUE) + # check if this table exist if (table_name %in% dbListTables(con)) { # prepare query @@ -991,6 +992,9 @@ server <- function(input, output) { ## LOGGING ACTIONS log_action_to_db <- function(action, pat_id = as.character(NA), con) { + # DBI::dbConnect(RSQLite::SQLite(), dbfile) + # on.exit(DBI::dbDisconnect(con), add = TRUE) + action_row <- tibble( user = res_auth$user, action = action,