feat: функционал импорта базы данных из xlsx таблицы
This commit is contained in:
114
app.R
114
app.R
@@ -166,6 +166,7 @@ ui <- page_sidebar(
|
|||||||
actionButton("load_data_button", "Загрузить данные", icon("pencil", lib = "font-awesome")),
|
actionButton("load_data_button", "Загрузить данные", icon("pencil", lib = "font-awesome")),
|
||||||
downloadButton("downloadData", "Экспорт в .xlsx"),
|
downloadButton("downloadData", "Экспорт в .xlsx"),
|
||||||
downloadButton("downloadDocx", "get .docx (test only)"),
|
downloadButton("downloadDocx", "get .docx (test only)"),
|
||||||
|
actionButton("button_upload_data_from_xlsx", "импорт!"),
|
||||||
position = "left",
|
position = "left",
|
||||||
open = list(mobile = "always")
|
open = list(mobile = "always")
|
||||||
),
|
),
|
||||||
@@ -797,6 +798,7 @@ server <- function(input, output, session) {
|
|||||||
|
|
||||||
## загрузка данных по главному ключу ------------------
|
## загрузка данных по главному ключу ------------------
|
||||||
observeEvent(input$load_data, {
|
observeEvent(input$load_data, {
|
||||||
|
req(input$load_data_key_selector)
|
||||||
|
|
||||||
con <- db$make_db_connection("load_data")
|
con <- db$make_db_connection("load_data")
|
||||||
on.exit(db$close_db_connection(con, "load_data"), add = TRUE)
|
on.exit(db$close_db_connection(con, "load_data"), add = TRUE)
|
||||||
@@ -963,6 +965,118 @@ server <- function(input, output, session) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
## upload xlsx to df ----------------------
|
||||||
|
observeEvent(input$button_upload_data_from_xlsx, {
|
||||||
|
|
||||||
|
showModal(modalDialog(
|
||||||
|
title = "Добавить пациентов к текущему списку...",
|
||||||
|
fileInput(
|
||||||
|
"upload_xlsx",
|
||||||
|
NULL,
|
||||||
|
buttonLabel = "Выбрать файлы...",
|
||||||
|
placeholder = "No file selected",
|
||||||
|
multiple = TRUE,
|
||||||
|
accept = ".xlsx",
|
||||||
|
width = 450
|
||||||
|
),
|
||||||
|
checkboxInput("upload_data_from_xlsx_owerwrite_all_data", "перезаписать все данные", width = 450),
|
||||||
|
footer = tagList(
|
||||||
|
actionButton("button_upload_data_from_xlsx_confirm", "Добавить")
|
||||||
|
),
|
||||||
|
easyClose = TRUE
|
||||||
|
))
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
observeEvent(input$button_upload_data_from_xlsx_confirm, {
|
||||||
|
req(input$upload_xlsx
|
||||||
|
)
|
||||||
|
con <- db$make_db_connection("button_upload_data_from_xlsx_confirm")
|
||||||
|
on.exit(db$close_db_connection(con, "button_upload_data_from_xlsx_confirm"), add = TRUE)
|
||||||
|
|
||||||
|
file <- input$upload_xlsx$datapath
|
||||||
|
# print(file)
|
||||||
|
wb <- openxlsx2::wb_load(file)
|
||||||
|
|
||||||
|
# проверка на наличие всех листов в файле
|
||||||
|
if (!all(names(SCHEMES_LIST) %in% openxlsx2::wb_get_sheet_names(wb))) {
|
||||||
|
cli::cli_alert_warning("данные в файле '{file} не соответствуют схеме'")
|
||||||
|
return()
|
||||||
|
}
|
||||||
|
|
||||||
|
# проверка схемы --------------
|
||||||
|
for (table_name in names(SCHEMES_LIST)) {
|
||||||
|
|
||||||
|
df <- openxlsx2::read_xlsx(wb, table_name)
|
||||||
|
scheme <- SCHEMES_LIST[[table_name]] |>
|
||||||
|
filter(!form_type %in% c("description", "nested_forms"))
|
||||||
|
|
||||||
|
# столбцы в таблицы и схема
|
||||||
|
df_to_schema_compare <- setdiff(colnames(df), unique(scheme$form_id))
|
||||||
|
if (length(schema_to_df_compare) > 0 ) {
|
||||||
|
cli::cli_warn(c("в схеме для '{table_name}' нет следующих столбцов:", paste("- ", df_to_schema_compare)))
|
||||||
|
}
|
||||||
|
|
||||||
|
# схема и столбцы в таблице
|
||||||
|
schema_to_df_compare <- setdiff(unique(scheme$form_id), colnames(df))
|
||||||
|
if (length(schema_to_df_compare) > 0 ) {
|
||||||
|
|
||||||
|
message <- glue::glue("столбцы в таблице '{table_name}' не соответсвуют схеме")
|
||||||
|
cli::cli_warn(c(message, paste("- ", schema_to_df_compare)))
|
||||||
|
showNotification(message, type = "error")
|
||||||
|
|
||||||
|
return()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# обновление данных
|
||||||
|
for (table_name in names(SCHEMES_LIST)) {
|
||||||
|
|
||||||
|
df <- openxlsx2::read_xlsx(wb, table_name)
|
||||||
|
scheme <- SCHEMES_LIST[[table_name]] |>
|
||||||
|
filter(!form_type %in% c("description", "nested_forms"))
|
||||||
|
|
||||||
|
date_columns <- subset(scheme, form_type == "date", form_id, drop = TRUE)
|
||||||
|
|
||||||
|
df <- df |>
|
||||||
|
dplyr::mutate(
|
||||||
|
# даты - к единому формату
|
||||||
|
dplyr::across(tidyselect::all_of({{date_columns}}), \(x) purrr::map_chr(x, db$excel_to_db_dates_converter)),
|
||||||
|
) |>
|
||||||
|
select(all_of(unique(c("main_key", scheme$form_id))))
|
||||||
|
|
||||||
|
df_original <- DBI::dbReadTable(con, table_name) |>
|
||||||
|
as_tibble()
|
||||||
|
|
||||||
|
if (input$upload_data_from_xlsx_owerwrite_all_data == TRUE) cli::cli_abort("not implemented yet")
|
||||||
|
|
||||||
|
walk(
|
||||||
|
.x = unique(df$main_key),
|
||||||
|
.f = \(main_key) {
|
||||||
|
|
||||||
|
if (main_key %in% unique(df_original$main_key)) {
|
||||||
|
DBI::dbExecute(con, glue::glue("DELETE FROM {table_name} WHERE main_key = '{main_key}'"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
DBI::dbWriteTable(
|
||||||
|
con,
|
||||||
|
name = table_name,
|
||||||
|
value = df,
|
||||||
|
append = TRUE
|
||||||
|
)
|
||||||
|
|
||||||
|
message <- glue::glue("Данные таблицы '{table_name}' успешно обновлены")
|
||||||
|
showNotification(
|
||||||
|
message,
|
||||||
|
type = "message"
|
||||||
|
)
|
||||||
|
cli::cli_alert_success(message)
|
||||||
|
}
|
||||||
|
removeModal()
|
||||||
|
})
|
||||||
|
|
||||||
## cancel ==========================
|
## cancel ==========================
|
||||||
observeEvent(input$cancel_button, {
|
observeEvent(input$cancel_button, {
|
||||||
removeModal()
|
removeModal()
|
||||||
|
|||||||
55
modules/db.R
55
modules/db.R
@@ -165,25 +165,6 @@ write_df_to_db <- function(df, table_name, scheme, main_key, nested_key, con) {
|
|||||||
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)
|
||||||
|
|
||||||
excel_to_db_dates_converter <- function(date) {
|
|
||||||
|
|
||||||
parse_date1 <- tryCatch(
|
|
||||||
as.Date(date, tryFormats = c("%Y-%m-%d")),
|
|
||||||
error = function(e) NULL
|
|
||||||
)
|
|
||||||
parse_date2 <- suppressWarnings(as.Date(as.numeric(date), origin = "1899-12-30"))
|
|
||||||
|
|
||||||
date <- if (!is.null(parse_date1)) {
|
|
||||||
parse_date1
|
|
||||||
} else if (!is.na(parse_date2)) {
|
|
||||||
parse_date2
|
|
||||||
} else {
|
|
||||||
date
|
|
||||||
}
|
|
||||||
|
|
||||||
date <- as.character(format(date, "%Y-%m-%d"))
|
|
||||||
}
|
|
||||||
|
|
||||||
df <- df |>
|
df <- df |>
|
||||||
dplyr::mutate(
|
dplyr::mutate(
|
||||||
# даты - к единому формату
|
# даты - к единому формату
|
||||||
@@ -260,4 +241,38 @@ get_nested_keys_from_table <- function(table_name, main_key, con) {
|
|||||||
DBI::dbGetQuery(con, glue::glue("SELECT DISTINCT nested_key FROM {table_name} WHERE main_key == '{main_key}'")) |>
|
DBI::dbGetQuery(con, glue::glue("SELECT DISTINCT nested_key FROM {table_name} WHERE main_key == '{main_key}'")) |>
|
||||||
dplyr::pull()
|
dplyr::pull()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
### HELPERS ---------
|
||||||
|
#' @export
|
||||||
|
excel_to_db_dates_converter <- function(date) {
|
||||||
|
|
||||||
|
if(is.na(date)) return(NA)
|
||||||
|
# cli::cli_inform("date: {date} | nchar: {nchar(date)} | typeof: {typeof(date)}")
|
||||||
|
|
||||||
|
# если текст, количество символов 7, и маска соответствует 'MM.YYYY'
|
||||||
|
if (typeof(date) == "character" & nchar(date) == 4 & grepl("((?:19|20)\\d\\d)", date)) {
|
||||||
|
date <- sprintf("%s-01-01", date)
|
||||||
|
} else if (typeof(date) == "character" & nchar(date) == 7 & grepl("(0?[1-9]|1[012])\\.((?:19|20)\\d\\d)", date)) {
|
||||||
|
# если текст, количество символов 7, и маска соответствует 'MM.YYYY'
|
||||||
|
date <- sprintf("01.%s", date)
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_date1 <- tryCatch(
|
||||||
|
as.Date(date, tryFormats = c("%d.%m.%Y", "%Y-%m-%d")),
|
||||||
|
error = function(e) NULL
|
||||||
|
)
|
||||||
|
parse_date2 <- suppressWarnings(as.Date(as.numeric(date), origin = "1899-12-30"))
|
||||||
|
|
||||||
|
date <- if (!is.null(parse_date1)) {
|
||||||
|
parse_date1
|
||||||
|
} else if (!is.na(parse_date2)) {
|
||||||
|
parse_date2
|
||||||
|
} else {
|
||||||
|
date
|
||||||
|
}
|
||||||
|
|
||||||
|
date <- as.character(format(date, "%Y-%m-%d"))
|
||||||
|
date
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user