Compare commits

..

15 Commits

Author SHA1 Message Date
b5260a510f Merge branch 'main' of https://gitea.madelirihs.ru/madeliri/shiny_form 2026-04-17 14:48:00 +03:00
a000a4e123 gitupdate 2026-04-17 14:47:01 +03:00
f0063f3f83 Merge branch 'main' of https://gitea.madelirihs.ru/madeliri/shiny_form 2026-04-16 17:33:16 +03:00
20f0d52f12 feat: добавление новых строк в формы select_one, select_multiple 2026-04-16 17:33:11 +03:00
65a85d330c Merge branch 'main' of https://gitea.madelirihs.ru/madeliri/shiny_form 2026-04-15 20:38:11 +03:00
e60a21013a what 2026-04-15 20:38:10 +03:00
68faf06e38 fix: корректная работа во вложенных формах при добавлении нового ключа 2026-04-15 17:26:35 +03:00
f344859f92 fix: nested forms for select_one and et cetera 2026-04-15 15:02:33 +03:00
d196715f50 fix: проверка на длину названий вложенных таблиц 2026-04-15 14:53:51 +03:00
c50a259541 fix: некорректная загрузка box-модуля 2026-04-14 17:31:40 +03:00
6cf76641ed refactor: небольшие изменения кода 2026-04-14 11:43:10 +03:00
5a021b7e8f feat: проверка на - в id форм 2026-04-13 20:24:53 +03:00
3f396dedb4 feat: проверка для избежания использования зарезрвированных имен таблиц 2026-04-13 20:19:15 +03:00
c72cf5b815 feat: мета-данные к выгрузке данных 2026-04-13 20:18:47 +03:00
0212726b06 feat: более явный перенос данных (даты, числа, все остальное - текст) 2026-04-13 19:58:17 +03:00
9 changed files with 85 additions and 21 deletions

View File

@@ -1 +1,21 @@
source("renv/activate.R") source("renv/activate.R")
(function() {
paths <- c(
"FORM_AUTH_ENABLED",
"FORM_VERSION",
"FORM_TITLE"
)
lines <- paths[Sys.getenv(paths) == ""]
if (length(lines) > 0) {
cli::cli_h3("Настройка путей окружения")
cli::cli_code(paste0(lines, "="))
cli::cli_inform(c(
"i" = "Для нормальной работы скриптов добавьте в {.file .Renviron}:"
))
}
})()

3
.gitignore vendored
View File

@@ -2,7 +2,8 @@
/temp /temp
scheme.rds scheme.rds
configs/schemas/d2tra_t.xlsx
configs/schemas/antifib.xlsx
.Renviron .Renviron
.DS_Store .DS_Store
.lintr .lintr

41
app.R
View File

@@ -118,9 +118,9 @@ server <- function(input, output, session) {
if (AUTH_ENABLED) { if (AUTH_ENABLED) {
reactiveValuesToList(res_auth) reactiveValuesToList(res_auth)
if (res_auth$admin) { if (res_auth$admin) {
print("admin") # print("admin")
} else { } else {
print("not_admin") # print("not_admin")
showing_buttons <- FALSE showing_buttons <- FALSE
} }
} }
@@ -342,8 +342,13 @@ server <- function(input, output, session) {
this_nested_form_scheme <- mhcs()$get_scheme(values$nested_form_id) this_nested_form_scheme <- mhcs()$get_scheme(values$nested_form_id)
# мини-схема для ключа # мини-схема для ключа
this_nested_form_key_scheme <- subset(this_nested_form_scheme, form_id == key_id) this_nested_form_key_scheme <- this_nested_form_scheme |>
if (nrow(this_nested_form_key_scheme) > 1) cli::cli_abort("количество строк не может быть больше одного для ключа") dplyr::filter(form_id == {{key_id}})
this_nested_form_key_scheme_smoll <- this_nested_form_key_scheme |>
dplyr::distinct(form_id, form_label, form_type)
if (nrow(this_nested_form_key_scheme_smoll) > 1) cli::cli_abort("количество строк не может быть больше одного для ключа")
# выбираем все ключи из баз данных # выбираем все ключи из баз данных
kyes_for_this_table <- db$get_nested_keys_from_table(values$nested_form_id, mhcs(), values$main_key, con) kyes_for_this_table <- db$get_nested_keys_from_table(values$nested_form_id, mhcs(), values$main_key, con)
@@ -352,7 +357,7 @@ server <- function(input, output, session) {
values$nested_key <- if (length(kyes_for_this_table) == 0) NULL else kyes_for_this_table[[1]] values$nested_key <- if (length(kyes_for_this_table) == 0) NULL else kyes_for_this_table[[1]]
# если ключ в формате даты - дать человекочитаемые данные # если ключ в формате даты - дать человекочитаемые данные
if (this_nested_form_key_scheme$form_type == "date") { if (this_nested_form_key_scheme_smoll$form_type == "date") {
kyes_for_this_table <- setNames( kyes_for_this_table <- setNames(
kyes_for_this_table, kyes_for_this_table,
format(as.Date(kyes_for_this_table), "%d.%m.%Y") format(as.Date(kyes_for_this_table), "%d.%m.%Y")
@@ -390,7 +395,7 @@ server <- function(input, output, session) {
width = 300, width = 300,
selectizeInput( selectizeInput(
inputId = "nested_key_selector", inputId = "nested_key_selector",
label = strong(this_nested_form_key_scheme$form_label), label = strong(this_nested_form_key_scheme_smoll$form_label),
choices = kyes_for_this_table, choices = kyes_for_this_table,
selected = values$nested_key, selected = values$nested_key,
# options = list(placeholder = "действие комиссии", create = FALSE, onInitialize = I('function() { this.setValue(""); }')) # options = list(placeholder = "действие комиссии", create = FALSE, onInitialize = I('function() { this.setValue(""); }'))
@@ -589,6 +594,8 @@ server <- function(input, output, session) {
mhcs(), mhcs(),
ns = NS(values$nested_form_id) ns = NS(values$nested_form_id)
) )
} else {
utils$clean_forms(values$nested_form_id, mhcs(), NS(values$nested_form_id))
} }
}) })
@@ -868,6 +875,7 @@ server <- function(input, output, session) {
date_columns <- subset(scheme, form_type == "date", form_id, drop = TRUE) date_columns <- subset(scheme, form_type == "date", form_id, drop = TRUE)
number_columns <- subset(scheme, form_type == "number", form_id, drop = TRUE) number_columns <- subset(scheme, form_type == "number", form_id, drop = TRUE)
other_cols <- which(colnames(df) %in% c(date_columns, number_columns))
df <- df |> df <- df |>
dplyr::mutate( dplyr::mutate(
@@ -875,14 +883,24 @@ server <- function(input, output, session) {
dplyr::across(tidyselect::all_of({{date_columns}}), as.Date), dplyr::across(tidyselect::all_of({{date_columns}}), as.Date),
# числа - к единому формату десятичных значений # числа - к единому формату десятичных значений
dplyr::across(tidyselect::all_of({{number_columns}}), ~ gsub("\\.", "," , .x)), dplyr::across(tidyselect::all_of({{number_columns}}), ~ gsub("\\.", "," , .x)),
dplyr::across(tidyselect::all_of({{other_cols}}), as.character)
) |> ) |>
# очистка от пустых ключей # очистка от пустых ключей
dplyr::filter(!is.na(main_key)) dplyr::filter(!is.na(mhcs()$get_main_key_id))
df df
} }
) )
# добавить мета информацию
list_of_df[["meta"]] <- dplyr::tribble(
~`Параметр` , ~`Значение`,
"Пользователь" , ifelse(AUTH_ENABLED, res_auth$user, "anonymous"),
"Название базы" , names(enabled_schemas)[enabled_schemas == scheme()],
"id базы" , scheme(),
"Время выгрузки" , format(Sys.time(), "%d.%m.%Y %H:%M:%S"),
)
# set date params # set date params
options("openxlsx2.dateFormat" = "dd.mm.yyyy") options("openxlsx2.dateFormat" = "dd.mm.yyyy")
@@ -1044,6 +1062,7 @@ server <- function(input, output, session) {
# столбцы в таблицы и схема # столбцы в таблицы и схема
df_to_schema_compare <- setdiff(colnames(df), unique(scheme$form_id)) df_to_schema_compare <- setdiff(colnames(df), unique(scheme$form_id))
df_to_schema_compare <- df_to_schema_compare[df_to_schema_compare != main_key_id]
schema_to_df_compare <- setdiff(unique(scheme$form_id), colnames(df)) schema_to_df_compare <- setdiff(unique(scheme$form_id), colnames(df))
if (length(df_to_schema_compare) > 0 ) { if (length(df_to_schema_compare) > 0 ) {
@@ -1054,8 +1073,8 @@ server <- function(input, output, session) {
if (length(schema_to_df_compare) > 0 ) { if (length(schema_to_df_compare) > 0 ) {
message <- glue::glue("столбцы в таблице '{table_name}' не соответсвуют схеме") message <- glue::glue("столбцы в таблице '{table_name}' не соответсвуют схеме")
cli::cli_warn(c(message, paste("- ", schema_to_df_compare)))
showNotification(message, type = "error") showNotification(message, type = "error")
cli::cli_warn(c(message, paste("- ", schema_to_df_compare)))
return() return()
} }
@@ -1069,6 +1088,7 @@ server <- function(input, output, session) {
date_columns <- subset(scheme, form_type == "date", form_id, drop = TRUE) date_columns <- subset(scheme, form_type == "date", form_id, drop = TRUE)
number_columns <- subset(scheme, form_type == "number", form_id, drop = TRUE) number_columns <- subset(scheme, form_type == "number", form_id, drop = TRUE)
other_cols <- which(colnames(df) %in% c(date_columns, number_columns))
# функция для преобразование числовых значений и сохранения "NA" # функция для преобразование числовых значений и сохранения "NA"
num_converter <- function(old_col) { num_converter <- function(old_col) {
@@ -1088,6 +1108,7 @@ server <- function(input, output, session) {
# даты - к единому формату # даты - к единому формату
dplyr::across(tidyselect::all_of({{date_columns}}), \(x) purrr::map_chr(x, db$excel_to_db_dates_converter)), dplyr::across(tidyselect::all_of({{date_columns}}), \(x) purrr::map_chr(x, db$excel_to_db_dates_converter)),
dplyr::across(tidyselect::all_of({{number_columns}}), num_converter), dplyr::across(tidyselect::all_of({{number_columns}}), num_converter),
dplyr::across(tidyselect::all_of({{other_cols}}), as.character)
) |> ) |>
select(all_of(unique(c(main_key_id, scheme$form_id)))) select(all_of(unique(c(main_key_id, scheme$form_id))))
@@ -1115,7 +1136,7 @@ server <- function(input, output, session) {
# очистка от пустых ключей # очистка от пустых ключей
df <- df |> df <- df |>
dplyr::filter(!is.na(main_key)) dplyr::filter(!is.na({{main_key_id}}))
DBI::dbWriteTable( DBI::dbWriteTable(
con, con,
@@ -1124,7 +1145,7 @@ server <- function(input, output, session) {
append = TRUE append = TRUE
) )
message <- glue::glue("Данные таблицы '{table_name}' успешно обновлены") message <- glue::glue("Данные таблицы '{table_name}' успешно обновлены (добавлено {nrow(df)} записей)")
showNotification( showNotification(
message, message,
type = "message" type = "message"

View File

@@ -191,6 +191,7 @@ write_df_to_db = function(
date_columns <- subset(scheme, form_type == "date", form_id, drop = TRUE) date_columns <- subset(scheme, form_type == "date", form_id, drop = TRUE)
number_columns <- subset(scheme, form_type == "number", form_id, drop = TRUE) number_columns <- subset(scheme, form_type == "number", form_id, drop = TRUE)
other_cols <- which(colnames(df) %in% c(date_columns, number_columns))
df <- df |> df <- df |>
dplyr::mutate( dplyr::mutate(
@@ -198,8 +199,12 @@ write_df_to_db = function(
dplyr::across(tidyselect::all_of({{date_columns}}), \(x) purrr::map_chr(x, excel_to_db_dates_converter)), dplyr::across(tidyselect::all_of({{date_columns}}), \(x) purrr::map_chr(x, excel_to_db_dates_converter)),
# числа - к единому формату десятичных значений # числа - к единому формату десятичных значений
dplyr::across(tidyselect::all_of({{number_columns}}), ~ gsub("\\.", "," , .x)), dplyr::across(tidyselect::all_of({{number_columns}}), ~ gsub("\\.", "," , .x)),
dplyr::across(tidyselect::all_of({{other_cols}}), as.character),
) )
df |>
dplyr::glimpse()
if (table_name == "main") { if (table_name == "main") {
del_query <- glue::glue("DELETE FROM main WHERE {main_key_id} = '{main_key_value}'") del_query <- glue::glue("DELETE FROM main WHERE {main_key_id} = '{main_key_value}'")
} }

View File

@@ -5,7 +5,7 @@ set_global_options = function(
APP.DEBUG = FALSE, APP.DEBUG = FALSE,
# APP.FILE_DB = fs::path("data.sqlite"), # APP.FILE_DB = fs::path("data.sqlite"),
shiny.host = "127.0.0.1", shiny.host = "127.0.0.1",
shiny.port = 1337, shiny.port = 1338,
... ...
) { ) {
options( options(

View File

@@ -18,6 +18,14 @@ scheme_R6 <- R6::R6Class(
dplyr::distinct(form_id) |> dplyr::distinct(form_id) |>
dplyr::pull(form_id) dplyr::pull(form_id)
# проверка на не пересечение с зарезервированными именами
check <- private$reserved_table_names %in% private$nested_forms_names
if (any(check)) cli::cli_abort(c("нельзя использовать данные имена вложенных таблиц:", paste("- ", private$reserved_table_names[check])))
# проверка на длину строк
check <- (nchar(private$nested_forms_names) > 31)
if (any(check)) cli::cli_abort(c("нельзя использовать имена длиной более 31 символа:", paste("- ", private$nested_forms_names[check])))
purrr::walk( purrr::walk(
.x = purrr::set_names(private$nested_forms_names), .x = purrr::set_names(private$nested_forms_names),
.f = \(nested_form_id) { .f = \(nested_form_id) {
@@ -109,6 +117,7 @@ scheme_R6 <- R6::R6Class(
nested_forms_names = NA, nested_forms_names = NA,
bslib_rendered_ui = NA, bslib_rendered_ui = NA,
excluded_types = c("nested_forms", "description", "description_header"), excluded_types = c("nested_forms", "description", "description_header"),
reserved_table_names = c("meta", "log", "main"),
load_scheme_from_xlsx = function(sheet_name) { load_scheme_from_xlsx = function(sheet_name) {
@@ -132,8 +141,15 @@ scheme_R6 <- R6::R6Class(
dplyr::pull(form_id) dplyr::pull(form_id)
if (length(duplicate_ids) > 0) { if (length(duplicate_ids) > 0) {
cli::cli_abort(c("В схеме для формы '{sheet_name}' содержатся повторяющиеся id:", paste("-", duplicate_ids))) cli::cli_abort(c("В схеме '{private$scheme_file_path}' для формы '{sheet_name}' содержатся повторяющиеся id:", paste("-", duplicate_ids)))
} }
# проверка на корректные id
input_names_with_dash <- unique(table$form_id)[grepl("-", unique(table$form_id))]
if (length(input_names_with_dash) > 0) {
cli::cli_abort(c("В схеме '{private$scheme_file_path}' в id форм содержатся `-`, может привести к некорректной последующей работой с базой данных", paste("-", input_names_with_dash)))
}
table table
} }
) )

View File

@@ -162,7 +162,7 @@ render_forms = function(
choices = choices, choices = choices,
selected = NULL, selected = NULL,
options = list( options = list(
create = FALSE, create = TRUE,
onInitialize = I('function() { this.setValue(""); }') onInitialize = I('function() { this.setValue(""); }')
) )
) )
@@ -177,7 +177,7 @@ render_forms = function(
selected = NULL, selected = NULL,
multiple = TRUE, multiple = TRUE,
options = list( options = list(
create = FALSE, create = TRUE,
onInitialize = I('function() { this.setValue(""); }') onInitialize = I('function() { this.setValue(""); }')
) )
) )
@@ -264,6 +264,7 @@ update_forms_with_data = function(
ns ns
) { ) {
options(box.path = here::here())
box::use(modules/data_manipulations[is_this_empty_value]) box::use(modules/data_manipulations[is_this_empty_value])
# print("-----------------") # print("-----------------")