From 820ed486d2b0e9f93c17c5b88e770b293ac9e819 Mon Sep 17 00:00:00 2001 From: Edouard Date: Mon, 25 Sep 2023 21:44:11 -0500 Subject: [PATCH 1/3] interface modularisation - put things into graveler.. --- .github/ISSUE_TEMPLATE/bug_report.md | 30 + .github/ISSUE_TEMPLATE/feature_request.md | 20 + R/_disable_autoload.R | 3 - R/app_config.R | 47 +- R/app_server.R | 18 +- R/app_ui.R | 660 +-------------------- R/body.R | 28 + R/globals.R | 17 +- R/golem_utils_server.R | 64 -- R/header.R | 5 + R/mod_collection.R | 162 +++++ R/mod_content.R | 235 ++++++++ R/mod_context.R | 162 +++++ R/mod_forms.R | 131 ++++ R/mod_home.R | 90 +++ R/mod_home_ui.R | 34 -- R/run_app.R | 10 +- R/sidebar.R | 23 + R/theme.R | 110 ---- R/utils-pipe.R | 14 - README.md | 4 +- dev/01_dev.R | 63 ++ dev/01_start.R | 68 --- dev/02_dev.R | 96 --- dev/03_deploy.R | 62 -- dev/app_ui_initial_wireframe.R | 691 ++++++++++++++++++++++ dev/flat_mod_home.Rmd | 70 --- dev/note | 79 +++ dev/run_dev.R | 6 +- docs/articles/mon-module-home.html | 128 ++++ man/pipe.Rd | 20 - man/run_app.Rd | 32 +- 32 files changed, 1920 insertions(+), 1262 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md delete mode 100644 R/_disable_autoload.R mode change 100644 => 100755 R/app_server.R mode change 100644 => 100755 R/app_ui.R create mode 100755 R/body.R delete mode 100644 R/golem_utils_server.R create mode 100644 R/header.R create mode 100644 R/mod_collection.R create mode 100644 R/mod_content.R create mode 100644 R/mod_context.R create mode 100644 R/mod_forms.R create mode 100644 R/mod_home.R delete mode 100644 R/mod_home_ui.R mode change 100644 => 100755 R/run_app.R create mode 100755 R/sidebar.R delete mode 100755 R/theme.R delete mode 100644 R/utils-pipe.R create mode 100755 dev/01_dev.R delete mode 100644 dev/01_start.R delete mode 100644 dev/02_dev.R delete mode 100644 dev/03_deploy.R create mode 100644 dev/app_ui_initial_wireframe.R delete mode 100644 dev/flat_mod_home.Rmd create mode 100644 dev/note mode change 100644 => 100755 dev/run_dev.R create mode 100644 docs/articles/mon-module-home.html delete mode 100644 man/pipe.Rd mode change 100644 => 100755 man/run_app.Rd diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..2214757 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,30 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG]" +labels: bug +assignees: edouard-legoupil + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. +1. ... +2. ... + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**session info** + +please insert here the output of `devtools::session_info()` + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..524940e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[FR]" +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/R/_disable_autoload.R b/R/_disable_autoload.R deleted file mode 100644 index a8c9436..0000000 --- a/R/_disable_autoload.R +++ /dev/null @@ -1,3 +0,0 @@ -# Disabling shiny autoload - -# See ?shiny::loadSupport for more information diff --git a/R/app_config.R b/R/app_config.R index cf86275..7f5817d 100644 --- a/R/app_config.R +++ b/R/app_config.R @@ -1,44 +1,43 @@ -#' Access files in the current app -#' + ## Prewrite the app config ---------- + #' Access files in the current app + #' #' NOTE: If you manually change your package name in the DESCRIPTION, #' don't forget to change it here too, and in the config file. #' For a safer name change mechanism, use the `golem::set_golem_name()` function. #' #' @param ... character vectors, specifying subdirectory and file(s) #' within your package. The default, none, returns the root of the app. -#' -#' @noRd + #' + #' @noRd app_sys <- function(...) { - system.file(..., package = "surveyDesigner") +system.file(..., package = "surveyDesigner") } - - #' Read App Config -#' + #' #' @param value Value to retrieve from the config file. #' @param config GOLEM_CONFIG_ACTIVE value. If unset, R_CONFIG_ACTIVE. #' If unset, "default". #' @param use_parent Logical, scan the parent directory for config file. -#' @param file Location of the config file + #' @param file Location of the config file #' #' @noRd get_golem_config <- function( - value, - config = Sys.getenv( - "GOLEM_CONFIG_ACTIVE", - Sys.getenv( - "R_CONFIG_ACTIVE", - "default" - ) - ), - use_parent = TRUE, - # Modify this if your config file is somewhere else - file = app_sys("golem-config.yml") +value, +config = Sys.getenv( +"GOLEM_CONFIG_ACTIVE", +Sys.getenv( +"R_CONFIG_ACTIVE", +"default" + ) + ), +use_parent = TRUE, +# Modify this if your config file is somewhere else + file = app_sys("golem-config.yml") ) { config::get( - value = value, - config = config, + value = value, + config = config, file = file, - use_parent = use_parent + use_parent = use_parent ) -} + } diff --git a/R/app_server.R b/R/app_server.R old mode 100644 new mode 100755 index f431311..664cae2 --- a/R/app_server.R +++ b/R/app_server.R @@ -1,9 +1,19 @@ -#' The application server-side +#' Server +#' +#' This function is internally used to manage the shinyServer #' -#' @param input,output,session Internal parameters for {shiny}. -#' DO NOT REMOVE. #' @import shiny +#' @import shinydashboard #' @noRd +#' @keywords internal app_server <- function(input, output, session) { - # Your application server logic + + ## add a reactive value object to pass by elements between objects + AppReactiveValue <- reactiveValues() + # pins::board_register() # connect to pin board if needed + callModule(mod_home_server, "home_ui_1") + callModule(mod_context_server, "context_ui_1", AppReactiveValue) + callModule(mod_content_server, "content_ui_1", AppReactiveValue) + callModule(mod_collection_server, "collection_ui_1", AppReactiveValue) + callModule(mod_forms_server, "forms_ui_1", AppReactiveValue) } diff --git a/R/app_ui.R b/R/app_ui.R old mode 100644 new mode 100755 index 2d698a6..e6c85f3 --- a/R/app_ui.R +++ b/R/app_ui.R @@ -1,643 +1,24 @@ #' The application User-Interface #' -#' @param request Internal parameter for `{shiny}`. -#' DO NOT REMOVE. +#' This function is internally used to manage the UI entry point +#' #' @import shiny +#' @import golem #' @import shinydashboard -#' -#' @return Front end of app -#' #' @noRd -app_ui <- function(request) { - - +#' @keywords internal +app_ui <- function() { tagList( - - - # enable alert messages - #shinyWidgets::useSweetAlert(), - - # Leave this function for adding external resources - golem_add_external_resources(), - theme_dashboard(), - # List the first level UI elements here - dashboardPage( - dashboardHeader(title = "Survey Designer"), - dashboardSidebar( - #minified = FALSE, - #collapsed = FALSE, - #width = "10vw", - sidebarMenu( - menuItem(" Introduction", tabName = "Intro", icon = icon("circle-info")), - menuItem(" Step 1. Define Context", tabName = "Context", icon = icon("location-dot")), - menuItem(" Step 2. Configure Content", tabName = "Content", icon = icon("arrows-to-circle")), - menuItem(" Step 3. Set up Collection", tabName = "Collection", icon = icon("list-check")), - menuItem(" Step 4. Publish Surveys", tabName = "Forms", icon = icon("share-from-square")), - menuItem(" -- Save or Load Session", icon = icon("floppy-disk"), - menuSubItem("Save session Unique ID", tabName = "subitem1", icon = icon("angles-right")), - menuSubItem("Load session using unique ID", tabName = "subitem2", icon = icon("angles-left"))) - ) - ), - - dashboardBody( - tabItems( - tabItem( - tabName = "Intro", - fluidRow( - "--WARNING - you are now viewing the prototype wireframe - the application is not yet functional--", - tags$a(href="https://github.com/unhcr-americas/surveyDesigner", "Please check the github repo for more info and to provide feedback!"), - - box(title = "", - width = NULL, - collapsible = FALSE, - status = "info", - - - tags$div(class = "jumbotron text-left", - style = "margin-bottom:5px;margin-top:5px;margin-left:5px", - tags$h1(style = 'color:#0072BC;', - style = 'margin-bottom:0px;margin-top:0px', - 'As Local Survey Coordinator, integrating, configuring and tailoring multiple surveys at once - is often challenging!'), - tags$i(class = 'jumbotron-heading', - style = 'margin-left:25px', - 'How can you confirm that you will not miss a specific data point to calculate your final indicators and disaggregation?... - How can you make sure to enforce all existing statistical standards and good practices while also contextualizing correctly questions labels?... - How can you maximise your resources to collect your data smarty, through multiple waves and modes?... ' ) - ), ## end jumbotron.. - "This app aims at addressing the complex challenges associated with the design of integrated surveys. It is expected to help:" , - tags$ol( - tags$li(" Standardizing a questionnaire design process that starts with a",tags$b(" selection of indicators ") ,"that needs - to be measured and thenpre-select automatically all the required standardized survey modules and - questions in order to measure them, aka 'collect only what you need and use everything that you collected'. - This will ensure that standards are applied, - but it will also minimize the risk of questionnaire design mistakes" ), - tags$li(" Easing the",tags$b(" contextualization process ") ,"from the global referential to the actual implementation - in each country (i.e translating and adjusting the labels as per the specific context) and - facilitate a feedback loop so that if the same ad-hoc questions are used in multiple context, - they can be considered for inclusion in the global referential "), - tags$li(" Supporting the usage of mixed-mode (e.g.CAPI/CATI/CAWI) and multiple data collection waves - to collect the final dataset within an",tags$b(" annual survey data collection cycle") ,", all of - this allowing both to promote survey integration and to maximize the financial - resources invested in those activities" ) - ) - #br(), - ) - ), - fluidRow( - # Valid statuses are: primary, success, info, warning, danger. - column( - 3, - box(title = " Step 1. Define Context", - width = NULL, - collapsible = FALSE, - status = "warning", - "First, document the operational purpose of the data that you will need to collect for the incoming cycle.", - br(), - "This will be the first be the first filter of the design process", - br(), - actionButton(inputId="start", - label="Start now!", - icon("location-dot") ) - ) - ), - column( - 3, - box(title = " Step 2. Configure Content", - width = NULL, - status = "warning", - "Second, based on the consultation with partners and sectoral experts, select the topics and indicators - that needs to be covered for your annual survey cycle", - br() ) - ), - column( - 3, - box(title = " Step 3. Set up Collection", - width = NULL, - collapsible = FALSE, - status = "warning", - "Third, provides information about your data collection capacity, what kind - of mode can you use, how many waves can you afford, what enumeration capacity can you afford...", - br() ) - ), - column( - 3, - box(title = " Step 4. Publish Surveys", - width = NULL, - collapsible = FALSE, - status = "warning", - "Last, revise as pretty print word documents & export as xlsforms and the single or multiples - questionnaires that you will be required to implement your annual survey cycle. - You maybe need to then revise some questions labels. - Once done, you will get all the files needed to collect the data through kobotoolbox ") - ) - ) # End fluid row - ), - # Context ##### - tabItem(tabName = "Context", - fluidRow( - # Valid statuses are: primary, success, info, warning, danger. - column( - 3, - box(title = " 1. Metadata", - width = NULL, - collapsible = FALSE, - status = "warning", - " ", - textInput("caption", - label="Title for your data collection project", - value="short title"), - tags$div(title=" ", - selectInput("context", - label = "Year for your data collection", - choices = list("2025" = "2025", - "2024" = "2024", - "2023" = "2023" ), - selected = "2023") ), - textAreaInput("caption", - label="Provide an abstract for the data collection activities", - value="Who will collect? - What is the sample frame? - What are the main field access constraints?", - width = "100%") - ) - ) , - - column( - 3, - box(title = " 2. Methodology / Operational Purpose", - width = NULL, - collapsible = FALSE, - status = "warning", - " ", - tags$div(title="Interview", - radioButtons("library", - label = " What global referential to use?" , - choices = list("Household Survey (Ideally representative of stable population in a protracted situation)" = "household_survey", - "Flow Monitoring (People not aiming to establish their habitual residence in the place of interview)" = "flow_monitoring", - "Key Informant (Persons with local qualitative knowledge in an emergency situation)" = "key_informant"), - selected = "household_survey" ) ), - br(), - " Referentials are centrally managed by UNHCR Global Data Service. They are based on most appropriate international statistical standards (EGRISS, UN Stat Commissions, etc.)") - ), - column( - 3, - box(title = " 3. Country", - width = NULL, - status = "warning", - " ", - tags$div(title="Select your country ", - selectInput("context", - label = "Country, defining then required languages & geographic Pcodes", - choices = list("Panama" = "Panama", - "Colombia" = "Colombia", - "Ecuador" = "Ecuador" ), - selected = "Panama") ), - br(), - - tags$div(title="Geographic Coverage ", - selectInput("context", - label = "Are you aiming for a full national coverage?", - choices = list("yes" = "Yes", - "no" = "No" ), - selected = "yes") ), - - hr(), - "This will pull up the languages translation and geographic breakdown required for the country.", - " Global referentials are contextualised for each country through the regional survey support capacity in the bureau.", - "If your country does not appear in the that list, ignite a contextualisation request", - br(), - actionButton(inputId="contextualisationRequest", - label="Request Contextualisation", - icon("gears")) , - br()) - ), - column( - 3, - box(title = " 4. Targeted Groups", - width = NULL, - collapsible = FALSE, - status = "warning", - " ", - tags$div(title="Population Group to cover", - checkboxGroupInput("population", - label = "Target Group", - choices = list("Refugees (REF) & Asylum seekers (ASY)"="RAS", - "Internally displaced persons (IDP)"="IDP", - "Other people in need of international protection (OIP)"="OIP", - "Stateless Persons (STA)"="STA", - "Others of concern to UNHCR (OOC)"="OOC", - "Returnee (RET)"="RET", - "Host community (HCT)"="HCT" ), - - br() )) - ) - ) - ) , - fluidRow( - # Valid statuses are: primary, success, info, warning, danger. - column( - 12, - box(title = " Context Summary", - width = NULL, - collapsible = FALSE, - status = "success", - " ", - hr(), - actionButton(inputId="start", - label="Go to Configure Content", - icon("arrows-to-circle")) - ) - ) - )# End fluid row - ), ## end context tiem - - # Content ##### - tabItem(tabName = "Content", - fluidRow( - # Valid statuses are: primary, success, info, warning, danger. - column( - 3, - box(title = " 1. Apply filters on Topics", - width = NULL, - collapsible = FALSE, - status = "warning", - " Topics below corresponds to standard metadata. - One indicator might be linked to mulitple topics", - checkboxGroupInput("topic", - label = " Tick to filter", - choiceNames = list( - list( - icon("gavel") , - "Rights" - ), - list( - icon("home"), - "Trajectory" - ), - list( - icon("thermometer-full"), - "Intention" - ), - list( - icon("check-circle"), - "Basic Needs" - ), - list( - icon("certificate"), - "Coping Capacity" - ), - list( - icon("users"), - "Well-Being" - ), - list( - icon("blind"), - "Victimisation" - ), - list( - icon("usd"), - "Livelihood" - ), - list( - icon("book"), - "Education" - ), - list( - icon("medkit"), - "Health-Nutrition" - ), - list( - icon("eye"), - "Accountability" - ), - list( - icon("info-circle"), - "Information" - )), - choiceValues = list("Rights" , - "Trajectory" , - "Intention" , - "Basic Needs" , - "Coping Capacity" , - "Well-Being" , - "Victimisation" , - "Livelihood" , - "Education" , - "Health-Nutrition" , - "Accountability", - "Information" )), - br(), - p(" This will filters the corresponding indicators") , - br() ) - ), - column( - 3, - box(title = " 2. Apply filters on Indicators Family", - width = NULL, - status = "warning", - " One indicator might be linked to mulitple families. - The items below will bring a second filters levels on the indicator menu", - tags$div(title="Standard Indicators", - checkboxGroupInput("Disaggregation", - label = " Tick to filter ", - choices = list("UNHCR Programme / RBM"="RBM", - "UN Stat Commission / EGRISS"="EGRISS", - "Sustainable Development Goal / SDG"="SDG", - "Global Compact on Refugee / GCR"="GCR", - "Humanitarian Sphere Standards"="Sphere", - "Post Distribution & Assistance Monitoring"="PDM", - "Assistance Targeting / Excellence Hub"="targeting", - "IASC Framework on Durable Solutions for IDPs."="Durable", - "Composite Indicator / Index"="Index" - ) ) ) - ) - ), - column( - 3, - box(title = " 3. Select Indicators", - width = NULL, - status = "warning", - "Note that one indicator might requires multiples questions - within the final survey to be calculated. - Do not worry the app will take care of that complex step", - tags$div(title="Standard Indicators", - checkboxGroupInput("indicator", - label = "Select indicators - to be turned to autocomplete-multiple selection..", - choices = list("Access to drinking water"="outcome12_1", - "Residing in physically safe and secure settlements with access to basic facilities"="impact2_2", - "Could access health facilities"="impact2_3", - "Feeling safe walking alone"="impact3_3", - "Children under 5 birth registered with civil authorities"="outcome1_2", - "Has legally recognized documents"="outcome1_3", - "Proportion of PoC who know where to access available GBV services"="outcome4_1", - "Do not accept violence against women"="outcome4_2", - "With primary reliance on clean (cooking) fuels and technology"="outcome8_2", - "Living in Habitable and affordable shelter"="outcome9_1", - "Access to energy to ensure lighting"="outcome9_2", - "With access to a safe household toilet"="outcome12_2", - "With a bank account or mobile-money service provider"="outcome13_1", - "Working age individuals who are unemployed"="outcome13_3", - "With secure tenure and/or property rights"="outcome16_1", - "Covered by social protection floors/systems"="outcome16_2", - "and so on..."="oo" - ) ) ) - ) - ), - column( - 3, - box(title = " 4. Context-specific Questions", - width = NULL, - status = "warning", - " You may here add context specific questions, that is to say questions for which - the equivalent statistical construct is not (yet) available within the global referential. - Thoses questions will be shared back with the global referential manager - for potential inclusion in the next release of the referential", - br() , - hr(), - actionButton(inputId="start", - label="Select / Define Ad-hoc Questions", - icon("gears"))) - ) - ) , - fluidRow( - # Valid statuses are: primary, success, info, warning, danger. - column( - 12, - box(title = " Indicator Plan -- Analysis", - width = NULL, - collapsible = FALSE, - status = "success", - " A small summary review will be automatically generated below. - it's not for data entry but rather to output a summary - of the decision points that have been set up above.. - For instance - indicating how many unique questions - would be required in order to compile all the indicators - that got selected..", - br(), - hr(), - actionButton(inputId="start", - label="Next Set up Collection", - icon("list-check")) - ) - ) - )# End fluid row - ), # end content item... - - # Collection #### - tabItem(tabName = "Collection" , - fluidRow( - # Valid statuses are: primary, success, info, warning, danger. - column( - 4, - box(title = " 1. Modes", - width = NULL, - collapsible = FALSE, - status = "warning", - "Using multiple data collection modes can help minimizing - non-reponse and increase coverage. ", - tags$a(href="https://www.youtube.com/watch?v=qRmy2OAnyWc", "Learn more on mixed-mode here"), - br(), - checkboxGroupInput("mode", - label = "What Data Collection Modes do you have capacity to implement for this annual cycle?" , - choiceNames = list( - list( - icon("clipboard-question"), - "CAPI"="CAPI, i.e. Face-to-face " - ), - list( - icon("phone-square") , - "CATI"="CATI, i.e. Phone-Interview " - ), - list( - icon("wifi"), - "CAWI"= "CAWI, i.e. Self-administered " - )), - choiceValues = list("CAPI" , - "CATI" , - "CAWI" )), - br() ) - ), - column( - 4, - box(title = " 2. Waves", - width = NULL, - status = "warning", - " Define how many data collection waves you can manage within the year.", - - "Using multiple data collection waves can provide an opportunity for - more indicators to be collected. ", - tags$a(href="https://openknowledge.worldbank.org/server/api/core/bitstreams/e80ce277-f8d0-58aa-87b7-288c2895e87a/content#page=4", "Learn more on multi-waves here"), - - br(), - selectInput("wave", - label = "How many data collection waves for this annual cycle?", - choices = list("One Wave" = "One-wave", - "Two Waves" = "Two-waves", - "Three Waves" = "Three-waves", - "Four Waves" = "Four-waves" ), - selected = "One-waves"), - "As a result of this stage, below is an estimation of Interview lenght per questionnaire", - br() ) - ), - column( - 4, - box(title = " 3. Enumeration Capacity", - width = NULL, - collapsible = FALSE, - status = "warning", - tags$div(title="Budget", - numericInput("budget in $", - label = "Available Annual Budget for Data Collection (in US$)", - 30000, - min = 5000, - max = 500000) ), - "The Maximum monthly data collection depends on the capacity of the - organisation that partners or on the company that got contracted for that activity", - - hr(), - tags$div(title="Face1", - numericInput("face", - label = "Total Cost per Interview (in US$) for Face to face", - 50, min = 20, max = 300) ), - tags$div(title="Face2", - numericInput("face", - label = "Max Capacity of monthly working day for Face to face enumerators", - 50, min = 1, max = 100) ), - hr(), - tags$div(title="Face1", - numericInput("face", - label = "Total Cost per Interview (in US$) for Telephone", - 20, min = 5, max = 100) ), - - tags$div(title="Call Center enumerators", - numericInput("face", - label = "Max Capacity of monthly working day for Call Center enumerators", - 20, min = 1, max = 100) ), - br() ) - ) - ) , - fluidRow( - # Valid statuses are: primary, success, info, warning, danger. - column( - 12, - box(title = " Data Collection Plan -- Analysis", - width = NULL, - collapsible = FALSE, - status = "success", - "Feedback on your data collection analysis plan based on - estimated interview duration, budget and enumeration capacity - will be automatically generated below. - indication of the suggested length of the interview given the mode: - CAPI, 60-90 min; CATI; 30-45 min…and that dictates the number - of questions that are able to be included. - function for that https://edouard-legoupil.github.io/XlsFormPrettyPrint/reference/interview_duration.html", - hr(), - actionButton(inputId="start", - label="Now Publish Survey", - icon("share-from-square") - ) - )) - )# End fluid row - ), # end collection item - # Forms #### - tabItem(tabName = "Forms", - fluidRow( - # Valid statuses are: primary, success, info, warning, danger. - column( - 3, - box(title = " 1. Survey Introduction Scripts", - width = NULL, - collapsible = FALSE, - status = "warning", - " This is the text that enumerator shold use to introduce the interview. - It is important for it to be context sensitive... ", - textAreaInput("caption", - label="Add the script", - value=" Hello, my name is .. and we would like to interview you for.... - Data will be used by.. You will be able to see aggregated results here...", - width = "100%"), - br() ) - ), - column( - 3, - box(title = " 2. Survey Modules Sequence", - width = NULL, - status = "warning", - " Adjust the default sequence between modules and - then between questions within modules.", - br(), - "Note that the design already comes with suggested sequence of modules. - For instance depending on the mode, sensitive or longer/more complicated - questions comes towards the end for CAPI while they are more at the begining for CATI. - Also not all sequences can be changed as the questionnaire logic comes with hard constraints - to be respected.") - ), - column( - 3, - box(title = " 3. Preview Questionnaires", - width = NULL, - collapsible = FALSE, - status = "warning", - " Get a word version of the different questionnaires for an easy revision with field colleagues", - actionButton(inputId="start", - label="Pretty Print", - icon("file-word")) - ) - ) , - column( - 3, - box(title = " 4. Labels Review Request", - width = NULL, - collapsible = FALSE, - status = "warning", - " You may then request changes in the contextualised referentials.... ", - actionButton(inputId="start", - label="Request Changes", - icon("gears")) - ) - ) - ), - fluidRow( - column( - 6, - box(title = " Documentation", - width = NULL, - collapsible = FALSE, - status = "success", - "Download the documentation for your annual survey data collection cycle", - actionButton(inputId="start", - label="Annual Survey Cycle", - icon("boxes-packing")) - ) - ), - column( - 6, - box(title = " Publication", - width = NULL, - collapsible = FALSE, - status = "success", - "Publish all Surveys directly on the data collection server: ", - br(), - actionButton(inputId="start", - label="Publish on Kobo", - icon("paper-plane")), - br(), - " or download the corresponding xlsform files to upload them yourself on kobotoolbox: ", - br(), - actionButton(inputId="start", - label="XlsForm", - icon("file-excel")) - ) - ) - )# End fluid row - ) # end publish item - ) #end all items - ) # end body - ) # end page - ) # end tag + golem_add_external_resources() + ) + shinydashboard::dashboardPage( + header(), + sidebar(), + body( ) + ) } + #' Add external Resources to the Application #' #' This function is internally used to add external @@ -646,22 +27,11 @@ app_ui <- function(request) { #' @import shiny #' @importFrom golem add_resource_path activate_js favicon bundle_resources #' @noRd +#' @keywords internal golem_add_external_resources <- function() { add_resource_path( "www", app_sys("app/www") ) - - tags$head( - golem::activate_js(), - golem::favicon(), - bundle_resources( - path = app_sys("app/www"), - app_title = "surveyDesigner" - ) - # Add here other external resources - # If you have a custom.css in the inst/app/www - # Or for example, you can add shinyalert::useShinyalert() here - #tags$link(rel="stylesheet", type="text/css", href="www/custom.css") - ) } + diff --git a/R/body.R b/R/body.R new file mode 100755 index 0000000..d6d5b63 --- /dev/null +++ b/R/body.R @@ -0,0 +1,28 @@ +#' UI Body +#' +#' This function is internally used to manage the body +#' +#' @import shiny +#' @import shinydashboard +#' @importFrom unhcrshiny theme_shinydashboard_unhcr +#' @noRd +#' @keywords internal + +body <- function() { + shinydashboard::dashboardBody( + unhcrshiny::theme_shinydashboard_unhcr(), + golem::activate_js(), + tags$head( + tags$script(src = "custom.js") + ), + shinydashboard::tabItems( + #Add ui module here - separated with a coma! + mod_home_ui("home_ui_1"), + mod_context_ui("context_ui_1"), + mod_content_ui("content_ui_1"), + mod_collection_ui("collection_ui_1"), + mod_forms_ui("forms_ui_1") + + ) + ) +} diff --git a/R/globals.R b/R/globals.R index 2dd42b2..55bd7df 100644 --- a/R/globals.R +++ b/R/globals.R @@ -1,8 +1,13 @@ +# remotes::install_github("thinkr-open/checkhelper") +# checkhelper::print_globals() + globalVariables(unique(c( - # get_choices_for_question: - "list_name", "name", "label", - # get_groups: - ".", - # get_groups : : + # mod_home_server: + "parent_session", + # get_choices_for_question: + "label", "list_name", "name", + # get_groups: + ".", + # get_groups : : "type" -))) \ No newline at end of file +))) \ No newline at end of file diff --git a/R/golem_utils_server.R b/R/golem_utils_server.R deleted file mode 100644 index 2806f6f..0000000 --- a/R/golem_utils_server.R +++ /dev/null @@ -1,64 +0,0 @@ -#' Inverted versions of in, is.null and is.na -#' -#' @noRd -#' -#' @examples -#' 1 %not_in% 1:10 -#' not_null(NULL) -`%not_in%` <- Negate(`%in%`) - -not_null <- Negate(is.null) - -not_na <- Negate(is.na) - -#' Removes the null from a vector -#' -#' @noRd -#' -#' @example -#' drop_nulls(list(1, NULL, 2)) -drop_nulls <- function(x){ - x[!sapply(x, is.null)] -} - -#' If x is `NULL`, return y, otherwise return x -#' -#' @param x,y Two elements to test, one potentially `NULL` -#' -#' @noRd -#' -#' @examples -#' NULL %||% 1 -"%||%" <- function(x, y){ - if (is.null(x)) { - y - } else { - x - } -} - -#' If x is `NA`, return y, otherwise return x -#' -#' @param x,y Two elements to test, one potentially `NA` -#' -#' @noRd -#' -#' @examples -#' NA %||% 1 -"%|NA|%" <- function(x, y){ - if (is.na(x)) { - y - } else { - x - } -} - -#' Typing reactiveValues is too long -#' -#' @inheritParams reactiveValues -#' @inheritParams reactiveValuesToList -#' -#' @noRd -# rv <- shiny::reactiveValues -# rvtl <- shiny::reactiveValuesToList - diff --git a/R/header.R b/R/header.R new file mode 100644 index 0000000..8c63bdc --- /dev/null +++ b/R/header.R @@ -0,0 +1,5 @@ +header <- function() { + shinydashboard::dashboardHeader( title = ' +surveyDesigner +') +} diff --git a/R/mod_collection.R b/R/mod_collection.R new file mode 100644 index 0000000..2893c33 --- /dev/null +++ b/R/mod_collection.R @@ -0,0 +1,162 @@ +#' Module UI + +#' @title mod_collection_ui and mod_collection_server +#' @description A shiny module. +#' @description A shiny module. +#' @param id,input,output,session Internal parameters for {shiny}. +#' +#' @noRd +#' @import shiny +#' @import shinydashboard +#' @keywords internal + +mod_collection_ui <- function(id) { + ns <- NS(id) + tabItem( + tabName = "collection", + fluidRow( + column( + width = 12, + h2('Step 3. Set up Collection'), + p("Third, provides information about your data collection capacity, + what kind of mode can you use, how many waves can you afford, + what enumeration capacity can you afford... ") + ) + ), + + fluidRow( + # Valid statuses are: primary, success, info, warning, danger. + column( + 4, + box(title = " 1. Modes", + width = NULL, + collapsible = FALSE, + status = "warning", + "Using multiple data collection modes can help minimizing + non-reponse and increase coverage. ", + tags$a(href="https://www.youtube.com/watch?v=qRmy2OAnyWc", "Learn more on mixed-mode here"), + br(), + checkboxGroupInput(inputId = ns("mode"), + label = "What Data Collection Modes do you have capacity to implement for this annual cycle?" , + choiceNames = list( + list( + icon("clipboard-question"), + "CAPI"="CAPI, i.e. Face-to-face " + ), + list( + icon("phone-square") , + "CATI"="CATI, i.e. Phone-Interview " + ), + list( + icon("wifi"), + "CAWI"= "CAWI, i.e. Self-administered " + )), + choiceValues = list("CAPI" , + "CATI" , + "CAWI" )), + br() ) + ), + column( + 4, + box(title = " 2. Waves", + width = NULL, + status = "warning", + " Define how many data collection waves you can manage within the year.", + + "Using multiple data collection waves can provide an opportunity for + more indicators to be collected. ", + tags$a(href="https://openknowledge.worldbank.org/server/api/core/bitstreams/e80ce277-f8d0-58aa-87b7-288c2895e87a/content#page=4", "Learn more on multi-waves here"), + + br(), + selectInput(inputId = ns("wave"), + label = "How many data collection waves for this annual cycle?", + choices = list("One Wave" = "One-wave", + "Two Waves" = "Two-waves", + "Three Waves" = "Three-waves", + "Four Waves" = "Four-waves" ), + selected = "One-waves"), + "As a result of this stage, below is an estimation of Interview lenght per questionnaire", + br() ) + ), + column( + 4, + box(title = " 3. Enumeration Capacity", + width = NULL, + collapsible = FALSE, + status = "warning", + # tags$div(title="Budget", + # numericInput("budget in $", + # label = "Available Annual Budget for Data Collection (in US$)", + # 30000, + # min = 5000, + # max = 500000) ), + "The Maximum monthly data collection depends on the capacity of the + organisation that partners or on the company that got contracted for that activity", + + hr(), + tags$div(title="Face1", + numericInput(inputId = ns("face1"), + label = "Total Cost per Interview (in US$) for Face to face", + 50, min = 20, max = 300) ), + tags$div(title="Face2", + numericInput(inputId = ns("face2"), + label = "Max Capacity of monthly working day for Face to face enumerators", + 50, min = 1, max = 100) ), + hr(), + tags$div(title="Tel1", + numericInput(inputId = ns("Tel1"), + label = "Total Cost per Interview (in US$) for Telephone", + 20, min = 5, max = 100) ), + + tags$div(title="Call Center enumerators", + numericInput(inputId = ns("callcenter"), + label = "Max Capacity of monthly working day for Call Center enumerators", + 20, min = 1, max = 100) ), + br() ) + ) + ) , + fluidRow( + # Valid statuses are: primary, success, info, warning, danger. + column( + 12, + box(title = " Data Collection Plan -- Analysis", + width = NULL, + collapsible = FALSE, + status = "success", + "Feedback on your data collection analysis plan based on + estimated interview duration, budget and enumeration capacity + will be automatically generated below. + indication of the suggested length of the interview given the mode: + CAPI, 60-90 min; CATI; 30-45 min…and that dictates the number + of questions that are able to be included. + function for that https://edouard-legoupil.github.io/XlsFormPrettyPrint/reference/interview_duration.html", + hr(), + actionButton(inputId = ns("start"), + label="Now Publish Survey", + icon("share-from-square") + ) + )) + )# End fluid row + ) +} + +#' Module Server +#' @noRd +#' @import shiny +#' @import tidyverse +#' @keywords internal + +mod_collection_server <- function(input, output, session, AppReactiveValue) { + ns <- session$ns +## add here the server logic part of your module.... +} + +## copy to body.R +# mod_collection_ui("collection_ui_1") + +## copy to sidebar.R +# shinydashboard::menuItem("displayName",tabName = "collection",icon = icon("user")) + +## and copy to app_server.R +# callModule(mod_collection_server, "collection_ui_1", AppReactiveValue) + diff --git a/R/mod_content.R b/R/mod_content.R new file mode 100644 index 0000000..68b0528 --- /dev/null +++ b/R/mod_content.R @@ -0,0 +1,235 @@ +#' Module UI + +#' @title mod_content_ui and mod_content_server +#' @description A shiny module. +#' @description A shiny module. +#' @param id,input,output,session Internal parameters for {shiny}. +#' +#' @noRd +#' @import shiny +#' @import shinydashboard +#' @keywords internal + +mod_content_ui <- function(id) { + ns <- NS(id) + tabItem( + tabName = "content", + fluidRow( + column( + width = 12, + h2('Step 2. Configure Content'), + p("Second, based on the consultation with partners and sectoral + experts, select the topics and indicators that needs to be covered + for your annual survey cycle ") + ) + ), + + fluidRow( + # Valid statuses are: primary, success, info, warning, danger. + column( + 6, + box(title = " 1. Apply filters ", + width = NULL, + collapsible = FALSE, + status = "warning", + " One indicator might be linked to mulitple topics and framewoks. + You can use the different filters below to narrow down the list indicators + to search in", + br(), + + + fluidRow( + # Valid statuses are: primary, success, info, warning, danger. + column( + 6, + box(title = " Filters on Topics", + width = NULL, + collapsible = FALSE, + status = "warning", + checkboxGroupInput(inputId = ns("topic"), + label = " Tick to filter", + choiceNames = list( + list( + icon("gavel") , + "Rights" + ), + list( + icon("home"), + "Trajectory" + ), + list( + icon("thermometer-full"), + "Intention" + ), + list( + icon("check-circle"), + "Basic Needs" + ), + list( + icon("certificate"), + "Coping Capacity" + ), + list( + icon("users"), + "Well-Being" + ), + list( + icon("blind"), + "Victimisation" + ), + list( + icon("usd"), + "Livelihood" + ), + list( + icon("book"), + "Education" + ), + list( + icon("medkit"), + "Health-Nutrition" + ), + list( + icon("eye"), + "Accountability" + ), + list( + icon("info-circle"), + "Information" + )), + choiceValues = list("Rights" , + "Trajectory" , + "Intention" , + "Basic Needs" , + "Coping Capacity" , + "Well-Being" , + "Victimisation" , + "Livelihood" , + "Education" , + "Health-Nutrition" , + "Accountability", + "Information" )) + ) + ), + column( + 6, + box(title = " Filters on Framework", + width = NULL, + status = "warning", + tags$div(title="Standard Indicators", + checkboxGroupInput(inputId = ns("Disaggregation"), + label = " Tick to filter ", + choices = list("UNHCR Compass / RBM"="RBM", + "UN Stat Commission / EGRISS"="EGRISS", + "Sustainable Development Goal / SDG"="SDG", + "Global Compact on Refugee / GCR"="GCR", + "Humanitarian Sphere Standards"="Sphere", + "Post Distribution & Assistance Monitoring"="PDM", + "Assistance Targeting / Excellence Hub"="targeting", + "Framework on Durable Solutions /IASC "="Durable", + "Protection Mainstreaming / ECHO "="Durable", + "Composite Indicator / Index"="Index" + ) ) ) + ) + ) + ) + + + + + + ) + ), + column( + 3, + box(title = " 2. Select Indicators", + width = NULL, + status = "warning", + "Note that one indicator might requires multiples questions + within the final survey to be calculated. + Do not worry the app will take care of that complex step", + tags$div(title="Standard Indicators", + checkboxGroupInput(inputId = ns("indicator"), + label = "Select indicators - to be turned to autocomplete-multiple selection..", + choices = list("Access to drinking water"="outcome12_1", + "Residing in physically safe and secure settlements with access to basic facilities"="impact2_2", + "Could access health facilities"="impact2_3", + "Feeling safe walking alone"="impact3_3", + "Children under 5 birth registered with civil authorities"="outcome1_2", + "Has legally recognized documents"="outcome1_3", + "Proportion of PoC who know where to access available GBV services"="outcome4_1", + "Do not accept violence against women"="outcome4_2", + "With primary reliance on clean (cooking) fuels and technology"="outcome8_2", + "Living in Habitable and affordable shelter"="outcome9_1", + "Access to energy to ensure lighting"="outcome9_2", + "With access to a safe household toilet"="outcome12_2", + "With a bank account or mobile-money service provider"="outcome13_1", + "Working age individuals who are unemployed"="outcome13_3", + "With secure tenure and/or property rights"="outcome16_1", + "Covered by social protection floors/systems"="outcome16_2", + "and so on..."="oo" + ) ) ) + ) + ), + column( + 3, + box(title = " 3. Context-specific Questions", + width = NULL, + status = "warning", + " You may here add context specific questions, that is to say questions for which + the equivalent statistical construct is not (yet) available within the global referential. + Thoses questions will be shared back with the global referential manager + for potential inclusion in the next release of the referential", + br() , + hr(), + actionButton(inputId = ns("start"), + label="Select / Define Ad-hoc Questions", + icon("gears"))) + ) + ) , + fluidRow( + # Valid statuses are: primary, success, info, warning, danger. + column( + 12, + box(title = " Indicator Plan -- Analysis", + width = NULL, + collapsible = FALSE, + status = "success", + " A small summary review will be automatically generated below. + it's not for data entry but rather to output a summary + of the decision points that have been set up above.. + For instance - indicating how many unique questions + would be required in order to compile all the indicators + that got selected..", + br(), + hr(), + actionButton(inputId = ns("start"), + label="Next Set up Collection", + icon("list-check")) + ) + )# End fluid row + + ) + ) +} + +#' Module Server +#' @noRd +#' @import shiny +#' @import tidyverse +#' @keywords internal + +mod_content_server <- function(input, output, session, AppReactiveValue) { + ns <- session$ns +## add here the server logic part of your module.... +} + +## copy to body.R +# mod_content_ui("content_ui_1") + +## copy to sidebar.R +# shinydashboard::menuItem("displayName",tabName = "content",icon = icon("user")) + +## and copy to app_server.R +# callModule(mod_content_server, "content_ui_1", AppReactiveValue) + diff --git a/R/mod_context.R b/R/mod_context.R new file mode 100644 index 0000000..b98ea83 --- /dev/null +++ b/R/mod_context.R @@ -0,0 +1,162 @@ +#' Module UI + +#' @title mod_context_ui and mod_context_server +#' @description A shiny module. +#' @description A shiny module. +#' @param id,input,output,session Internal parameters for {shiny}. +#' +#' @noRd +#' @import shiny +#' @import shinydashboard +#' @keywords internal + +mod_context_ui <- function(id) { + ns <- NS(id) + tabItem( + tabName = "context", + fluidRow( + column( + width = 12, + h2(' Step 1. Define Context'), + p("First, document the operational purpose of the data that you will need to collect for the incoming cycle. +This will be the first be the first filter of the design process ") + ) + ), + + fluidRow( + # Valid statuses are: primary, success, info, warning, danger. + column( + 3, + box(title = " 1. Metadata", + width = NULL, + collapsible = FALSE, + status = "warning", + " ", + textInput("caption", + label="Title for your data collection project", + value="short title"), + tags$div(title=" ", + selectInput(inputId = ns("context"), + label = "Year for your data collection", + choices = list("2025" = "2025", + "2024" = "2024", + "2023" = "2023" ), + selected = "2023") ), + textAreaInput(inputId = ns("caption"), + label="Provide an abstract for the data collection activities", + value="Who will collect? What sample frame can be used? What are the main field access constraints?", + width = "100%", + height = "300px") + ) + ) , + + column( + 3, + box(title = " 2. Methodology / Operational Purpose", + width = NULL, + collapsible = FALSE, + status = "warning", + " ", + tags$div(title="Interview", + radioButtons(inputId = ns("library"), + label = " What global referential to use?" , + choices = list("Household Survey (Ideally representative of stable population in a protracted situation)" = "household_survey", + "Flow Monitoring (People not aiming to establish their habitual residence in the place of interview)" = "flow_monitoring", + "Key Informant (Persons with local qualitative knowledge in an emergency situation)" = "key_informant"), + selected = "household_survey" ) ), + br(), + " Referentials are centrally managed by UNHCR Global Data Service. They are based on most appropriate international statistical standards (EGRISS, UN Stat Commissions, etc.)") + ), + column( + 3, + box(title = " 3. Country", + width = NULL, + status = "warning", + " ", + tags$div(title="Select your country ", + selectInput(inputId = ns("context"), + label = "Country, defining then required languages & geographic Pcodes", + choices = list("Panama" = "Panama", + "Colombia" = "Colombia", + "Ecuador" = "Ecuador" ), + selected = "Panama") ), + br(), + + tags$div(title="Geographic Coverage ", + selectInput(inputId = ns("context"), + label = "Are you aiming for a full national coverage?", + choices = list("yes" = "Yes", + "no" = "No" ), + selected = "yes") ), + + hr(), + "This will pull up the languages translation and geographic breakdown required for the country.", + " Global referentials are contextualised for each country through the regional survey support capacity in the bureau.", + "If your country does not appear in the that list, ignite a contextualisation request", + br(), + actionButton(inputId = ns("contextualisationRequest"), + label="Contextualisation", + icon("gears")) , + br()) + ), + column( + 3, + box(title = " 4. Targeted Groups", + width = NULL, + collapsible = FALSE, + status = "warning", + " ", + tags$div(title="Population Group to cover", + checkboxGroupInput(inputId = ns("population"), + label = "Target Group", + choices = list("Refugees (REF) & Asylum seekers (ASY)"="RAS", + "Internally displaced persons (IDP)"="IDP", + "Other people in need of international protection (OIP)"="OIP", + "Stateless Persons (STA)"="STA", + "Others of concern to UNHCR (OOC)"="OOC", + "Returnee (RET)"="RET", + "Host community (HCT)"="HCT" ), + + br() )) + ) + ) + ) , + fluidRow( + # Valid statuses are: primary, success, info, warning, danger. + column( + 12, + box(title = " Context Summary", + width = NULL, + collapsible = FALSE, + status = "success", + " ", + hr(), + actionButton(inputId = ns("start"), + label="Go to Configure Content", + icon("arrows-to-circle")) + ) + ) + )# End fluid row + ) +} + +#' Module Server +#' @noRd +#' @import shiny +#' @import tidyverse +#' @keywords internal + +mod_context_server <- function(input, output, session, AppReactiveValue) { + ns <- session$ns +## add here the server logic part of your module.... +} + +## copy to body.R +# mod_context_ui("context_ui_1") + +## copy to sidebar.R +# shinydashboard::menuItem("displayName",tabName = "context",icon = icon("user")) + +## and copy to app_server.R +# callModule(mod_context_server, "context_ui_1", AppReactiveValue) + diff --git a/R/mod_forms.R b/R/mod_forms.R new file mode 100644 index 0000000..7fd30fd --- /dev/null +++ b/R/mod_forms.R @@ -0,0 +1,131 @@ +#' Module UI + +#' @title mod_forms_ui and mod_forms_server +#' @description A shiny module. +#' @description A shiny module. +#' @param id,input,output,session Internal parameters for {shiny}. +#' +#' @noRd +#' @import shiny +#' @import shinydashboard +#' @keywords internal + +mod_forms_ui <- function(id) { + ns <- NS(id) + tabItem( + tabName = "forms", + fluidRow( + column( + width = 12, + h2('Step 4. Publish Surveys'), + p("Last, revise as pretty print word documents & export as xlsforms + and the single or multiples questionnaires that you will be + required to implement your annual survey cycle. + You maybe need to then revise some questions labels. + Once done, you will get all the files needed to collect the data + through kobotoolbox ") + ) + ), + + fluidRow( + column( + 4, + box(title = " 1. Survey Modules Sequence", + width = NULL, + status = "warning", + " You can adjust the default sequence between modules and + then between questions within modules.", + br(), + "Note that the design already comes with suggested sequence of modules. + For instance depending on the mode, sensitive or longer/more complicated + questions comes towards the end for CAPI while they are more at the begining for CATI. + Also not all sequences can be changed as the questionnaire logic comes with hard constraints + to be respected.", + br(), + actionButton(inputId = ns("changeseq"), + label="Change Sequence", + icon("gears")) ) + ), + column( + 4, + box(title = " 2. Preview Questionnaires", + width = NULL, + collapsible = FALSE, + status = "warning", + " Get a word version of the different questionnaires for a review with field colleagues", + br(), + actionButton(inputId = ns("pretty"), + label="Pretty Print", + icon("file-word")) + ) + ) , + column( + 4, + box(title = " 3. Labels Review", + width = NULL, + collapsible = FALSE, + status = "warning", + " Building on expert review, use this function to apply changes in the contextualisable content.... ", + br(), + actionButton(inputId = ns("revlabel"), + label="Labels", + icon("gears")) + ) + ) + ), + fluidRow( + column( + 6, + box(title = " Documentation", + width = NULL, + collapsible = FALSE, + status = "success", + "Download the documentation for your annual survey data collection cycle", + actionButton(inputId = ns("document"), + label="Annual Survey Cycle", + icon("boxes-packing")) + ) + ), + column( + 6, + box(title = " Publication", + width = NULL, + collapsible = FALSE, + status = "success", + "Publish all Surveys directly on the data collection server: ", + br(), + actionButton(inputId = ns("publishkobo"), + label="Publish on Kobo", + icon("paper-plane")), + br(), + " or download the corresponding xlsform files to upload them yourself on kobotoolbox: ", + br(), + actionButton(inputId = ns("getxlsform"), + label="XlsForm", + icon("file-excel")) + ) + ) + )# End fluid row + ) +} + +#' Module Server +#' @noRd +#' @import shiny +#' @import tidyverse +#' @keywords internal + +mod_forms_server <- function(input, output, session, AppReactiveValue) { + ns <- session$ns +## add here the server logic part of your module.... +} + +## copy to body.R +# mod_forms_ui("forms_ui_1") + +## copy to sidebar.R +# shinydashboard::menuItem("displayName",tabName = "forms",icon = icon("user")) + +## and copy to app_server.R +# callModule(mod_forms_server, "forms_ui_1", AppReactiveValue) + diff --git a/R/mod_home.R b/R/mod_home.R new file mode 100644 index 0000000..61f8605 --- /dev/null +++ b/R/mod_home.R @@ -0,0 +1,90 @@ +# Module UI Home + +#' @title mod_home_ui and mod_home_server +#' @description A shiny module. +#' @import shiny +#' @import shinydashboard +#' @noRd +#' @keywords internal + +mod_home_ui <- function(id) { + ns <- NS(id) + tabItem( + tabName = "home", + absolutePanel( ## refers to a css class + id = "splash_panel", top = 0, left = 0, right = 0, bottom = 0, + ### Get the name for your tool + p( + tags$span("Survey Designer ", style = "font-size: 80px"), + tags$span("alpha-version", style = "font-size: 34px") + ), + br(), + ### Then a short explainer + p( "As Local Survey Coordinator, integrating, configuring and tailoring + multiple surveys at once is often challenging! + Including all data points expected by indicators calculations, + contextualizing correctly questions labels, maximising + resources for smarter data collect over multiple waves and modes are not straightforward processes... ", + style = "font-size: 20px"), + br(), + p( "This app aims at addressing the complex challenges associated with the design of integrated surveys. It is expected to help:" , + tags$ol( + tags$li(" Standardizing a questionnaire design process that starts with a",tags$b(" selection of indicators ") ,"that needs + to be measured and thenpre-select automatically all the required standardized survey modules and + questions in order to measure them, aka 'collect only what you need and use everything that you collected'. + This will ensure that standards are applied, + but it will also minimize the risk of questionnaire design mistakes", + style = "font-size: 16px; text-align: left;" ), + tags$li(" Easing the",tags$b(" contextualization process ") ,"from the global referential to the actual implementation + in each country (i.e translating and adjusting the labels as per the specific context) and + facilitate a feedback loop so that if the same ad-hoc questions are used in multiple context, + they can be considered for inclusion in the global referential ", + style = "font-size: 16px; text-align: left;"), + tags$li(" Supporting the usage of mixed-mode (e.g.CAPI/CATI/CAWI) and multiple data collection waves + to collect the final dataset within an",tags$b(" annual survey data collection cycle") ,", all of + this allowing both to promote survey integration and to maximize the financial + resources invested in those activities", + style = "font-size: 16px; text-align: left;" ) + ) , + style = "font-size: 16px; text-align: left;"), + + p(tags$i( class = "fa fa-github"), + "App built with ", + tags$a(href="https://edouard-legoupil.github.io/graveler/", + "{graveler}" ), + " -- report ", + tags$a(href="https://github.com/unhcr-americas/surveyDesigner/issues/new/choose", + "issues here." , + ), + style = "font-size: 10px") + ) + ) +} + +# Module Server +#' @import shiny +#' @import shinydashboard +#' @noRd +#' @keywords internal + +mod_home_server <- function(input, output, session) { + ns <- session$ns + # This create the links for the button that allow to go to the next module + observeEvent(input$go_to_firstmod, { + shinydashboard::updateTabItems( + session = parent_session, + inputId = "tab_selected", + selected = "firstmod" + ) + }) +} + +## copy to body.R +# mod_home_ui("home_ui_1") + +## copy to app_server.R +# callModule(mod_home_server, "home_ui_1") + +## copy to sidebar.R +# menuItem("displayName",tabName = "home",icon = icon("user")) + diff --git a/R/mod_home_ui.R b/R/mod_home_ui.R deleted file mode 100644 index 7790fa1..0000000 --- a/R/mod_home_ui.R +++ /dev/null @@ -1,34 +0,0 @@ -# WARNING - Generated by {fusen} from /dev/flat_mod_home.Rmd: do not edit by hand - -#' home UI Function -#' -#' @description A shiny Module. -#' -#' @param id,input,output,session Internal parameters for {shiny}. -#' -#' @noRd -#' -#' @importFrom shiny NS tagList -mod_home_ui <- function(id){ - ns <- NS(id) - tagList( - - ) -} - -#' home Server Functions -#' -#' @noRd -mod_home_server <- function(id){ - moduleServer( id, function(input, output, session){ - ns <- session$ns - - }) -} - -## To be copied in the UI -# mod_home_ui("home_1") - -## To be copied in the server -# mod_home_server("home_1") - diff --git a/R/run_app.R b/R/run_app.R old mode 100644 new mode 100755 index 5d60ac1..85efbd0 --- a/R/run_app.R +++ b/R/run_app.R @@ -8,11 +8,11 @@ #' @importFrom shiny shinyApp #' @importFrom golem with_golem_options run_app <- function( - onStart = NULL, - options = list(), - enableBookmarking = NULL, - uiPattern = "/", - ... + onStart = NULL, + options = list(), + enableBookmarking = NULL, + uiPattern = "/", + ... ) { with_golem_options( app = shinyApp( diff --git a/R/sidebar.R b/R/sidebar.R new file mode 100755 index 0000000..61dcfa7 --- /dev/null +++ b/R/sidebar.R @@ -0,0 +1,23 @@ +#' UI Side menau +#' +#' This function is internally used to manage the side menu +#' +#' @import shiny +#' @import shinydashboard +#' @noRd +#' @keywords internal +#' +sidebar <- function() { + shinydashboard::dashboardSidebar( + shinydashboard::sidebarMenu( + ## Here the menu item entry to the first module + shinydashboard::menuItem("About",tabName = "home",icon = icon("bookmark")), + shinydashboard::menuItem(" Define Context", tabName = "context", icon = icon("location-dot")), + shinydashboard::menuItem(" Configure Content", tabName = "content", icon = icon("arrows-to-circle")), + shinydashboard::menuItem(" Set up Collection", tabName = "collection", icon = icon("list-check")), + shinydashboard::menuItem(" Publish Surveys", tabName = "forms", icon = icon("share-from-square")) + # - add more - separated by a comma! + ## For icon search on https://fontawesome.com/search?o=r&m=free - filter on free + ) + ) +} diff --git a/R/theme.R b/R/theme.R deleted file mode 100755 index ddb703e..0000000 --- a/R/theme.R +++ /dev/null @@ -1,110 +0,0 @@ -#' theme_dashboard -#' -#' -#' @return nothing -#' -#' @importFrom dashboardthemes shinyDashboardThemeDIY -#' -#' @noRd -theme_dashboard <- function() { - primary <- "#0072BC" - accent <- "#18375F" - secondary <- "#FAEB00" - - dashboardthemes::shinyDashboardThemeDIY( - - #general - appFontFamily = "Lato" - ,appFontColor = "#696969" - ,primaryFontColor = "#696969" - ,infoFontColor = "#696969" - ,successFontColor = "#696969" - ,warningFontColor = "#696969" - ,dangerFontColor = "#696969" - ,bodyBackColor = "#F8F8F8" - - #header - ,logoBackColor = primary - ,headerButtonBackColor = primary - ,headerButtonIconColor = "white" - ,headerButtonBackColorHover = accent - ,headerButtonIconColorHover = "white" - ,headerBackColor = primary - ,headerBoxShadowColor = "#aaaaaa" - ,headerBoxShadowSize = "0px 0px 0px" - - #sidebar - ,sidebarBackColor = "#EDEDED" - - ,sidebarPadding = 0 - - ,sidebarMenuBackColor = "transparent" - ,sidebarMenuPadding = 0 - ,sidebarMenuBorderRadius = 0 - - ,sidebarShadowRadius = "0px 0px 0px" - ,sidebarShadowColor = "#aaaaaa" - - ,sidebarUserTextColor = "#696969" - - ,sidebarSearchBackColor = "rgb(55,72,80)" - ,sidebarSearchIconColor = "rgb(153,153,153)" - ,sidebarSearchBorderColor = "rgb(55,72,80)" - - ,sidebarTabTextColor = "#696969" - ,sidebarTabTextSize = 13 - ,sidebarTabBorderStyle = "none none none solid" - ,sidebarTabBorderColor = "transparent" - ,sidebarTabBorderWidth = 5 - ,sidebarTabBackColorSelected = "transparent" - ,sidebarTabTextColorSelected = primary - ,sidebarTabRadiusSelected = "0px 0px 0px 0px" - ,sidebarTabBackColorHover = "#EDEDED" - ,sidebarTabTextColorHover = accent - ,sidebarTabBorderStyleHover = "none none none solid" - ,sidebarTabBorderColorHover = secondary - ,sidebarTabBorderWidthHover = 5 - ,sidebarTabRadiusHover = "0px 0px 0px 0px" - - ### boxes - ,boxBackColor = "white" - ,boxBorderRadius = 5 - ,boxShadowSize = "0px 0px 0px" - ,boxShadowColor = paste0(primary,"30") - ,boxTitleSize = 16 - ,boxDefaultColor = "white" - ,boxPrimaryColor = "white" - ,boxInfoColor = "rgb(210,214,220)" - ,boxSuccessColor = "rgba(0,255,213,1)" - ,boxWarningColor = "rgb(244,156,104)" - ,boxDangerColor = "rgb(255,88,55)" - - ,tabBoxTabColor = "white" - ,tabBoxTabTextSize = 12 - ,tabBoxTabTextColor = "#696969" - ,tabBoxTabTextColorSelected = primary - ,tabBoxBackColor = "white" - ,tabBoxHighlightColor = primary - ,tabBoxBorderRadius = 0 - - ### inputs - ,buttonBackColor = "rgb(245,245,245)" - ,buttonTextColor = "rgb(0,0,0)" - ,buttonBorderColor = "rgb(200,200,200)" - ,buttonBorderRadius = 5 - ,buttonBackColorHover = "rgb(235,235,235)" - ,buttonTextColorHover = "rgb(100,100,100)" - ,buttonBorderColorHover = "rgb(200,200,200)" - ,textboxBackColor = "rgb(255,255,255)" - ,textboxBorderColor = "rgb(200,200,200)" - ,textboxBorderRadius = 5 - ,textboxBackColorSelect = "rgb(245,245,245)" - ,textboxBorderColorSelect = "rgb(200,200,200)" - - ### tables - ,tableBackColor = NA - ,tableBorderColor = NA - ,tableBorderTopSize = 1 - ,tableBorderRowSize = 1 - ) -} #theme diff --git a/R/utils-pipe.R b/R/utils-pipe.R deleted file mode 100644 index fd0b1d1..0000000 --- a/R/utils-pipe.R +++ /dev/null @@ -1,14 +0,0 @@ -#' Pipe operator -#' -#' See \code{magrittr::\link[magrittr:pipe]{\%>\%}} for details. -#' -#' @name %>% -#' @rdname pipe -#' @keywords internal -#' @export -#' @importFrom magrittr %>% -#' @usage lhs \%>\% rhs -#' @param lhs A value or the magrittr placeholder. -#' @param rhs A function call using the magrittr semantics. -#' @return The result of calling `rhs(lhs)`. -NULL diff --git a/README.md b/README.md index 0434210..cf0904c 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,8 @@ Read more on the rationale for this app in the [vignette](https://unhcr-americas To run it locally, use ``` r -# install.packages("remotes") -remotes::install_github("unhcr-americas/surveyDesigner") +# install.packages("pak") +pak::pkg_install("unhcr-americas/surveyDesigner") # Run the application surveyDesigner::run_app() ``` diff --git a/dev/01_dev.R b/dev/01_dev.R new file mode 100755 index 0000000..ea45f82 --- /dev/null +++ b/dev/01_dev.R @@ -0,0 +1,63 @@ +####################################################### +## This file will guide you in the creation of your app +####################################################### + +## Install the required dev dependencies ---- +golem::install_dev_deps() + +## Make sure to update imported libraries in the package namespace +attachment::att_amend_desc() + +### Initial set up for Documentation ---------- + +## Connect to github to enable documentation and collaboratio +rstudioapi::navigateToFile("dev/githublink.R") +## Set up the README +usethis::use_readme_rmd(open = FALSE) +## Start documenting the project objectives in the readme file +rstudioapi::navigateToFile("README.Rmd") +## Now build it +devtools::build_readme() + +### Back office ---------- + +## Go to function_documentation.Rmd to build your back office functions with Fusen +rstudioapi::navigateToFile("dev/function_documentation.Rmd") +## Get your function and then build your package with +fusen::inflate(flat_file = "dev/function_documentation.Rmd", vignette_name = "Development") + +# Your goal is to to get : +# > 0 errors ✔ | 0 warnings ✔ | 0 notes ✔ + +## Set up a globals.R with +# remotes::install_github("thinkr-open/checkhelper") +# checkhelper::print_globals() + +## Build the html package documentation for your utilities functions +pkgdown::build_site() + + +### Start Building the app ---------- + +## Init Testing Infrastructure +golem::use_recommended_tests() +## Ensure the empty dashboard loads correctly +golem::run_dev() + +## Now start adding modules from console +# Modules are like the pipe between your back-office functions and your user +# Name of the module - "my_first_module" +# graveler::level_up(name = "my_first_module") +# graveler::level_up(name = "context") +# graveler::level_up(name = "content") +# graveler::level_up(name = "collection") +# graveler::level_up(name = "forms") + + +### Deploy the app ---------- + +## Add manifest for CI/CD +rsconnect::writeManifest() +## For deployment use the app.R file at the root of your project and then use +# the deployment button in blue to publish your project http://rstudio.unhcr.org + diff --git a/dev/01_start.R b/dev/01_start.R deleted file mode 100644 index c3da9b5..0000000 --- a/dev/01_start.R +++ /dev/null @@ -1,68 +0,0 @@ -# Building a Prod-Ready, Robust Shiny Application. -# -# README: each step of the dev files is optional, and you don't have to -# fill every dev scripts before getting started. -# 01_start.R should be filled at start. -# 02_dev.R should be used to keep track of your development during the project. -# 03_deploy.R should be used once you need to deploy your app. -# -# -######################################## -#### CURRENT FILE: ON START SCRIPT ##### -######################################## - -## Fill the DESCRIPTION ---- -## Add meta data about your application -## -## /!\ Note: if you want to change the name of your app during development, -## either re-run this function, call golem::set_golem_name(), or don't forget -## to change the name in the app_sys() function in app_config.R /!\ -## -golem::fill_desc( - pkg_name = "surveyDesigner", # The Name of the package containing the App - pkg_title = "PKG_TITLE", # The Title of the package containing the App - pkg_description = "PKG_DESC.", # The Description of the package containing the App - author_first_name = "AUTHOR_FIRST", # Your First Name - author_last_name = "AUTHOR_LAST", # Your Last Name - author_email = "AUTHOR@MAIL.COM", # Your Email - repo_url = NULL, # The URL of the GitHub Repo (optional), - pkg_version = "0.0.0.9000" # The Version of the package containing the App -) - -## Set {golem} options ---- -golem::set_golem_options() - -## Install the required dev dependencies ---- -golem::install_dev_deps() - -## Create Common Files ---- -## See ?usethis for more information -usethis::use_mit_license("Golem User") # You can set another license here -usethis::use_readme_rmd(open = FALSE) -devtools::build_readme() -# Note that `contact` is required since usethis version 2.1.5 -# If your {usethis} version is older, you can remove that param -usethis::use_code_of_conduct(contact = "Golem User") -usethis::use_lifecycle_badge("Experimental") -usethis::use_news_md(open = FALSE) - -## Use git ---- -usethis::use_git() - -## Init Testing Infrastructure ---- -## Create a template for tests -golem::use_recommended_tests() - -## Favicon ---- -# If you want to change the favicon (default is golem's one) -golem::use_favicon() # path = "path/to/ico". Can be an online file. -# golem::remove_favicon() # Uncomment to remove the default favicon - -## Add helper functions ---- -golem::use_utils_ui(with_test = TRUE) -golem::use_utils_server(with_test = TRUE) - -# You're now set! ---- - -# go to dev/02_dev.R -rstudioapi::navigateToFile("dev/02_dev.R") diff --git a/dev/02_dev.R b/dev/02_dev.R deleted file mode 100644 index 1c8c052..0000000 --- a/dev/02_dev.R +++ /dev/null @@ -1,96 +0,0 @@ -# Building a Prod-Ready, Robust Shiny Application. -# -# README: each step of the dev files is optional, and you don't have to -# fill every dev scripts before getting started. -# 01_start.R should be filled at start. -# 02_dev.R should be used to keep track of your development during the project. -# 03_deploy.R should be used once you need to deploy your app. -# -# -################################### -#### CURRENT FILE: DEV SCRIPT ##### -################################### - -# Engineering - -## Dependencies ---- -## Amend DESCRIPTION with dependencies read from package code parsing -## install.packages('attachment') # if needed. -attachment::att_amend_desc() - -## Add modules ---- -## Create a module infrastructure in R/ -golem::add_module(name = "name_of_module1", with_test = TRUE) # Name of the module -golem::add_module(name = "name_of_module2", with_test = TRUE) # Name of the module - -## Add helper functions ---- -## Creates fct_* and utils_* -golem::add_fct("helpers", with_test = TRUE) -golem::add_utils("helpers", with_test = TRUE) - -## External resources -## Creates .js and .css files at inst/app/www -golem::add_js_file("script") -golem::add_js_handler("handlers") -golem::add_css_file("custom") -golem::add_sass_file("custom") - -## Add internal datasets ---- -## If you have data in your package -usethis::use_data_raw(name = "my_dataset", open = FALSE) - -## Tests ---- -## Add one line by test you want to create -usethis::use_test("app") - -# Documentation - -## Vignette ---- -usethis::use_vignette("surveyDesignergolem") -devtools::build_vignettes() - -## Code Coverage---- -## Set the code coverage service ("codecov" or "coveralls") -usethis::use_coverage() - -# Create a summary readme for the testthat subdirectory -covrpage::covrpage() - -## CI ---- -## Use this part of the script if you need to set up a CI -## service for your application -## -## (You'll need GitHub there) -usethis::use_github() - -# GitHub Actions -usethis::use_github_action() -# Chose one of the three -# See https://usethis.r-lib.org/reference/use_github_action.html -usethis::use_github_action_check_release() -usethis::use_github_action_check_standard() -usethis::use_github_action_check_full() -# Add action for PR -usethis::use_github_action_pr_commands() - -# Travis CI -usethis::use_travis() -usethis::use_travis_badge() - -# AppVeyor -usethis::use_appveyor() -usethis::use_appveyor_badge() - -# Circle CI -usethis::use_circleci() -usethis::use_circleci_badge() - -# Jenkins -usethis::use_jenkins() - -# GitLab CI -usethis::use_gitlab_ci() - -# You're now set! ---- -# go to dev/03_deploy.R -rstudioapi::navigateToFile("dev/03_deploy.R") diff --git a/dev/03_deploy.R b/dev/03_deploy.R deleted file mode 100644 index 8ac58c6..0000000 --- a/dev/03_deploy.R +++ /dev/null @@ -1,62 +0,0 @@ -# Building a Prod-Ready, Robust Shiny Application. -# -# README: each step of the dev files is optional, and you don't have to -# fill every dev scripts before getting started. -# 01_start.R should be filled at start. -# 02_dev.R should be used to keep track of your development during the project. -# 03_deploy.R should be used once you need to deploy your app. -# -# -###################################### -#### CURRENT FILE: DEPLOY SCRIPT ##### -###################################### - -# Test your app - -## Run checks ---- -## Check the package before sending to prod -devtools::check() -rhub::check_for_cran() - -# Deploy - -## Local, CRAN or Package Manager ---- -## This will build a tar.gz that can be installed locally, -## sent to CRAN, or to a package manager -devtools::build() - -## RStudio ---- -## If you want to deploy on RStudio related platforms -golem::add_rstudioconnect_file() -#golem::add_shinyappsio_file() -#golem::add_shinyserver_file() - -## Docker ---- -## If you want to deploy via a generic Dockerfile -golem::add_dockerfile_with_renv() - -## If you want to deploy to ShinyProxy -#golem::add_dockerfile_with_renv_shinyproxy() - - -# Deploy to Posit Connect or ShinyApps.io -# In command line. -rsconnect::deployApp( - appName = desc::desc_get_field("Package"), - appTitle = desc::desc_get_field("Package"), - appFiles = c( - # Add any additional files unique to your app here. - "R/", - "inst/", - "data/", - "NAMESPACE", - "DESCRIPTION", - "app.R" - ), - appId = rsconnect::deployments(".")$appID, - lint = FALSE, - forceUpdate = TRUE -) - -library(golem) -add_rstudioconnect_file(pkg = get_golem_wd(), open = TRUE) diff --git a/dev/app_ui_initial_wireframe.R b/dev/app_ui_initial_wireframe.R new file mode 100644 index 0000000..e0dbdac --- /dev/null +++ b/dev/app_ui_initial_wireframe.R @@ -0,0 +1,691 @@ +#' The application User-Interface +#' +#' @param request Internal parameter for `{shiny}`. +#' DO NOT REMOVE. +#' @import shiny +#' @import shinydashboard +#' +#' @return Front end of app +#' +#' @noRd +app_ui <- function(request) { + + + tagList( + + + # enable alert messages + #shinyWidgets::useSweetAlert(), + + # Leave this function for adding external resources + golem_add_external_resources(), + theme_dashboard(), + # List the first level UI elements here + dashboardPage( + dashboardHeader(title = "Survey Designer"), + dashboardSidebar( + #minified = FALSE, + #collapsed = FALSE, + #width = "10vw", + sidebarMenu( + menuItem(" Introduction", tabName = "Intro", icon = icon("circle-info")), + menuItem(" Step 1. Define Context", tabName = "Context", icon = icon("location-dot")), + menuItem(" Step 2. Configure Content", tabName = "Content", icon = icon("arrows-to-circle")), + menuItem(" Step 3. Set up Collection", tabName = "Collection", icon = icon("list-check")), + menuItem(" Step 4. Publish Surveys", tabName = "Forms", icon = icon("share-from-square")), + menuItem(" -- Save or Load Session", icon = icon("floppy-disk"), + menuSubItem("Save session Unique ID", tabName = "subitem1", icon = icon("angles-right")), + menuSubItem("Load session using unique ID", tabName = "subitem2", icon = icon("angles-left"))) + ) + ), + + dashboardBody( + tabItems( + tabItem( + tabName = "Intro", + fluidRow( + "--WARNING - you are now viewing the prototype wireframe - the application is not yet functional--", + tags$a(href="https://github.com/unhcr-americas/surveyDesigner", "Please check the github repo for more info and to provide feedback!"), + + box(title = "", + width = NULL, + collapsible = FALSE, + status = "info", + + + tags$div(class = "jumbotron text-left", + style = "margin-bottom:5px;margin-top:5px;margin-left:5px", + tags$h1(style = 'color:#0072BC;', + style = 'margin-bottom:0px;margin-top:0px', + 'As Local Survey Coordinator, integrating, configuring and tailoring multiple surveys at once + is often challenging!'), + tags$i(class = 'jumbotron-heading', + style = 'margin-left:25px', + 'How can you confirm that you will not miss a specific data point to calculate your final indicators and disaggregation?... + How can you make sure to enforce all existing statistical standards and good practices while also contextualizing correctly questions labels?... + How can you maximise your resources to collect your data smarty, through multiple waves and modes?... ' ) + ), ## end jumbotron.. + "This app aims at addressing the complex challenges associated with the design of integrated surveys. It is expected to help:" , + tags$ol( + tags$li(" Standardizing a questionnaire design process that starts with a",tags$b(" selection of indicators ") ,"that needs + to be measured and thenpre-select automatically all the required standardized survey modules and + questions in order to measure them, aka 'collect only what you need and use everything that you collected'. + This will ensure that standards are applied, + but it will also minimize the risk of questionnaire design mistakes" ), + tags$li(" Easing the",tags$b(" contextualization process ") ,"from the global referential to the actual implementation + in each country (i.e translating and adjusting the labels as per the specific context) and + facilitate a feedback loop so that if the same ad-hoc questions are used in multiple context, + they can be considered for inclusion in the global referential "), + tags$li(" Supporting the usage of mixed-mode (e.g.CAPI/CATI/CAWI) and multiple data collection waves + to collect the final dataset within an",tags$b(" annual survey data collection cycle") ,", all of + this allowing both to promote survey integration and to maximize the financial + resources invested in those activities" ) + ) + #br(), + ) + ), + fluidRow( + # Valid statuses are: primary, success, info, warning, danger. + column( + 3, + box(title = " Step 1. Define Context", + width = NULL, + collapsible = FALSE, + status = "warning", + "First, document the operational purpose of the data that you will need to collect for the incoming cycle.", + br(), + "This will be the first be the first filter of the design process", + br(), + actionButton(inputId="start", + label="Start now!", + icon("location-dot") ) + ) + ), + column( + 3, + box(title = " Step 2. Configure Content", + width = NULL, + status = "warning", + "Second, based on the consultation with partners and sectoral experts, select the topics and indicators + that needs to be covered for your annual survey cycle", + br() ) + ), + column( + 3, + box(title = " Step 3. Set up Collection", + width = NULL, + collapsible = FALSE, + status = "warning", + "Third, provides information about your data collection capacity, what kind + of mode can you use, how many waves can you afford, what enumeration capacity can you afford...", + br() ) + ), + column( + 3, + box(title = " Step 4. Publish Surveys", + width = NULL, + collapsible = FALSE, + status = "warning", + "Last, revise as pretty print word documents & export as xlsforms and the single or multiples + questionnaires that you will be required to implement your annual survey cycle. + You maybe need to then revise some questions labels. + Once done, you will get all the files needed to collect the data through kobotoolbox ") + ) + ) # End fluid row + ), + # Context ##### + tabItem(tabName = "Context", + fluidRow( + # Valid statuses are: primary, success, info, warning, danger. + column( + 3, + box(title = " 1. Metadata", + width = NULL, + collapsible = FALSE, + status = "warning", + " ", + textInput("caption", + label="Title for your data collection project", + value="short title"), + tags$div(title=" ", + selectInput("context", + label = "Year for your data collection", + choices = list("2025" = "2025", + "2024" = "2024", + "2023" = "2023" ), + selected = "2023") ), + textAreaInput("caption", + label="Provide an abstract for the data collection activities", + value="Who will collect? What sample frame can be used? What are the main field access constraints?", + width = "100%", + height = "300px") + ) + ) , + + column( + 3, + box(title = " 2. Methodology / Operational Purpose", + width = NULL, + collapsible = FALSE, + status = "warning", + " ", + tags$div(title="Interview", + radioButtons("library", + label = " What global referential to use?" , + choices = list("Household Survey (Ideally representative of stable population in a protracted situation)" = "household_survey", + "Flow Monitoring (People not aiming to establish their habitual residence in the place of interview)" = "flow_monitoring", + "Key Informant (Persons with local qualitative knowledge in an emergency situation)" = "key_informant"), + selected = "household_survey" ) ), + br(), + " Referentials are centrally managed by UNHCR Global Data Service. They are based on most appropriate international statistical standards (EGRISS, UN Stat Commissions, etc.)") + ), + column( + 3, + box(title = " 3. Country", + width = NULL, + status = "warning", + " ", + tags$div(title="Select your country ", + selectInput("context", + label = "Country, defining then required languages & geographic Pcodes", + choices = list("Panama" = "Panama", + "Colombia" = "Colombia", + "Ecuador" = "Ecuador" ), + selected = "Panama") ), + br(), + + tags$div(title="Geographic Coverage ", + selectInput("context", + label = "Are you aiming for a full national coverage?", + choices = list("yes" = "Yes", + "no" = "No" ), + selected = "yes") ), + + hr(), + "This will pull up the languages translation and geographic breakdown required for the country.", + " Global referentials are contextualised for each country through the regional survey support capacity in the bureau.", + "If your country does not appear in the that list, ignite a contextualisation request", + br(), + actionButton(inputId="contextualisationRequest", + label="Contextualisation", + icon("gears")) , + br()) + ), + column( + 3, + box(title = " 4. Targeted Groups", + width = NULL, + collapsible = FALSE, + status = "warning", + " ", + tags$div(title="Population Group to cover", + checkboxGroupInput("population", + label = "Target Group", + choices = list("Refugees (REF) & Asylum seekers (ASY)"="RAS", + "Internally displaced persons (IDP)"="IDP", + "Other people in need of international protection (OIP)"="OIP", + "Stateless Persons (STA)"="STA", + "Others of concern to UNHCR (OOC)"="OOC", + "Returnee (RET)"="RET", + "Host community (HCT)"="HCT" ), + + br() )) + ) + ) + ) , + fluidRow( + # Valid statuses are: primary, success, info, warning, danger. + column( + 12, + box(title = " Context Summary", + width = NULL, + collapsible = FALSE, + status = "success", + " ", + hr(), + actionButton(inputId="start", + label="Go to Configure Content", + icon("arrows-to-circle")) + ) + ) + )# End fluid row + ), ## end context tiem + + # Content ##### + tabItem(tabName = "Content", + + + fluidRow( + # Valid statuses are: primary, success, info, warning, danger. + column( + 6, + box(title = " 1. Apply filters ", + width = NULL, + collapsible = FALSE, + status = "warning", + " One indicator might be linked to mulitple topics and framewoks. + You can use the different filters below to narrow down the list indicators + to search in", + br(), + + + fluidRow( + # Valid statuses are: primary, success, info, warning, danger. + column( + 6, + box(title = " Filters on Topics", + width = NULL, + collapsible = FALSE, + status = "warning", + checkboxGroupInput("topic", + label = " Tick to filter", + choiceNames = list( + list( + icon("gavel") , + "Rights" + ), + list( + icon("home"), + "Trajectory" + ), + list( + icon("thermometer-full"), + "Intention" + ), + list( + icon("check-circle"), + "Basic Needs" + ), + list( + icon("certificate"), + "Coping Capacity" + ), + list( + icon("users"), + "Well-Being" + ), + list( + icon("blind"), + "Victimisation" + ), + list( + icon("usd"), + "Livelihood" + ), + list( + icon("book"), + "Education" + ), + list( + icon("medkit"), + "Health-Nutrition" + ), + list( + icon("eye"), + "Accountability" + ), + list( + icon("info-circle"), + "Information" + )), + choiceValues = list("Rights" , + "Trajectory" , + "Intention" , + "Basic Needs" , + "Coping Capacity" , + "Well-Being" , + "Victimisation" , + "Livelihood" , + "Education" , + "Health-Nutrition" , + "Accountability", + "Information" )) + ) + ), + column( + 6, + box(title = " Filters on Framework", + width = NULL, + status = "warning", + tags$div(title="Standard Indicators", + checkboxGroupInput("Disaggregation", + label = " Tick to filter ", + choices = list("UNHCR Compass / RBM"="RBM", + "UN Stat Commission / EGRISS"="EGRISS", + "Sustainable Development Goal / SDG"="SDG", + "Global Compact on Refugee / GCR"="GCR", + "Humanitarian Sphere Standards"="Sphere", + "Post Distribution & Assistance Monitoring"="PDM", + "Assistance Targeting / Excellence Hub"="targeting", + "Framework on Durable Solutions /IASC "="Durable", + "Protection Mainstreaming / ECHO "="Durable", + "Composite Indicator / Index"="Index" + ) ) ) + ) + ) + ) + + + + + + ) + ), + column( + 3, + box(title = " 2. Select Indicators", + width = NULL, + status = "warning", + "Note that one indicator might requires multiples questions + within the final survey to be calculated. + Do not worry the app will take care of that complex step", + tags$div(title="Standard Indicators", + checkboxGroupInput("indicator", + label = "Select indicators - to be turned to autocomplete-multiple selection..", + choices = list("Access to drinking water"="outcome12_1", + "Residing in physically safe and secure settlements with access to basic facilities"="impact2_2", + "Could access health facilities"="impact2_3", + "Feeling safe walking alone"="impact3_3", + "Children under 5 birth registered with civil authorities"="outcome1_2", + "Has legally recognized documents"="outcome1_3", + "Proportion of PoC who know where to access available GBV services"="outcome4_1", + "Do not accept violence against women"="outcome4_2", + "With primary reliance on clean (cooking) fuels and technology"="outcome8_2", + "Living in Habitable and affordable shelter"="outcome9_1", + "Access to energy to ensure lighting"="outcome9_2", + "With access to a safe household toilet"="outcome12_2", + "With a bank account or mobile-money service provider"="outcome13_1", + "Working age individuals who are unemployed"="outcome13_3", + "With secure tenure and/or property rights"="outcome16_1", + "Covered by social protection floors/systems"="outcome16_2", + "and so on..."="oo" + ) ) ) + ) + ), + column( + 3, + box(title = " 3. Context-specific Questions", + width = NULL, + status = "warning", + " You may here add context specific questions, that is to say questions for which + the equivalent statistical construct is not (yet) available within the global referential. + Thoses questions will be shared back with the global referential manager + for potential inclusion in the next release of the referential", + br() , + hr(), + actionButton(inputId="start", + label="Select / Define Ad-hoc Questions", + icon("gears"))) + ) + ) , + fluidRow( + # Valid statuses are: primary, success, info, warning, danger. + column( + 12, + box(title = " Indicator Plan -- Analysis", + width = NULL, + collapsible = FALSE, + status = "success", + " A small summary review will be automatically generated below. + it's not for data entry but rather to output a summary + of the decision points that have been set up above.. + For instance - indicating how many unique questions + would be required in order to compile all the indicators + that got selected..", + br(), + hr(), + actionButton(inputId="start", + label="Next Set up Collection", + icon("list-check")) + ) + ) + )# End fluid row + ), # end content item... + + # Collection #### + tabItem(tabName = "Collection" , + fluidRow( + # Valid statuses are: primary, success, info, warning, danger. + column( + 4, + box(title = " 1. Modes", + width = NULL, + collapsible = FALSE, + status = "warning", + "Using multiple data collection modes can help minimizing + non-reponse and increase coverage. ", + tags$a(href="https://www.youtube.com/watch?v=qRmy2OAnyWc", "Learn more on mixed-mode here"), + br(), + checkboxGroupInput("mode", + label = "What Data Collection Modes do you have capacity to implement for this annual cycle?" , + choiceNames = list( + list( + icon("clipboard-question"), + "CAPI"="CAPI, i.e. Face-to-face " + ), + list( + icon("phone-square") , + "CATI"="CATI, i.e. Phone-Interview " + ), + list( + icon("wifi"), + "CAWI"= "CAWI, i.e. Self-administered " + )), + choiceValues = list("CAPI" , + "CATI" , + "CAWI" )), + br() ) + ), + column( + 4, + box(title = " 2. Waves", + width = NULL, + status = "warning", + " Define how many data collection waves you can manage within the year.", + + "Using multiple data collection waves can provide an opportunity for + more indicators to be collected. ", + tags$a(href="https://openknowledge.worldbank.org/server/api/core/bitstreams/e80ce277-f8d0-58aa-87b7-288c2895e87a/content#page=4", "Learn more on multi-waves here"), + + br(), + selectInput("wave", + label = "How many data collection waves for this annual cycle?", + choices = list("One Wave" = "One-wave", + "Two Waves" = "Two-waves", + "Three Waves" = "Three-waves", + "Four Waves" = "Four-waves" ), + selected = "One-waves"), + "As a result of this stage, below is an estimation of Interview lenght per questionnaire", + br() ) + ), + column( + 4, + box(title = " 3. Enumeration Capacity", + width = NULL, + collapsible = FALSE, + status = "warning", + # tags$div(title="Budget", + # numericInput("budget in $", + # label = "Available Annual Budget for Data Collection (in US$)", + # 30000, + # min = 5000, + # max = 500000) ), + "The Maximum monthly data collection depends on the capacity of the + organisation that partners or on the company that got contracted for that activity", + + hr(), + tags$div(title="Face1", + numericInput("face", + label = "Total Cost per Interview (in US$) for Face to face", + 50, min = 20, max = 300) ), + tags$div(title="Face2", + numericInput("face", + label = "Max Capacity of monthly working day for Face to face enumerators", + 50, min = 1, max = 100) ), + hr(), + tags$div(title="Face1", + numericInput("face", + label = "Total Cost per Interview (in US$) for Telephone", + 20, min = 5, max = 100) ), + + tags$div(title="Call Center enumerators", + numericInput("face", + label = "Max Capacity of monthly working day for Call Center enumerators", + 20, min = 1, max = 100) ), + br() ) + ) + ) , + fluidRow( + # Valid statuses are: primary, success, info, warning, danger. + column( + 12, + box(title = " Data Collection Plan -- Analysis", + width = NULL, + collapsible = FALSE, + status = "success", + "Feedback on your data collection analysis plan based on + estimated interview duration, budget and enumeration capacity + will be automatically generated below. + indication of the suggested length of the interview given the mode: + CAPI, 60-90 min; CATI; 30-45 min…and that dictates the number + of questions that are able to be included. + function for that https://edouard-legoupil.github.io/XlsFormPrettyPrint/reference/interview_duration.html", + hr(), + actionButton(inputId="start", + label="Now Publish Survey", + icon("share-from-square") + ) + )) + )# End fluid row + ), # end collection item + # Forms #### + tabItem(tabName = "Forms", + fluidRow( + # Valid statuses are: primary, success, info, warning, danger. + # column( + # 3, + # box(title = " 1. Survey Introduction Scripts", + # width = NULL, + # collapsible = FALSE, + # status = "warning", + # " This is the text that enumerator shold use to introduce the interview. + # It is important for it to be context sensitive... ", + # textAreaInput("caption", + # label="Add the script", + # value=" Hello, my name is .. and we would like to interview you for.... + # Data will be used by.. You will be able to see aggregated results here...", + # width = "100%"), + # br() ) + # ), + column( + 4, + box(title = " 1. Survey Modules Sequence", + width = NULL, + status = "warning", + " You can adjust the default sequence between modules and + then between questions within modules.", + br(), + "Note that the design already comes with suggested sequence of modules. + For instance depending on the mode, sensitive or longer/more complicated + questions comes towards the end for CAPI while they are more at the begining for CATI. + Also not all sequences can be changed as the questionnaire logic comes with hard constraints + to be respected.", + br(), + actionButton(inputId="start", + label="Change Sequence", + icon("gears")) ) + ), + column( + 4, + box(title = " 2. Preview Questionnaires", + width = NULL, + collapsible = FALSE, + status = "warning", + " Get a word version of the different questionnaires for a review with field colleagues", + br(), + actionButton(inputId="start", + label="Pretty Print", + icon("file-word")) + ) + ) , + column( + 4, + box(title = " 3. Labels Review", + width = NULL, + collapsible = FALSE, + status = "warning", + " Building on expert review, use this function to apply changes in the contextualisable content.... ", + br(), + actionButton(inputId="start", + label="Labels", + icon("gears")) + ) + ) + ), + fluidRow( + column( + 6, + box(title = " Documentation", + width = NULL, + collapsible = FALSE, + status = "success", + "Download the documentation for your annual survey data collection cycle", + actionButton(inputId="start", + label="Annual Survey Cycle", + icon("boxes-packing")) + ) + ), + column( + 6, + box(title = " Publication", + width = NULL, + collapsible = FALSE, + status = "success", + "Publish all Surveys directly on the data collection server: ", + br(), + actionButton(inputId="start", + label="Publish on Kobo", + icon("paper-plane")), + br(), + " or download the corresponding xlsform files to upload them yourself on kobotoolbox: ", + br(), + actionButton(inputId="start", + label="XlsForm", + icon("file-excel")) + ) + ) + )# End fluid row + ) # end publish item + ) #end all items + ) # end body + ) # end page + ) # end tag +} + +#' Add external Resources to the Application +#' +#' This function is internally used to add external +#' resources inside the Shiny application. +#' +#' @import shiny +#' @importFrom golem add_resource_path activate_js favicon bundle_resources +#' @noRd +golem_add_external_resources <- function() { + add_resource_path( + "www", + app_sys("app/www") + ) + + tags$head( + golem::activate_js(), + golem::favicon(), + bundle_resources( + path = app_sys("app/www"), + app_title = "surveyDesigner" + ) + # Add here other external resources + # If you have a custom.css in the inst/app/www + # Or for example, you can add shinyalert::useShinyalert() here + #tags$link(rel="stylesheet", type="text/css", href="www/custom.css") + ) +} diff --git a/dev/flat_mod_home.Rmd b/dev/flat_mod_home.Rmd deleted file mode 100644 index 58e246f..0000000 --- a/dev/flat_mod_home.Rmd +++ /dev/null @@ -1,70 +0,0 @@ ---- -title: "flat_mod_home.Rmd empty" -output: html_document -editor_options: - chunk_output_type: console ---- - -```{r development, include=FALSE} -library(testthat) -``` - -```{r development-load} -# Load already included functions if relevant -pkgload::load_all(export_all = FALSE) -``` - -# mod_home - -```{r function-mod_home} -#' home UI Function -#' -#' @description A shiny Module. -#' -#' @param id,input,output,session Internal parameters for {shiny}. -#' -#' @noRd -#' -#' @importFrom shiny NS tagList -mod_home_ui <- function(id){ - ns <- NS(id) - tagList( - - ) -} - -#' home Server Functions -#' -#' @noRd -mod_home_server <- function(id){ - moduleServer( id, function(input, output, session){ - ns <- session$ns - - }) -} - -## To be copied in the UI -# mod_home_ui("home_1") - -## To be copied in the server -# mod_home_server("home_1") - -``` - -```{r examples-mod_home} - -``` - -```{r tests-mod_home} -test_that("mod_home works", { - -}) -``` - - -```{r development-inflate, eval=FALSE} -# Run but keep eval=FALSE to avoid infinite loop -# Execute in the console directly -fusen::inflate(flat_file = "dev/flat_mod_home.Rmd", vignette_name = "Mon module home") -``` - diff --git a/dev/note b/dev/note new file mode 100644 index 0000000..315e901 --- /dev/null +++ b/dev/note @@ -0,0 +1,79 @@ +1. Start with metadata + +2. Beneficiry monitoring - + +4. What groups to you need to cover -- + +People who received assistance + +People who are registered with uNHCR + + + +This will inject the required identification modules.. + +Indicator Family - + +RBM +EGRISS +SDG +GCR + +Remove disagreggation! + + + +Remove budget - >> Budget is an output... + +Simplex -- > Methode... Optimum sous contraintes... + + +Flow moniotoring... -- is both key informant and directly key informant... + +Add indicators -- "Indicators Digester" --> + + +--- + + +-- Class context + +-- - initialiase -- +-- + +lire les deux fichiers... + +- on sait comment les joindre + +-- filtre les indicateurs - qui permettent de filter les questions - et on rapatries les context par la jointure + + +--- questionnaire s'initiliase par la jointure du context et du referentiel... + +L'objectif de + + +-- Referntial gere des checks... -- + +-- Context gere des checks... + +-- questionnaire es tl 'optimisation de la collecte...--- en sortie des setting d'optimisation.... + +-- Metdata initilialisation du questionnaire.... + + +Simplex -- Ressource = temps + +Qu'optimiser -- rapport temps(argent) / nombre d'indicateurs / accuracy - f(taille echantiloon) + + + + +referentiel <- Referential$new(system.file("SurveyDesigner_Referential.xlsx", package = "surveyDesigner") ) context <- Context$new(system.file(paste0(country, "_context.xlsx"), package = "surveyDesigner") ) questionnaire <- Questionnaire$new( indicateurs = indic, referentiel = referentiel, contexte = contexte ) # referential %>% full_join(context) %>% fitler(indicateurs) questionaires$optimize( budget = 100 temps= 2 mode= "ftf" waves= 4, person = 3 ) questionnaire$optimized <- list( data.frame en xlsForm ) questionnaire$my_optimize <- questionnaire$optimized [ wave = 1, ] + + + +-- Optimisation -- Disponibilite person-- +-- Sequence indicateur -- + +-- diff --git a/dev/run_dev.R b/dev/run_dev.R old mode 100644 new mode 100755 index 08030f4..6d53d5a --- a/dev/run_dev.R +++ b/dev/run_dev.R @@ -1,15 +1,13 @@ # Set options here options(golem.app.prod = FALSE) # TRUE = production mode, FALSE = development mode -# Comment this if you don't want the app to be served on a random port -options(shiny.port = httpuv::randomPort()) - # Detach all loaded packages and clean your environment golem::detach_all_attached() -# rm(list=ls(all.names = TRUE)) +rm(list = ls(all.names = TRUE)) # Document and reload your package golem::document_and_reload() # Run the application run_app() + diff --git a/docs/articles/mon-module-home.html b/docs/articles/mon-module-home.html new file mode 100644 index 0000000..a9004bd --- /dev/null +++ b/docs/articles/mon-module-home.html @@ -0,0 +1,128 @@ + + + + + + + +Mon module home • surveyDesigner + + + + + + + + + + + + +
+
+ + + + +
+
+ + + + +
+library(surveyDesigner)
+ +
+

mod_home +

+
+
+ + + +
+ + + +
+ +
+

+

Site built with pkgdown 2.0.7.

+
+ +
+
+ + + + + + + + diff --git a/man/pipe.Rd b/man/pipe.Rd deleted file mode 100644 index 1f8f237..0000000 --- a/man/pipe.Rd +++ /dev/null @@ -1,20 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/utils-pipe.R -\name{\%>\%} -\alias{\%>\%} -\title{Pipe operator} -\usage{ -lhs \%>\% rhs -} -\arguments{ -\item{lhs}{A value or the magrittr placeholder.} - -\item{rhs}{A function call using the magrittr semantics.} -} -\value{ -The result of calling `rhs(lhs)`. -} -\description{ -See \code{magrittr::\link[magrittr:pipe]{\%>\%}} for details. -} -\keyword{internal} diff --git a/man/run_app.Rd b/man/run_app.Rd old mode 100644 new mode 100755 index c6c36f7..be32ab4 --- a/man/run_app.Rd +++ b/man/run_app.Rd @@ -4,37 +4,7 @@ \alias{run_app} \title{Run the Shiny Application} \usage{ -run_app( - onStart = NULL, - options = list(), - enableBookmarking = NULL, - uiPattern = "/", - ... -) -} -\arguments{ -\item{onStart}{A function that will be called before the app is actually run. -This is only needed for \code{shinyAppObj}, since in the \code{shinyAppDir} -case, a \code{global.R} file can be used for this purpose.} - -\item{options}{Named options that should be passed to the \code{runApp} call -(these can be any of the following: "port", "launch.browser", "host", "quiet", -"display.mode" and "test.mode"). You can also specify \code{width} and -\code{height} parameters which provide a hint to the embedding environment -about the ideal height/width for the app.} - -\item{enableBookmarking}{Can be one of \code{"url"}, \code{"server"}, or -\code{"disable"}. The default value, \code{NULL}, will respect the setting from -any previous calls to \code{\link[shiny:enableBookmarking]{enableBookmarking()}}. See \code{\link[shiny:enableBookmarking]{enableBookmarking()}} -for more information on bookmarking your app.} - -\item{uiPattern}{A regular expression that will be applied to each \code{GET} -request to determine whether the \code{ui} should be used to handle the -request. Note that the entire request path must match the regular -expression in order for the match to be considered successful.} - -\item{...}{arguments to pass to golem_opts. -See `?golem::get_golem_options` for more details.} +run_app(...) } \description{ Run the Shiny Application From 82d9aea11cf8b539d72f4f5dd9376a8eba4c4e14 Mon Sep 17 00:00:00 2001 From: Edouard Date: Mon, 25 Sep 2023 21:47:48 -0500 Subject: [PATCH 2/3] rev pkg --- DESCRIPTION | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 6a86596..974a913 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,29 +1,32 @@ Package: surveyDesigner -Title: An Amazing Shiny App +Title: A Shiny App to help in the survey integration process Version: 0.0.0.9000 Authors@R: c( person("Edouard", "Legoupil", , "legoupil@unhcr.org", role = c("aut", "cre")), person("Cervan", "Girard", , "cervan@thinkr.fr", role = "aut") ) -Description: What the package does (one paragraph). +Description: The package offers a series of utilities to help field practionners + designing their annual survey cycle. License: MIT + file LICENSE Imports: config (>= 0.3.1), - dplyr, - dashboardthemes, + dplyr, shinydashboard, golem (>= 0.4.0), magrittr, purrr, R6, readxl, - shiny (>= 1.7.3) + shiny (>= 1.7.3), + unhcrshiny Suggests: knitr, prettydoc, rmarkdown, spelling, testthat (>= 3.0.0) +Remotes: + edouard-legoupil/unhcrshiny VignetteBuilder: knitr Config/testthat/edition: 3 From b86a400ae7fae55c31e7d8a11b76e1942b3f1718 Mon Sep 17 00:00:00 2001 From: Edouard Date: Mon, 25 Sep 2023 21:54:21 -0500 Subject: [PATCH 3/3] rev --- dev/lead_dev_create_weekly.Rmd | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dev/lead_dev_create_weekly.Rmd b/dev/lead_dev_create_weekly.Rmd index e9455d2..12014d8 100644 --- a/dev/lead_dev_create_weekly.Rmd +++ b/dev/lead_dev_create_weekly.Rmd @@ -16,6 +16,10 @@ knitr::opts_chunk$set( ``` ```{r setup} +install.packages( + "lozen", + repos = c("https://thinkr-open.r-universe.dev", "https://cloud.r-project.org") +) library(lozen) ```