Commit 21174836 authored by gaozhaochen's avatar gaozhaochen

update: 自动加载xlsx的全部sheet数据

parent ae842bf1
...@@ -8,38 +8,36 @@ observe({ ...@@ -8,38 +8,36 @@ observe({
dataset_id <- query[['datasetId']] dataset_id <- query[['datasetId']]
token <- query[['token']] token <- query[['token']]
# 初始化一个 session 级别的变量来记录已加载的 ID,防止刷新时的重复请求
if (is.null(session$userData$loaded_datasets)) {
session$userData$loaded_datasets <- c()
}
# 2. 仅当 ID 和 Token 均存在时执行 # 2. 仅当 ID 和 Token 均存在时执行
if (!is.null(dataset_id) && !is.null(token)) { if (!is.null(dataset_id) && !is.null(token)) {
# 定义 Radiant 内部使用的数据集名称 (例如: data_10086) # 3. [修改点] 检查 ID 是否已在本次会话中加载过
# 如果 URL 传了 name 参数就用 name,否则用 id 拼接 # 因为现在数据集名称是动态的 Sheet 名,下载前无法预知,所以改为判断 ID
ds_name <- if (!is.null(query[['name']])) query[['name']] else paste0("data_", dataset_id) if (!(dataset_id %in% session$userData$loaded_datasets)) {
# 3. 检查数据是否已存在 (防止重复加载)
if (is.null(r_data[[ds_name]])) {
withProgress(message = '正在从业务系统同步数据...', value = 0.2, { withProgress(message = '正在从业务系统同步数据...', value = 0.2, {
# 4. 获取环境变量中的 API 基地址 # 4. 获取环境变量
# api_base <- Sys.getenv("HOST_API_BASE","http://127.0.0.1:11999") api_base <- Sys.getenv("HOST_API_BASE", "https://ds.cixincloud.com/data-search-api")
api_base <- Sys.getenv("HOST_API_BASE","https://ds.cixincloud.com/data-search-api")
# 5. 拼接完整 API 路径 # 5. 拼接完整 API 路径
# target_url <- paste0(api_base, "/disease-data/data/export/apply/apply/case?applyId=", dataset_id)
target_url <- paste0(api_base, "/research-project/generate-project-dataset/", dataset_id) target_url <- paste0(api_base, "/research-project/generate-project-dataset/", dataset_id)
# 6. 创建临时文件 (明确 .xlsx 后缀) # 6. 创建临时文件
tmp_file <- tempfile(fileext = ".xlsx") tmp_file <- tempfile(fileext = ".xlsx")
tryCatch({ tryCatch({
incProgress(0.3, detail = "正在鉴权并下载...") incProgress(0.3, detail = "正在鉴权并下载...")
# 7. 发起带 Token 的 HTTP 请求 # 7. HTTP 请求
response <- httr::POST( response <- httr::POST(
url = target_url, url = target_url,
# 添加 Bearer Token (或根据你的接口要求修改 Header)
httr::add_headers(authentication = paste(token)), httr::add_headers(authentication = paste(token)),
# 将结果写入磁盘
httr::write_disk(tmp_file, overwrite = TRUE) httr::write_disk(tmp_file, overwrite = TRUE)
) )
...@@ -47,34 +45,47 @@ observe({ ...@@ -47,34 +45,47 @@ observe({
print(response$request) print(response$request)
message("===================") message("===================")
# 检查 HTTP 状态码
if (httr::status_code(response) != 200) { if (httr::status_code(response) != 200) {
stop(paste("下载失败,请手动导入,HTTP状态码:", httr::status_code(response))) stop(paste("下载失败,HTTP状态码:", httr::status_code(response)))
} }
incProgress(0.7, detail = "解析并导入 Radiant...") incProgress(0.7, detail = "解析并导入 Radiant...")
# 8. 复用 Radiant 核心加载函数 (manage_ui.R 中定义) # 8. 调用核心加载函数
# 这会自动完成读取、转因子、生成R代码、注册到下拉框等所有动作 # 注意:这里的 fname 仅用于扩展名识别,实际数据集名称由 Sheet 名决定
load_user_data( load_user_data(
fname = paste0(ds_name, ".xlsx"), # 虚拟文件名 fname = "downloaded_data.xlsx",
uFile = tmp_file, # 实际文件路径 uFile = tmp_file,
ext = "xlsx", ext = "xlsx",
xlsx_sheet = 1,
xlsx_header = TRUE, xlsx_header = TRUE,
man_str_as_factor = TRUE man_str_as_factor = TRUE
) )
# 9. 界面联动:选中数据并跳转到视图 # 9. 界面联动:获取刚才加载的 Sheet 名称并选中第一个
updateSelectInput(session, "dataset", selected = ds_name) # 必须再次读取 Sheet 列表并做 make.names 处理,才能匹配到 r_data 中的 Key
loaded_sheets <- try(readxl::excel_sheets(tmp_file), silent = TRUE)
if (!inherits(loaded_sheets, "try-error") && length(loaded_sheets) > 0) {
# 必须和 load_user_data 里的逻辑一致,使用 make.names 清洗
valid_names <- make.names(loaded_sheets)
first_sheet_name <- valid_names[1] # 默认选第一个
# 标记为已加载
session$userData$loaded_datasets <- c(session$userData$loaded_datasets, dataset_id)
# 更新下拉框并选中第一个 Sheet
updateSelectInput(session, "dataset", selected = first_sheet_name)
updateTabsetPanel(session, "nav_radiant", selected = "Data") updateTabsetPanel(session, "nav_radiant", selected = "Data")
updateTabsetPanel(session, "tabs_data", selected = "View") # 或者 "Visualize" updateTabsetPanel(session, "tabs_data", selected = "View")
showNotification(paste("数据集", ds_name, "加载成功!"), type = "message") showNotification(paste0("成功加载 ", length(valid_names), " 个数据集 (ID: ", dataset_id, ")"), type = "message")
} else {
showNotification("文件下载成功,但未发现有效的 Sheet", type = "warning")
}
}, error = function(e) { }, error = function(e) {
showNotification(paste("数据同步失败,请手动导入数据:", e$message), type = "error", duration = 10) showNotification(paste("数据同步失败:", e$message), type = "error", duration = 10)
#调试打印 print(e)
}) })
}) })
} }
......
...@@ -142,35 +142,77 @@ load_user_data <- function(fname, uFile, ext, header = TRUE, ...@@ -142,35 +142,77 @@ load_user_data <- function(fname, uFile, ext, header = TRUE,
} }
}else if (ext == "xlsx") { }else if (ext == "xlsx") {
if (!requireNamespace("readxl", quietly = TRUE)) { if (!requireNamespace("readxl", quietly = TRUE)) {
ret <- i18n$t("#### 读取xlsx文件需要readxl包") upload_error_handler(objname, i18n$t("#### 需要安装 readxl 包"))
upload_error_handler(objname, ret) return()
} else { }
# 用readxl读取xlsx
robj <- try(readxl::read_excel( # 1. 获取所有 Sheet
all_sheets <- try(readxl::excel_sheets(path = uFile), silent = TRUE)
if (inherits(all_sheets, "try-error")) {
upload_error_handler(objname, i18n$t("#### 无法解析 Excel 文件结构"))
return()
}
loaded_count <- 0
# 2. 循环处理
for (sheet_name in all_sheets) {
# 2.1 准备合法的变量名
clean_name <- make.names(sheet_name)
if (clean_name == "") clean_name <- paste0("Sheet_", sample(1000, 1))
# 2.2 读取数据
raw_data <- try(readxl::read_excel(
path = uFile, path = uFile,
sheet = xlsx_sheet, # 对应UI的“工作表索引” sheet = sheet_name,
col_names = xlsx_header# 对应UI的“第一行为表头” col_names = xlsx_header
), silent = TRUE) ), silent = TRUE)
if (inherits(robj, "try-error")) { # 2.3 卫语句:跳过无效数据(空表或读取错误)
upload_error_handler(objname, i18n$t("#### 读取xlsx文件失败,请检查文件是否损坏或格式正确")) if (inherits(raw_data, "try-error") || !is.data.frame(raw_data) || ncol(raw_data) == 0) {
} else { next
# 转换为data.frame并处理因子 }
r_data[[objname]] <- as.data.frame(robj, stringsAsFactors = FALSE) %>%
{if (man_str_as_factor) radiant.data::to_fct(.) else .} # 2.4 [关键] 立即执行 Radiant 标准数据清洗流程
# 生成R代码 # 必须在循环内做,因为我们不会走到函数底部的通用处理逻辑了
cmd <- glue(' try({
{objname} <- readxl::read_excel( # 清洗列名
{pp$rpath}, colnames(raw_data) <- radiant.data::fix_names(colnames(raw_data))
sheet = {xlsx_sheet}, # 转换因子
col_names = {xlsx_header} if (man_str_as_factor) raw_data <- radiant.data::to_fct(raw_data)
) %>% }, silent = TRUE)
as.data.frame(stringsAsFactors = FALSE)
{if (man_str_as_factor) paste0(objname, " <- radiant.data::to_fct(", objname, ")") else ""} # 2.5 入库
register("{objname}") r_data[[clean_name]] <- as.data.frame(raw_data, stringsAsFactors = FALSE)
# 2.6 生成并注册复现代码 (Reproducibility)
# 注意:这里直接注册 register,不需要 cmd 变量在外部累加
curr_cmd <- glue::glue('
{clean_name} <- readxl::read_excel("{uFile}", sheet = "{sheet_name}", col_names = {xlsx_header}) %>%
as.data.frame(stringsAsFactors = FALSE) %>%
fix_names()
{if (man_str_as_factor) paste0(clean_name, " <- radiant.data::to_fct(", clean_name, ")") else ""}
register("{clean_name}")
') ')
# 2.7 更新系统元数据 (Mimic bottom logic)
if (exists(clean_name, envir = r_data) && !bindingIsActive(as.symbol(clean_name), env = r_data)) {
shiny::makeReactiveBinding(clean_name, env = r_data)
}
r_info[[glue("{clean_name}_lcmd")]] <- curr_cmd
r_info[["datasetlist"]] <- unique(c(clean_name, r_info[["datasetlist"]]))
loaded_count <- loaded_count + 1
} }
if (loaded_count == 0) {
upload_error_handler(objname, i18n$t("#### 文件中未发现有效的表格数据"))
} }
# 直接返回
return()
}else if (ext %in% c("tsv", "csv", "txt")) { }else if (ext %in% c("tsv", "csv", "txt")) {
r_data[[objname]] <- load_csv( r_data[[objname]] <- load_csv(
uFile, uFile,
...@@ -197,6 +239,11 @@ load_user_data <- function(fname, uFile, ext, header = TRUE, ...@@ -197,6 +239,11 @@ load_user_data <- function(fname, uFile, ext, header = TRUE,
upload_error_handler(objname, ret) upload_error_handler(objname, ret)
} }
# ===========================================================================
# 下面的代码只为单数据集流程(CSV, RDS, parquet)服务
# XLSX 流程已经在上面 return() 了,不会执行到这里
# ===========================================================================
if (exists(objname, envir = r_data) && !bindingIsActive(as.symbol(objname), env = r_data)) { if (exists(objname, envir = r_data) && !bindingIsActive(as.symbol(objname), env = r_data)) {
shiny::makeReactiveBinding(objname, env = r_data) shiny::makeReactiveBinding(objname, env = r_data)
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment