From b4d091cd4cae9f66702cf60d476caf9795b5198d Mon Sep 17 00:00:00 2001 From: AudreiPavanello Date: Wed, 30 Oct 2024 23:07:14 -0300 Subject: [PATCH] Add new features Add modular structures Add new tabs Add dictionairies --- R/constants.R | 24 ++++++ R/dictionaries.R | 65 +++++++++++++++ R/server_modules.R | 196 +++++++++++++++++++++++++++++++++++++++++++++ R/ui_modules.R | 71 ++++++++++++++++ app.R | 192 ++++++++++++++------------------------------ global.R | 12 +++ 6 files changed, 428 insertions(+), 132 deletions(-) create mode 100644 R/constants.R create mode 100644 R/dictionaries.R create mode 100644 R/server_modules.R create mode 100644 R/ui_modules.R create mode 100644 global.R diff --git a/R/constants.R b/R/constants.R new file mode 100644 index 0000000..ef5b6ce --- /dev/null +++ b/R/constants.R @@ -0,0 +1,24 @@ +# System and location definitions +info_systems <- c( + "Sistema de Informações sobre Mortalidade (SIM-DO)" = "SIM-DO", + "Sistema de Informações sobre Nascidos Vivos (SINASC)" = "SINASC", + "Sistema de Informações Hospitalares (SIH-RD)" = "SIH-RD", + "Sistema de Informações Ambulatoriais (SIA-PA)" = "SIA-PA" +) + +estados <- c( + "Acre" = "AC", "Alagoas" = "AL", "Amapá" = "AP", "Amazonas" = "AM", + "Bahia" = "BA", "Ceará" = "CE", "Distrito Federal" = "DF", + "Espírito Santo" = "ES", "Goiás" = "GO", "Maranhão" = "MA", + "Mato Grosso" = "MT", "Mato Grosso do Sul" = "MS", "Minas Gerais" = "MG", + "Pará" = "PA", "Paraíba" = "PB", "Paraná" = "PR", "Pernambuco" = "PE", + "Piauí" = "PI", "Rio de Janeiro" = "RJ", "Rio Grande do Norte" = "RN", + "Rio Grande do Sul" = "RS", "Rondônia" = "RO", "Roraima" = "RR", + "Santa Catarina" = "SC", "São Paulo" = "SP", "Sergipe" = "SE", + "Tocantins" = "TO" +) + +meses <- setNames(1:12, c( + "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", + "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro" +)) \ No newline at end of file diff --git a/R/dictionaries.R b/R/dictionaries.R new file mode 100644 index 0000000..5dc5c24 --- /dev/null +++ b/R/dictionaries.R @@ -0,0 +1,65 @@ +# Data dictionaries +dicionario_sim <- data.frame( + Variavel = c("DTOBITO", "CAUSABAS", "IDADE", "SEXO", "RACACOR", "ESC", "LOCOCOR", "CODMUNRES", "CAUSABAS_O", "COMUNINF"), + Descricao = c( + "Data do óbito", + "Causa básica do óbito (CID-10)", + "Idade do falecido", + "Sexo do falecido", + "Raça/cor do falecido", + "Escolaridade", + "Local de ocorrência do óbito", + "Código do município de residência", + "Causa básica original", + "Código da unidade notificadora" + ) +) + +dicionario_sinasc <- data.frame( + Variavel = c("DTNASC", "SEXO", "PESO", "GESTACAO", "CONSULTAS", "RACACOR", "ESCMAE", "IDADEMAE", "CODMUNRES", "LOCNASC", "APGAR1"), + Descricao = c( + "Data de nascimento", + "Sexo do recém-nascido", + "Peso ao nascer (em gramas)", + "Duração da gestação em semanas", + "Número de consultas de pré-natal", + "Raça/cor do recém-nascido", + "Escolaridade da mãe", + "Idade da mãe", + "Código do município de residência", + "Local do nascimento", + "Índice de Apgar no 1º minuto" + ) +) + +dicionario_sih <- data.frame( + Variavel = c("DT_INTER", "PROC_REA", "DIAG_PRINC", "DIAS_PERM", "VAL_TOT", "CEP", "IDADE", "MUNIC_RES", "COMPLEX", "MORTE"), + Descricao = c( + "Data da internação", + "Procedimento realizado", + "Diagnóstico principal (CID-10)", + "Dias de permanência", + "Valor total da internação", + "CEP do paciente", + "Idade do paciente", + "Município de residência", + "Complexidade", + "Indicador de óbito" + ) +) + +dicionario_sia <- data.frame( + Variavel = c("PA_PROC", "PA_CIDPRI", "PA_SEXO", "PA_IDADE", "PA_RACACOR", "PA_QTDAPR", "PA_VALAPR", "PA_CODUNI", "PA_CBO", "PA_GESTAO"), + Descricao = c( + "Código do procedimento", + "CID principal", + "Sexo do paciente", + "Idade do paciente", + "Raça/cor do paciente", + "Quantidade aprovada", + "Valor aprovado", + "Código da unidade", + "Ocupação do profissional", + "Tipo de gestão" + ) +) \ No newline at end of file diff --git a/R/server_modules.R b/R/server_modules.R new file mode 100644 index 0000000..239aa04 --- /dev/null +++ b/R/server_modules.R @@ -0,0 +1,196 @@ +# Server Modules + +# Helper function to fetch and process DATASUS data +fetch_datasus_data <- function(year_start, year_end, uf, information_system, month_start = NULL, month_end = NULL) { + # Get data + dados <- microdatasus::fetch_datasus( + year_start = year_start, + year_end = year_end, + month_start = month_start, + month_end = month_end, + uf = uf, + information_system = information_system + ) + + dados_processados <- dados + + return(dados_processados) +} + +downloadServer <- function(id) { + moduleServer(id, function(input, output, session) { + # Reactive value to store the downloaded data + data_preview <- reactiveVal(NULL) + + # Loading screen + w <- Waiter$new( + id = "preview_table", + html = spin_dots(), + color = transparent(.5) + ) + + # Preview data action + observeEvent(input$preview, { + w$show() + + # Initialize progress only when preview button is clicked + progress <- shiny::Progress$new() + progress$set(message = "Download em andamento", value = 0) + + # Initialize progress + progress$set(message = "Baixando dados...", value = 0) + + tryCatch({ + # Basic parameter validation + validate( + need(input$ano_inicio <= input$ano_fim, "Ano inicial deve ser menor ou igual ao ano final"), + need(input$ano_inicio >= 1996 && input$ano_fim <= 2023, "Anos devem estar entre 1996 e 2023") + ) + + if(input$sistema %in% c("SIH-RD", "SIA-PA")) { + validate( + need( + !(input$ano_inicio == input$ano_fim && as.numeric(input$mes_inicio) > as.numeric(input$mes_fim)), + "Mês inicial deve ser menor ou igual ao mês final quando no mesmo ano" + ) + ) + } + + # Update progress + progress$set(value = 0.3, message = "Conectando ao DATASUS...") + + # Fetch data using microdatasus + if(input$sistema %in% c("SIH-RD", "SIA-PA")) { + dados <- fetch_datasus_data( + year_start = input$ano_inicio, + year_end = input$ano_fim, + month_start = as.numeric(input$mes_inicio), + month_end = as.numeric(input$mes_fim), + uf = input$estado, + information_system = input$sistema + ) + } else { + dados <- fetch_datasus_data( + year_start = input$ano_inicio, + year_end = input$ano_fim, + uf = input$estado, + information_system = input$sistema + ) + } + + # Update progress + progress$set(value = 0.6, message = "Processando dados...") + + # Process data according to the information system + dados_processados <- switch(input$sistema, + "SIM-DO" = microdatasus::process_sim(dados), + "SINASC" = microdatasus::process_sinasc(dados), + "SIH-RD" = microdatasus::process_sih(dados), + "SIA-PA" = microdatasus::process_sia(dados) + ) + + # Update progress + progress$set(value = 0.8, message = "Finalizando...") + + # Store the processed data + data_preview(dados_processados) + + # Update column selection choices + updateSelectInput(session, "selected_columns", + choices = names(dados_processados), + selected = names(dados_processados)[1:min(5, length(names(dados_processados)))]) + + # Complete progress + progress$set(value = 1, message = "Concluído!") + + }, error = function(e) { + shinyjs::html("error_message", paste("Erro:", e$message)) + data_preview(NULL) + progress$set(value = 1, message = "Erro no download!") + }) + + w$hide() + + # Close progress + progress$close() + }) + + # Preview table output + output$data_loaded <- reactive({ + !is.null(data_preview()) + }) + outputOptions(output, "data_loaded", suspendWhenHidden = FALSE) + + output$preview_table <- renderDT({ + req(data_preview()) + data_to_show <- if (!is.null(input$selected_columns)) { + data_preview()[, input$selected_columns, drop = FALSE] + } else { + data_preview() + } + + datatable( + head(data_to_show, 100), + options = list( + pageLength = 10, + scrollX = TRUE + ) + ) + }) + + # Download handler + output$download <- downloadHandler( + filename = function() { + ext <- if(input$formato == "xlsx") "xlsx" else "csv" + paste0( + "datasus_", tolower(input$sistema), "_", + input$estado, "_", + input$ano_inicio, "-", input$ano_fim, + ".", ext + ) + }, + content = function(file) { + withProgress(message = 'Preparando arquivo para download...', value = 0, { + req(data_preview()) + + incProgress(0.3, detail = "Selecionando colunas...") + data_to_export <- if (!is.null(input$selected_columns)) { + data_preview()[, input$selected_columns, drop = FALSE] + } else { + data_preview() + } + + incProgress(0.3, detail = "Gravando arquivo...") + if(input$formato == "xlsx") { + write.xlsx(data_to_export, file) + } else { + write.csv(data_to_export, file, row.names = FALSE) + } + + incProgress(0.4, detail = "Finalizando...") + }) + } + ) + }) +} + +dictionaryServer <- function(id) { + moduleServer(id, function(input, output, session) { + output$dict_table <- renderDT({ + dict_data <- switch(input$dict_sistema, + "SIM-DO" = dicionario_sim, + "SINASC" = dicionario_sinasc, + "SIH-RD" = dicionario_sih, + "SIA-PA" = dicionario_sia) + + datatable( + dict_data, + options = list( + pageLength = 25, + dom = 't', + scrollY = TRUE + ) + ) + }) + }) +} \ No newline at end of file diff --git a/R/ui_modules.R b/R/ui_modules.R new file mode 100644 index 0000000..af01c9a --- /dev/null +++ b/R/ui_modules.R @@ -0,0 +1,71 @@ +# UI Modules for each tab +downloadTabUI <- function(id) { + ns <- NS(id) + + layout_sidebar( + sidebar = sidebar( + title = "Opções de Download", + + selectInput(ns("sistema"), "Sistema de Informação:", + choices = info_systems), + + selectInput(ns("estado"), "Estado:", + choices = estados), + + numericInput(ns("ano_inicio"), "Ano Inicial:", + value = 2023, min = 1996, max = 2023), + + numericInput(ns("ano_fim"), "Ano Final:", + value = 2023, min = 1996, max = 2023), + + conditionalPanel( + condition = sprintf("input['%s'] == 'SIH-RD' || input['%s'] == 'SIA-PA'", + ns("sistema"), ns("sistema")), + selectInput(ns("mes_inicio"), "Mês Inicial:", + choices = meses, + selected = 1), + selectInput(ns("mes_fim"), "Mês Final:", + choices = meses, + selected = 12) + ), + + actionButton(ns("preview"), "Visualizar Dados", class = "btn-primary"), + + conditionalPanel( + condition = sprintf("output['%s']", ns("data_loaded")), + hr(), + selectInput(ns("selected_columns"), "Selecionar Colunas:", + choices = NULL, + multiple = TRUE) + ), + + radioButtons(ns("formato"), "Formato do arquivo:", + choices = c("Excel (xlsx)" = "xlsx", "CSV" = "csv"), + selected = "xlsx"), + + downloadButton(ns("download"), "Baixar Dados"), + + div(id = ns("error_message"), style = "color: red;") + ), + + card( + card_header("Prévia dos Dados"), + DTOutput(ns("preview_table")) + ) + ) +} + +dictionaryTabUI <- function(id) { + ns <- NS(id) + + layout_sidebar( + sidebar = sidebar( + selectInput(ns("dict_sistema"), "Selecione o Sistema:", + choices = info_systems) + ), + card( + card_header("Descrição das Variáveis"), + DTOutput(ns("dict_table")) + ) + ) +} \ No newline at end of file diff --git a/app.R b/app.R index eb2d023..ecfa260 100644 --- a/app.R +++ b/app.R @@ -3,150 +3,78 @@ library(bslib) library(microdatasus) library(dplyr) library(openxlsx) +library(shinyjs) +library(waiter) +library(DT) -# List of available information systems -info_systems <- c( - "Sistema de Informações sobre Mortalidade (SIM-DO)" = "SIM-DO", - "Sistema de Informações sobre Nascidos Vivos (SINASC)" = "SINASC", - "Sistema de Informações Hospitalares (SIH-RD)" = "SIH-RD", - "Sistema de Informações Ambulatoriais (SIA-PA)" = "SIA-PA" -) - -# List of Brazilian states -estados <- c( - "Acre" = "AC", "Alagoas" = "AL", "Amapá" = "AP", "Amazonas" = "AM", - "Bahia" = "BA", "Ceará" = "CE", "Distrito Federal" = "DF", - "Espírito Santo" = "ES", "Goiás" = "GO", "Maranhão" = "MA", - "Mato Grosso" = "MT", "Mato Grosso do Sul" = "MS", "Minas Gerais" = "MG", - "Pará" = "PA", "Paraíba" = "PB", "Paraná" = "PR", "Pernambuco" = "PE", - "Piauí" = "PI", "Rio de Janeiro" = "RJ", "Rio Grande do Norte" = "RN", - "Rio Grande do Sul" = "RS", "Rondônia" = "RO", "Roraima" = "RR", - "Santa Catarina" = "SC", "São Paulo" = "SP", "Sergipe" = "SE", - "Tocantins" = "TO" -) +# Source all modules and helper files +source("global.R") +source("R/ui_modules.R") +source("R/server_modules.R") -# List of months in Portuguese with numeric values -meses <- setNames(1:12, c( - "Janeiro", "Fevereiro", "Março", "Abril", - "Maio", "Junho", "Julho", "Agosto", - "Setembro", "Outubro", "Novembro", "Dezembro" -)) - -ui <- page_sidebar( - title = "Download de Dados do DATASUS", - theme = bs_theme(bootswatch = "flatly"), +ui <- page_fluid( + useShinyjs(), + useWaiter(), - sidebar = sidebar( - title = "Opções de Download", - - selectInput("sistema", "Sistema de Informação:", - choices = info_systems), + navset_card_tab( + title = "Download de Dados do DATASUS", - selectInput("estado", "Estado:", - choices = estados), - - numericInput("ano_inicio", "Ano Inicial:", - value = 2023, min = 1996, max = 2023), - - numericInput("ano_fim", "Ano Final:", - value = 2023, min = 1996, max = 2023), + nav_panel( + title = "Instruções", + card( + card_header("Como usar"), + tags$ol( + tags$li("Selecione o sistema de informação desejado"), + tags$li("Escolha o estado"), + tags$li("Defina o período (ano inicial e final)"), + tags$li("Para SIH e SIA, selecione também os meses inicial e final"), + tags$li("Clique em 'Visualizar Dados' para ver uma prévia"), + tags$li("Selecione as colunas desejadas (opcional)"), + tags$li("Escolha o formato do arquivo"), + tags$li("Clique em 'Baixar Dados'") + ), + tags$p( + tags$strong("Obs:"), + "Para períodos longos, o download pode demorar alguns minutos." + ) + ) + ), - # Add month inputs that appear only for SIH and SIA - conditionalPanel( - condition = "input.sistema == 'SIH-RD' || input.sistema == 'SIA-PA'", - selectInput("mes_inicio", "Mês Inicial:", - choices = meses, - selected = 1), - selectInput("mes_fim", "Mês Final:", - choices = meses, - selected = 12) + nav_panel( + title = "Download", + downloadTabUI("download") ), - radioButtons("formato", "Formato do arquivo:", - choices = c("Excel (xlsx)" = "xlsx", "CSV" = "csv"), - selected = "xlsx"), + nav_panel( + title = "Dicionário de Variáveis", + dictionaryTabUI("dictionary") + ), - downloadButton("download", "Baixar Dados") - ), - - card( - card_header("Instruções"), - p("1. Selecione o sistema de informação desejado"), - p("2. Escolha o estado"), - p("3. Defina o período (ano inicial e final)"), - p("4. Para SIH e SIA, selecione também os meses inicial e final"), - p("5. Selecione o formato do arquivo"), - p("6. Clique em 'Baixar Dados'"), - p("Obs: Para períodos longos, o download pode demorar alguns minutos.") + nav_panel( + title = "Sobre", + card( + card_header("Informações sobre os Sistemas"), + + h4("SIM-DO (Sistema de Informações sobre Mortalidade)"), + p("Contém informações sobre óbitos, incluindo causa mortis, local, data e dados demográficos."), + + h4("SINASC (Sistema de Informações sobre Nascidos Vivos)"), + p("Registra informações sobre nascimentos, incluindo dados da mãe, da gestação e do recém-nascido."), + + h4("SIH-RD (Sistema de Informações Hospitalares)"), + p("Registra todas as internações hospitalares financiadas pelo SUS."), + + h4("SIA-PA (Sistema de Informações Ambulatoriais)"), + p("Contém registros de todos os atendimentos ambulatoriais realizados pelo SUS.") + ) + ) ) ) server <- function(input, output, session) { - - # Reactive expression to fetch and process data - dados <- reactive({ - # Show a loading message - withProgress(message = 'Baixando dados...', { - - # Different fetch logic based on the system - if (input$sistema %in% c("SIH-RD", "SIA-PA")) { - dados_brutos <- fetch_datasus( - year_start = input$ano_inicio, - year_end = input$ano_fim, - month_start = as.numeric(input$mes_inicio), - month_end = as.numeric(input$mes_fim), - uf = input$estado, - information_system = input$sistema - ) - } else { - dados_brutos <- fetch_datasus( - year_start = input$ano_inicio, - year_end = input$ano_fim, - uf = input$estado, - information_system = input$sistema - ) - } - - # Process the data according to the system - dados_processados <- switch(input$sistema, - "SIM-DO" = process_sim(dados_brutos), - "SINASC" = process_sinasc(dados_brutos), - "SIH-RD" = process_sih(dados_brutos), - "SIA-PA" = process_sia(dados_brutos, - information_system = "SIA-PA", - nome_proced = TRUE, - nome_ocupacao = TRUE, - nome_equipe = TRUE, - municipality_data = TRUE) - ) - - return(dados_processados) - }) - }) - - # Download handler - output$download <- downloadHandler( - filename = function() { - if (input$sistema %in% c("SIH-RD", "SIA-PA")) { - paste0("dados_", input$sistema, "_", input$estado, "_", - input$ano_inicio, ".", as.numeric(input$mes_inicio), "-", - input$ano_fim, ".", as.numeric(input$mes_fim), ".", input$formato) - } else { - paste0("dados_", input$sistema, "_", input$estado, "_", - input$ano_inicio, "-", input$ano_fim, ".", input$formato) - } - }, - content = function(file) { - if (input$formato == "csv") { - write.csv(dados(), file, row.names = FALSE) - } else { - wb <- createWorkbook() - addWorksheet(wb, "Dados") - writeData(wb, "Dados", dados()) - saveWorkbook(wb, file, overwrite = TRUE) - } - } - ) + # Call module servers + downloadServer("download") + dictionaryServer("dictionary") } shinyApp(ui, server) \ No newline at end of file diff --git a/global.R b/global.R new file mode 100644 index 0000000..16075fd --- /dev/null +++ b/global.R @@ -0,0 +1,12 @@ +library(shiny) +library(bslib) +library(microdatasus) +library(dplyr) +library(openxlsx) +library(shinyjs) +library(waiter) +library(DT) + +# Source all helper files +source("R/constants.R") +source("R/dictionaries.R") \ No newline at end of file