From ba436035db5d4cc61722cfe3ef5407358dc10c69 Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 1 Sep 2023 12:19:43 +0200 Subject: [PATCH 01/30] add hbGPS to page 1 #90 --- R/identify_tools.R | 12 +++++--- R/myApp.R | 21 +++++++++----- tests/testthat/test_identify_tools.R | 41 ++++++++++++++-------------- 3 files changed, 43 insertions(+), 31 deletions(-) diff --git a/R/identify_tools.R b/R/identify_tools.R index afbd82e..bc14cb0 100644 --- a/R/identify_tools.R +++ b/R/identify_tools.R @@ -11,9 +11,9 @@ # S4 class needs to be defined outside function setClass(Class = "toolio", slots = list(input = "character", output = "character", usecases = "character")) -identify_tools = function(datatypes = c("AccRaw", "ACount", "GPS", "GIS", "PALMSpy_out"), +identify_tools = function(datatypes = c("AccRaw", "ACount", "GPS", "GIS", "PALMSpy_out", "GGIR_out"), goals = c("PA", "QC", "Trips", "Environment"), - available_tools = c("GGIR", "PALMSpy", "palmsplusr", "Counts")) { + available_tools = c("GGIR", "PALMSpy", "palmsplusr", "Counts", "hbGPS")) { iotools = list(GGIR = new("toolio", input = "AccRaw", output = c("GGIR_out", "ACount"), @@ -29,9 +29,13 @@ identify_tools = function(datatypes = c("AccRaw", "ACount", "GPS", "GIS", "PALMS Counts = new("toolio", input = "AccRaw", output = c("Counts_out"), - usecases = c("PA", "Trips", "QC", "Environment"))) + usecases = c("PA", "Trips", "QC", "Environment")), + hbGPS = new("toolio", + input = c("GGIR_out","GPS"), + output = c("hbGPS_out"), + usecases = c("Trips", "QC", "Environment"))) iotools = iotools[which(names(iotools) %in% available_tools)] # only look at available tools - allgoals = tools_needed = outputs = c() + tools_needed = outputs = c() # loop over tools and select the ones that generate the output users needs and is able to generate for (j in 1:length(available_tools)) { # assumption is that pipeline is never longer then number of tools for (i in 1:length(iotools)) { diff --git a/R/myApp.R b/R/myApp.R index 2c84b00..f2ce671 100644 --- a/R/myApp.R +++ b/R/myApp.R @@ -46,11 +46,15 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { "GPS (in .csv format)", "GIS (shape files + linkage file)", "PALMS(py) output previously generated", + "GGIR time series output previously generated", "Sleep Diary (in GGIR compatible .csv format)"), - choiceValues = list("AccRaw", "ACount", "GPS", "GIS", "PALMSpy_out", "SleepDiary"), width = '100%'), + choiceValues = list("AccRaw", "ACount", "GPS", "GIS", + "PALMSpy_out", "GGIR_out", "SleepDiary"), width = '100%'), conditionalPanel(condition = paste0("input.availabledata.indexOf(`AccRaw`) > -1 || ", # GGIR "(input.availabledata.indexOf(`ACount`) > -1 && ", # PALMSpy "input.availabledata.indexOf(`GPS`) > -1) || ", + "(input.availabledata.indexOf(`GGIR_out`) > -1 && ", # hbGPS + "input.availabledata.indexOf(`GPS`) > -1) || ", "(input.availabledata.indexOf(`ACount`) > -1 && ", # palmsplusr variant 1 "input.availabledata.indexOf(`GPS`) > -1 && ", "input.availabledata.indexOf(`GIS`) > -1) || ", @@ -67,8 +71,9 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { choiceNames = list("GGIR (R package)", "Neishabouri Counts / actilifecounts (R package)", "PALMSpy (Python library)", + "hbGPS (R package)", "palmsplusr (R package)"), - choiceValues = list("GGIR", "Counts", "PALMSpy", "palmsplusr"), width = '100%') + choiceValues = list("GGIR", "Counts", "PALMSpy", "hbGPS", "palmsplusr"), width = '100%') ), actionButton("page_12", "next"), actionButton("restart_1", "restart", @@ -310,11 +315,11 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), if ("GGIR" %in% input$tools == TRUE & "AccRaw" %in% input$availabledata == FALSE) { showNotification("GGIR not possible without access to raw accelerometer data", type = "error") } else { - if ("PALMSpy" %in% input$tools == TRUE & "GPS" %in% input$availabledata == FALSE) { - showNotification("PALMSpy not possible without access to GPS data", type = "error") + if ("PALMSpy" %in% input$tools == TRUE & all(c("AccRaw", "ACount", "GPS") %in% input$availabledata == FALSE)) { + showNotification("PALMSpy not possible without access to Accelerometer and GPS data", type = "error") } else { - if ("PALMSpy" %in% input$tools == TRUE & all(c("AccRaw", "ACount") %in% input$availabledata == FALSE)) { - showNotification("PALMSpy not possible without access to Accelerometer data", type = "error") + if ("hbGPS" %in% input$tools == TRUE & all(c("GPS", "GGIR_out") %in% input$availabledata == FALSE)) { + showNotification("hbGPS not possible without access to GPS data and GGIR times series output", type = "error") } else { if ("palmsplusr" %in% input$tools == TRUE & "GIS" %in% input$availabledata == FALSE) { showNotification("palmsplusr not possible without access to GIS data", type = "error") @@ -332,6 +337,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), } } } + } } } @@ -542,9 +548,10 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), if (is.null(x)) x <- character(0) researchgoals = c() - if ("GPS" %in% x & any(c("AccRaw", "ACount") %in% x)) researchgoals = c(researchgoals, "Trips", "QC") + if ("GPS" %in% x & any(c("AccRaw", "ACount", "GGIR_out") %in% x)) researchgoals = c(researchgoals, "Trips", "QC") if (all(c("GPS", "GIS") %in% x) & any(c("AccRaw", "ACount") %in% x)) researchgoals = c(researchgoals, "Environment", "QC") if (all(c("PALMSpy_out", "GIS") %in% x)) researchgoals = c(researchgoals, "Environment", "QC") + if (all(c("GGIR_out", "GIS") %in% x)) researchgoals = c(researchgoals, "Environment", "QC") if ("AccRaw" %in% x | all(c("AccCount", "GPS") %in% x)) researchgoals = c(researchgoals, "PB", "QC") if ("AccRaw" %in% x) researchgoals = c(researchgoals, "QC") if ("ACount" %in% x == TRUE & "GPS" %in% x == FALSE & "AccRaw" %in% x == FALSE) researchgoals = c() diff --git a/tests/testthat/test_identify_tools.R b/tests/testthat/test_identify_tools.R index f5cbe4f..11fd093 100644 --- a/tests/testthat/test_identify_tools.R +++ b/tests/testthat/test_identify_tools.R @@ -1,39 +1,40 @@ library(HabitusGUI) context("Identify tools needed to process data") test_that("Correct tools are proposed by test_identify_tools", { - + available_tools = c("GGIR", "PALMSpy", "hbGPS", "palmsplusr", "Counts") # Scenario 1: All tools needed - sce1 = identify_tools(datatypes = c("AccRaw", "ACount", "GPS", "GIS"), + sce1 = identify_tools(datatypes = c("AccRaw", "ACount", "GPS", "GIS", "GGIR_out"), goals = c("PA", "Sleep", "QC", "Trips", "Environment"), - available_tools = c("GGIR", "PALMSpy", "palmsplusr", "Counts")) - expect_equal(length(sce1$tools_needed), 3) - expect_equal(sce1$tools_needed, c("GGIR", "PALMSpy", "palmsplusr")) + available_tools = available_tools) + expect_equal(length(sce1$tools_needed), 4) + expect_equal(sce1$tools_needed, c("GGIR", "PALMSpy", "palmsplusr", "hbGPS")) expect_equal(sce1$iotools[[1]]@output, c("GGIR_out", "ACount")) expect_equal(sce1$iotools[[3]]@output, "palmsplusr_out") + expect_equal(sce1$iotools[[4]]@output, "hbGPS_out") # Scenario 2: GIS missing - sce2 = identify_tools(datatypes = c("AccRaw", "ACount", "GPS"), + sce2 = identify_tools(datatypes = c("AccRaw", "ACount", "GPS", "GGIR_out"), goals = c("PA", "Sleep", "QC", "Trips", "Environment"), - available_tools = c("GGIR", "PALMSpy", "palmsplusr", "Counts")) - expect_equal(length(sce2$tools_needed), 2) - expect_equal(sce2$tools_needed, c("GGIR", "PALMSpy")) + available_tools = available_tools) + expect_equal(length(sce2$tools_needed), 3) + expect_equal(sce2$tools_needed, c("GGIR", "PALMSpy", "hbGPS")) expect_equal(sce2$iotools[[2]]@output, "PALMSpy_out") # Scenario 3: AccRaw missing - sce3 = identify_tools(datatypes = c("ACount", "GPS", "GIS"), + sce3 = identify_tools(datatypes = c("ACount", "GPS", "GIS", "GGIR_out"), goals = c("PA", "Sleep", "QC", "Trips", "Environment"), - available_tools = c("GGIR", "PALMSpy", "palmsplusr", "Counts")) - expect_equal(length(sce3$tools_needed), 2) - expect_equal(sce3$tools_needed, c("PALMSpy", "palmsplusr")) + available_tools = available_tools) + expect_equal(length(sce3$tools_needed), 3) + expect_equal(sce3$tools_needed, c("PALMSpy", "palmsplusr", "hbGPS")) expect_equal(sce3$iotools[[2]]@output, "palmsplusr_out") expect_equal(sce3$iotools[[2]]@usecases, c("Environment", "QC")) # Scenario 4: ACount missing - sce4 = identify_tools(datatypes = c("AccRaw", "GPS", "GIS"), + sce4 = identify_tools(datatypes = c("AccRaw", "GPS", "GIS", "GGIR_out"), goals = c("PA", "Sleep", "QC", "Trips", "Environment"), - available_tools = c("GGIR", "PALMSpy", "palmsplusr", "Counts")) - expect_equal(length(sce4$tools_needed), 4) - expect_equal(sce4$tools_needed, c("GGIR", "PALMSpy", "palmsplusr", "Counts")) + available_tools = available_tools) + expect_equal(length(sce4$tools_needed), 5) + expect_equal(sce4$tools_needed, c("GGIR", "PALMSpy", "palmsplusr", "Counts", "hbGPS")) expect_equal(sce4$iotools[[2]]@output, "PALMSpy_out") expect_equal(sce4$iotools[[2]]@usecases, c("Trips", "QC", "Environment")) expect_equal(sce4$iotools[[4]]@output, "Counts_out") @@ -42,9 +43,9 @@ test_that("Correct tools are proposed by test_identify_tools", { # Scenario 5: All data vailable, but only interest in Environment sce5 = identify_tools(datatypes = c("AccRaw", "ACount", "GPS", "GIS"), goals = c("Environment"), - available_tools = c("GGIR", "PALMSpy", "palmsplusr", "Counts")) - expect_equal(length(sce5$tools_needed), 3) - expect_equal(sce5$tools_needed, c("GGIR", "PALMSpy", "palmsplusr")) + available_tools = available_tools) + expect_equal(length(sce5$tools_needed), 4) + expect_equal(sce5$tools_needed, c("GGIR", "PALMSpy", "palmsplusr", "hbGPS")) expect_equal(sce5$iotools[[2]]@output, "PALMSpy_out") expect_equal(sce5$iotools[[2]]@usecases, c("Trips", "QC", "Environment")) }) \ No newline at end of file From 3dfeb09b4f89fd454cf5004409cb70b47da6000c Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 1 Sep 2023 14:38:50 +0200 Subject: [PATCH 02/30] #90 clarify that user can use PALMSpy or hbGPS --- R/identify_tools.R | 8 ++-- R/myApp.R | 61 ++++++++++++++++------------ tests/testthat/test_identify_tools.R | 4 +- 3 files changed, 40 insertions(+), 33 deletions(-) diff --git a/R/identify_tools.R b/R/identify_tools.R index bc14cb0..b79a021 100644 --- a/R/identify_tools.R +++ b/R/identify_tools.R @@ -13,7 +13,7 @@ setClass(Class = "toolio", slots = list(input = "character", output = "character identify_tools = function(datatypes = c("AccRaw", "ACount", "GPS", "GIS", "PALMSpy_out", "GGIR_out"), goals = c("PA", "QC", "Trips", "Environment"), - available_tools = c("GGIR", "PALMSpy", "palmsplusr", "Counts", "hbGPS")) { + available_tools = c("GGIR", "PALMSpy", "palmsplusr", "CountConverter", "hbGPS")) { iotools = list(GGIR = new("toolio", input = "AccRaw", output = c("GGIR_out", "ACount"), @@ -26,7 +26,7 @@ identify_tools = function(datatypes = c("AccRaw", "ACount", "GPS", "GIS", "PALMS input = c("PALMSpy_out", "GIS"), output = c("palmsplusr_out"), usecases = c("Environment", "QC")), - Counts = new("toolio", + CountConverter = new("toolio", input = "AccRaw", output = c("Counts_out"), usecases = c("PA", "Trips", "QC", "Environment")), @@ -50,10 +50,10 @@ identify_tools = function(datatypes = c("AccRaw", "ACount", "GPS", "GIS", "PALMS if ("AccRaw" %in% datatypes == FALSE & "GGIR" %in% tools_needed) { tools_needed = tools_needed[-which(tools_needed == "GGIR")] } - if ("Counts" %in% tools_needed) { + if ("CountConverter" %in% tools_needed) { if ("ACount" %in% datatypes == TRUE | # No need to estimate counts if they already exist ("AccRaw" %in% datatypes == TRUE & "GPS" %in% datatypes == FALSE)) { # No need to estimate counts if there is no GPS data - tools_needed = tools_needed[-which(tools_needed == "Counts")] + tools_needed = tools_needed[-which(tools_needed == "CountConverter")] } } invisible(list(tools_needed = tools_needed, iotools = iotools[which(names(iotools) %in% tools_needed)])) diff --git a/R/myApp.R b/R/myApp.R index f2ce671..27d0fd6 100644 --- a/R/myApp.R +++ b/R/myApp.R @@ -69,11 +69,11 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { hr(), checkboxGroupInput("tools", label = "Select the tools you would like to use:", choiceNames = list("GGIR (R package)", - "Neishabouri Counts / actilifecounts (R package)", + "CountConverter (R package GGIR + actilifecounts)", "PALMSpy (Python library)", "hbGPS (R package)", "palmsplusr (R package)"), - choiceValues = list("GGIR", "Counts", "PALMSpy", "hbGPS", "palmsplusr"), width = '100%') + choiceValues = list("GGIR", "CountConverter", "PALMSpy", "hbGPS", "palmsplusr"), width = '100%') ), actionButton("page_12", "next"), actionButton("restart_1", "restart", @@ -88,7 +88,7 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { # Select input folder raw accelerometer data if raw data is available and GGIR is planned------------------ conditionalPanel(condition = paste0("input.availabledata.indexOf(`AccRaw`) > -1 && ", "(input.tools.includes(`GGIR`) || ", - "input.tools.includes(`Counts`))"), + "input.tools.includes(`CountConverter`))"), shinyFiles::shinyDirButton("rawaccdir", label = "Raw accelerometry data directory...", title = "Select raw accelerometer data directory"), verbatimTextOutput("rawaccdir", placeholder = TRUE), @@ -166,9 +166,9 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { modConfigUI("edit_ggir_config"), hr() ), - conditionalPanel(condition = "input.tools.includes('Counts')", - h2("Counts"), - p("No parameters are needed for the Counts"), + conditionalPanel(condition = "input.tools.includes('CountConverter')", + h2("CountConverter"), + p("No parameters are needed for the CountConverter"), hr() ), conditionalPanel(condition = "input.tools.includes('PALMSpy')", @@ -197,13 +197,13 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { # hr(), p("\n"), conditionalPanel(condition = paste0("input.tools.includes('GGIR') || ", - "input.tools.includes('Counts')"), + "input.tools.includes('CountConverter')"), conditionalPanel(condition = paste0("input.tools.indexOf(`GGIR`) > -1 && ", - "input.tools.indexOf(`Counts`) > -1"), - h3("GGIR and Counts:") + "input.tools.indexOf(`CountConverter`) > -1"), + h3("GGIR and CountConverter:") ), - conditionalPanel(condition = "input.tools.indexOf(`Counts`) == -1", + conditionalPanel(condition = "input.tools.indexOf(`CountConverter`) == -1", h3("GGIR:") ), shinyjs::useShinyjs(), @@ -328,8 +328,8 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), "GPS" %in% input$availabledata == FALSE & all(c("AccRaw", "ACount") %in% input$availabledata == FALSE))) { showNotification("palmsplusr requires either previously generated PALMS(py) output or GPS and Accelerometer data", type = "error") } else { - if ("Counts" %in% input$tools == TRUE & "AccRaw" %in% input$availabledata == FALSE) { - showNotification("Counts not possible without access to raw accelerometer data", type = "error") + if ("CountConverter" %in% input$tools == TRUE & "AccRaw" %in% input$availabledata == FALSE) { + showNotification("CountConverter not possible without access to raw accelerometer data", type = "error") } else { switch_page(2) } @@ -373,7 +373,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), if ("AccRaw" %in% input$availabledata & "GGIR" %in% input$tools & as.character(input$rawaccdir)[1] == "0" & is.null(selectedRawaccdir)) { showNotification("Select raw accelerometer data directory", type = "error") } else { - if ("AccRaw" %in% input$availabledata & "Counts" %in% input$tools & as.character(input$rawaccdir)[1] == "0" & is.null(selectedRawaccdir)) { + if ("AccRaw" %in% input$availabledata & "CountConverter" %in% input$tools & as.character(input$rawaccdir)[1] == "0" & is.null(selectedRawaccdir)) { showNotification("Select raw accelerometer data directory", type = "error") } else { if ("ACount" %in% input$availabledata & "PALMSpy" %in% input$tools & as.character(input$countaccdir)[1] == "0" & is.null(selectedCountaccdir)) { @@ -586,7 +586,14 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), # Identify pipeline with tools to be used and send to UI proposed_pipeline <- reactive(identify_tools(datatypes = input$availabledata, goals = input$researchgoals)$tools_needed) output$pipeline <- renderText({ - message = paste0("Proposed software pipeline: ", paste0(proposed_pipeline(), collapse = " + ")) + if (all(c("hbGPS", "PALMSpy") %in% proposed_pipeline()) == TRUE) { + message = paste0("Proposed software pipeline: ", + paste0(grep(pattern = "PALMSpy|CountConverter", x = proposed_pipeline(), invert = TRUE, value = TRUE), collapse = " + "), + " OR replace hbGPS by ", + paste0(grep(pattern = "PALMSpy|CountConverter", x = proposed_pipeline(), invert = FALSE, value = TRUE), collapse = " + ")) + } else { + message = paste0("Proposed software pipeline: ", paste0(proposed_pipeline(), collapse = " + ")) + } ifelse(length(proposed_pipeline()) == 0, yes = "--> Tick boxes above according to the analysis you would like to do", no = message) @@ -792,13 +799,13 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), #======================================================================== - # Apply GGIR / Counts after button is pressed + # Apply GGIR / CountConverter after button is pressed #======================================================================== runGGIR <- eventReactive(input$start_ggir, { GGIRCounts_message = "" - if ("GGIR" %in% input$tools | "Counts" %in% input$tools) { + if ("GGIR" %in% input$tools | "CountConverter" %in% input$tools) { GGIRCounts_message = "" # Basic check before running function: ready_to_run_ggirCounts = FALSE @@ -815,8 +822,8 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), # Only run function when checks are met: if (ready_to_run_ggirCounts == TRUE) { shinyjs::hide(id = "start_ggir") - if ("Counts" %in% input$tools) { - id_ggir = showNotification("GGIR and Counts in progress ...", type = "message", duration = NULL, closeButton = FALSE) + if ("CountConverter" %in% input$tools) { + id_ggir = showNotification("GGIR and CountConverter in progress ...", type = "message", duration = NULL, closeButton = FALSE) do.Counts = TRUE } else { # this line makes that if user is trying to use a config defined in a previous @@ -826,7 +833,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), if (as.logical(config.Counts) == TRUE) { # if counts was not selected as a tool, but acc.metric was defined as # NeishabouriCount, then turn do.Counts to TRUE - id_ggir = showNotification("GGIR and Counts in progress ...", type = "message", duration = NULL, closeButton = FALSE) + id_ggir = showNotification("GGIR and CountConverter in progress ...", type = "message", duration = NULL, closeButton = FALSE) do.Counts = TRUE } else { id_ggir = showNotification("GGIR in progress ...", type = "message", duration = NULL, closeButton = FALSE) @@ -882,21 +889,21 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), expected_outputdir_ggir = paste0(global$data_out, "/output_", basename(global$raw_acc_in)) expected_ggiroutput_file = paste0(global$data_out, "/output_", basename(global$raw_acc_in), "/results/part2_daysummary.csv") if (file.exists(expected_ggiroutput_file) == TRUE) { # checks whether ggir output was created - if ("Counts" %in% input$tools) { # if Counts was suppoed to run + if ("CountConverter" %in% input$tools) { # if CountConverter was suppoed to run expected_outputdir_Counts = paste0(global$data_out, "/actigraph") if (dir.exists(expected_outputdir_Counts) == TRUE) { # checks whether output dir was created if (length(dir(expected_outputdir_Counts) > 0)) { # checks whether it has been filled with results - GGIRCounts_message = paste0(#"Counts and GGIR successfully completed at ", Sys.time(), + GGIRCounts_message = paste0(#"CountConverter and GGIR successfully completed at ", Sys.time(), "Output is stored in ", expected_outputdir_Counts, " and ", expected_outputdir_ggir, "
The table below shows the content of part2_daysummary.csv") GGIRpart2 = read.csv(expected_ggiroutput_file) output$GGIRpart2 <- DT::renderDataTable(GGIRpart2, options = list(scrollX = TRUE)) } else { - GGIRCounts_message = paste0("Counts unsuccessful. No file found inside ", expected_outputdir_Counts) + GGIRCounts_message = paste0("CountConverter unsuccessful. No file found inside ", expected_outputdir_Counts) } } else { - GGIRCounts_message = paste0("Counts unsuccessful. Dir ",expected_outputdir_Counts, " not found") + GGIRCounts_message = paste0("CountConverter unsuccessful. Dir ",expected_outputdir_Counts, " not found") } } else { GGIRCounts_message = paste0(#"GGIR successfully completed at ", Sys.time(), @@ -946,7 +953,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), } } else { PALMSpy_message = paste0("Folder that is supposed to hold count files does not exist: ", - count_file_location, " First run GGIR and Counts.") + count_file_location, " First run GGIR and CountConverter.") } } if (ready_to_run_palsmpy == TRUE) { @@ -1148,9 +1155,9 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), }) output$recommendorder <- renderText({ pipeline = proposed_pipeline() - if ("GGIR" %in% pipeline & "Counts" %in% pipeline) { - pipeline = pipeline[-which(pipeline %in% c("GGIR", "Counts"))] - pipeline = c("GGIR & Counts", pipeline) + if ("GGIR" %in% pipeline & "CountConverter" %in% pipeline) { + pipeline = pipeline[-which(pipeline %in% c("GGIR", "CountConverter"))] + pipeline = c("GGIR & CountConverter", pipeline) } if (length(pipeline) > 1) { message = paste0("Recommended order of analyses: ", paste0(pipeline, collapse = " -> ")) diff --git a/tests/testthat/test_identify_tools.R b/tests/testthat/test_identify_tools.R index 11fd093..703996d 100644 --- a/tests/testthat/test_identify_tools.R +++ b/tests/testthat/test_identify_tools.R @@ -1,7 +1,7 @@ library(HabitusGUI) context("Identify tools needed to process data") test_that("Correct tools are proposed by test_identify_tools", { - available_tools = c("GGIR", "PALMSpy", "hbGPS", "palmsplusr", "Counts") + available_tools = c("GGIR", "PALMSpy", "hbGPS", "palmsplusr", "CountConverter") # Scenario 1: All tools needed sce1 = identify_tools(datatypes = c("AccRaw", "ACount", "GPS", "GIS", "GGIR_out"), goals = c("PA", "Sleep", "QC", "Trips", "Environment"), @@ -34,7 +34,7 @@ test_that("Correct tools are proposed by test_identify_tools", { goals = c("PA", "Sleep", "QC", "Trips", "Environment"), available_tools = available_tools) expect_equal(length(sce4$tools_needed), 5) - expect_equal(sce4$tools_needed, c("GGIR", "PALMSpy", "palmsplusr", "Counts", "hbGPS")) + expect_equal(sce4$tools_needed, c("GGIR", "PALMSpy", "palmsplusr", "CountConverter", "hbGPS")) expect_equal(sce4$iotools[[2]]@output, "PALMSpy_out") expect_equal(sce4$iotools[[2]]@usecases, c("Trips", "QC", "Environment")) expect_equal(sce4$iotools[[4]]@output, "Counts_out") From 2830002977d780c55c16afae56b150a4feb3a021 Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 1 Sep 2023 16:08:58 +0200 Subject: [PATCH 03/30] #90 page 1 now also able to account for previously generated hbGPS output --- R/identify_tools.R | 29 ++++++++++++++++------------ R/myApp.R | 26 ++++++++++++++++++------- tests/testthat/test_identify_tools.R | 17 +++++++++++++--- 3 files changed, 50 insertions(+), 22 deletions(-) diff --git a/R/identify_tools.R b/R/identify_tools.R index b79a021..d12eb12 100644 --- a/R/identify_tools.R +++ b/R/identify_tools.R @@ -11,7 +11,8 @@ # S4 class needs to be defined outside function setClass(Class = "toolio", slots = list(input = "character", output = "character", usecases = "character")) -identify_tools = function(datatypes = c("AccRaw", "ACount", "GPS", "GIS", "PALMSpy_out", "GGIR_out"), +identify_tools = function(datatypes = c("AccRaw", "ACount", "GPS", "GIS", + "PALMSpy_out", "GGIR_out", "hbGPS_out"), goals = c("PA", "QC", "Trips", "Environment"), available_tools = c("GGIR", "PALMSpy", "palmsplusr", "CountConverter", "hbGPS")) { iotools = list(GGIR = new("toolio", @@ -19,21 +20,25 @@ identify_tools = function(datatypes = c("AccRaw", "ACount", "GPS", "GIS", "PALMS output = c("GGIR_out", "ACount"), usecases = c("PA", "QC", "Trips", "Environment")), PALMSpy = new("toolio", - input = c("ACount","GPS"), + input = c("ACount", "GPS"), output = c("PALMSpy_out"), usecases = c("Trips", "QC", "Environment")), - palmsplusr = new("toolio", - input = c("PALMSpy_out", "GIS"), - output = c("palmsplusr_out"), - usecases = c("Environment", "QC")), + palmsplusr = new("toolio", # palmsplusr based on PALMSpy output + input = c("PALMSpy_out", "GIS"), + output = c("palmsplusr_out"), + usecases = c("Environment", "QC")), + palmsplusr = new("toolio", # palmsplusr based on hbGPS output + input = c("hbGPS_out", "GIS"), + output = c("palmsplusr_out"), + usecases = c("Environment", "QC")), CountConverter = new("toolio", - input = "AccRaw", - output = c("Counts_out"), - usecases = c("PA", "Trips", "QC", "Environment")), + input = "AccRaw", + output = c("Counts_out"), + usecases = c("PA", "Trips", "QC", "Environment")), hbGPS = new("toolio", - input = c("GGIR_out","GPS"), - output = c("hbGPS_out"), - usecases = c("Trips", "QC", "Environment"))) + input = c("GGIR_out","GPS"), + output = c("hbGPS_out"), + usecases = c("Trips", "QC", "Environment"))) iotools = iotools[which(names(iotools) %in% available_tools)] # only look at available tools tools_needed = outputs = c() # loop over tools and select the ones that generate the output users needs and is able to generate diff --git a/R/myApp.R b/R/myApp.R index 27d0fd6..fee5b35 100644 --- a/R/myApp.R +++ b/R/myApp.R @@ -45,11 +45,13 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { "Counts (in ActiGraph .csv format)", "GPS (in .csv format)", "GIS (shape files + linkage file)", - "PALMS(py) output previously generated", - "GGIR time series output previously generated", - "Sleep Diary (in GGIR compatible .csv format)"), - choiceValues = list("AccRaw", "ACount", "GPS", "GIS", - "PALMSpy_out", "GGIR_out", "SleepDiary"), width = '100%'), + "Sleep Diary (in GGIR compatible .csv format)", + "previously generated PALMS(py) output", + "previously generated GGIR time series output", + "previously generated hbGPS output"), + choiceValues = list("AccRaw", "ACount", "GPS", "GIS", "SleepDiary", + "PALMSpy_out", "GGIR_out", "hbGPS_out"), width = '100%'), + # Only show more check boxs if user specified available data sufficient for any of the tools conditionalPanel(condition = paste0("input.availabledata.indexOf(`AccRaw`) > -1 || ", # GGIR "(input.availabledata.indexOf(`ACount`) > -1 && ", # PALMSpy "input.availabledata.indexOf(`GPS`) > -1) || ", @@ -58,7 +60,11 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { "(input.availabledata.indexOf(`ACount`) > -1 && ", # palmsplusr variant 1 "input.availabledata.indexOf(`GPS`) > -1 && ", "input.availabledata.indexOf(`GIS`) > -1) || ", - "(input.availabledata.indexOf(`PALMSpy_out`) > -1 && ", #palmsplusr variant 2 + "(input.availabledata.indexOf(`AccRaw`) > -1 && ", # palmsplusr variant 2 + "input.availabledata.indexOf(`GPS`) > -1 && ", + "input.availabledata.indexOf(`GIS`) > -1) || ", + "((input.availabledata.indexOf(`hbGPS_out`) > -1 || ", #palmsplusr variant 3 + "input.availabledata.indexOf(`PALMSpy_out`) > -1) && ", "input.availabledata.indexOf(`GIS`) > -1)"), hr(), # If there is enough input data then show second check box to ask user about their research goals @@ -325,8 +331,13 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), showNotification("palmsplusr not possible without access to GIS data", type = "error") } else { if ("palmsplusr" %in% input$tools == TRUE & ("PALMSpy_out" %in% input$availabledata == FALSE & + "hbGPS_out" %in% input$availabledata == FALSE & "GPS" %in% input$availabledata == FALSE & all(c("AccRaw", "ACount") %in% input$availabledata == FALSE))) { - showNotification("palmsplusr requires either previously generated PALMS(py) output or GPS and Accelerometer data", type = "error") + showNotification(paste0("palmsplusr requires either previously", + " generated PALMS(py) or hbGPS output,", + " or GPS and Accelerometer data such", + " that either PALMSpyor hbGPS can be", + " run"), type = "error") } else { if ("CountConverter" %in% input$tools == TRUE & "AccRaw" %in% input$availabledata == FALSE) { showNotification("CountConverter not possible without access to raw accelerometer data", type = "error") @@ -551,6 +562,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), if ("GPS" %in% x & any(c("AccRaw", "ACount", "GGIR_out") %in% x)) researchgoals = c(researchgoals, "Trips", "QC") if (all(c("GPS", "GIS") %in% x) & any(c("AccRaw", "ACount") %in% x)) researchgoals = c(researchgoals, "Environment", "QC") if (all(c("PALMSpy_out", "GIS") %in% x)) researchgoals = c(researchgoals, "Environment", "QC") + if (all(c("hbGPS_out", "GIS") %in% x)) researchgoals = c(researchgoals, "Environment", "QC") if (all(c("GGIR_out", "GIS") %in% x)) researchgoals = c(researchgoals, "Environment", "QC") if ("AccRaw" %in% x | all(c("AccCount", "GPS") %in% x)) researchgoals = c(researchgoals, "PB", "QC") if ("AccRaw" %in% x) researchgoals = c(researchgoals, "QC") diff --git a/tests/testthat/test_identify_tools.R b/tests/testthat/test_identify_tools.R index 703996d..225bd04 100644 --- a/tests/testthat/test_identify_tools.R +++ b/tests/testthat/test_identify_tools.R @@ -10,7 +10,8 @@ test_that("Correct tools are proposed by test_identify_tools", { expect_equal(sce1$tools_needed, c("GGIR", "PALMSpy", "palmsplusr", "hbGPS")) expect_equal(sce1$iotools[[1]]@output, c("GGIR_out", "ACount")) expect_equal(sce1$iotools[[3]]@output, "palmsplusr_out") - expect_equal(sce1$iotools[[4]]@output, "hbGPS_out") + expect_equal(sce1$iotools[[4]]@output, "palmsplusr_out") + expect_equal(sce1$iotools[[5]]@output, "hbGPS_out") # Scenario 2: GIS missing sce2 = identify_tools(datatypes = c("AccRaw", "ACount", "GPS", "GGIR_out"), @@ -37,8 +38,8 @@ test_that("Correct tools are proposed by test_identify_tools", { expect_equal(sce4$tools_needed, c("GGIR", "PALMSpy", "palmsplusr", "CountConverter", "hbGPS")) expect_equal(sce4$iotools[[2]]@output, "PALMSpy_out") expect_equal(sce4$iotools[[2]]@usecases, c("Trips", "QC", "Environment")) - expect_equal(sce4$iotools[[4]]@output, "Counts_out") - expect_equal(sce4$iotools[[4]]@usecases, c("PA", "Trips", "QC", "Environment")) + expect_equal(sce4$iotools[[4]]@output, "palmsplusr_out") + expect_equal(sce4$iotools[[5]]@usecases, c("PA", "Trips", "QC", "Environment")) # Scenario 5: All data vailable, but only interest in Environment sce5 = identify_tools(datatypes = c("AccRaw", "ACount", "GPS", "GIS"), @@ -48,4 +49,14 @@ test_that("Correct tools are proposed by test_identify_tools", { expect_equal(sce5$tools_needed, c("GGIR", "PALMSpy", "palmsplusr", "hbGPS")) expect_equal(sce5$iotools[[2]]@output, "PALMSpy_out") expect_equal(sce5$iotools[[2]]@usecases, c("Trips", "QC", "Environment")) + + # Scenario 6: hbGPS_out and GIS available + sce6 = identify_tools(datatypes = c("hbGPS_out", "GIS"), + goals = c("QC"), + available_tools = available_tools) + expect_equal(length(sce6$tools_needed), 1) + expect_equal(sce6$tools_needed, "palmsplusr") + expect_equal(sce6$iotools[[1]]@output, "palmsplusr_out") + expect_equal(sce6$iotools[[1]]@usecases, c("Environment", "QC")) + }) \ No newline at end of file From d8c8bb4a4b4bde1659e8d886883b8ce1d969757c Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 1 Sep 2023 16:26:24 +0200 Subject: [PATCH 04/30] #90 integrating hbGPS on page 2 of the app --- R/myApp.R | 44 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/R/myApp.R b/R/myApp.R index fee5b35..14331d5 100644 --- a/R/myApp.R +++ b/R/myApp.R @@ -110,7 +110,9 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { hr() ), # Select input folder gps data ----------------------------------- - conditionalPanel(condition = "input.availabledata.indexOf(`GPS`) > -1 && input.tools.includes(`PALMSpy`)", + conditionalPanel(condition = paste0("input.availabledata.indexOf(`GPS`) > -1 && ", + "(input.tools.includes(`PALMSpy`) || ", + "input.tools.includes(`hbGPS`))"), shinyFiles::shinyDirButton("gpsdir", label = "GPS data directory...", title = "Select GPS data directory"), verbatimTextOutput("gpsdir", placeholder = TRUE), @@ -136,6 +138,14 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { verbatimTextOutput("palmspyoutdir", placeholder = TRUE), hr() ), + # Select input folder hbGPS output data ----------------------------------- + conditionalPanel(condition = paste0("input.availabledata.indexOf(`hbGPS_out`) > -1 && ", + "input.tools.includes(`palmsplusr`) && !input.tools.includes(`hbGPS`)"), + shinyFiles::shinyDirButton("hbgpsoutdir", label = "Previously generated hbGPS output directory...", + title = "Select hbGPS output directory"), + verbatimTextOutput("hbgpsoutdir", placeholder = TRUE), + hr() + ), # Upload sleep diary ---------------------------------------------------- conditionalPanel(condition = "input.availabledata.indexOf(`SleepDiary`) > -1 && input.tools.includes(`GGIR`)", shinyFiles::shinyFilesButton("sleepdiaryfile", label = "Sleepdiary file...", @@ -294,6 +304,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), if (length(values$gisdir) < 2) selectedGisdir = c() else selectedGisdir = paste(values$gisdir$path, collapse = .Platform$file.sep) if (length(values$gislinkfile) < 2) selectedGislinkfile = c() else selectedGislinkfile = paste(values$gislinkfile$path, collapse = .Platform$file.sep) if (length(values$palmspyoutdir) < 2) selectedPalmspyoutdir = c() else selectedPalmspyoutdir = paste(values$palmspyoutdir$path, collapse = .Platform$file.sep) + if (length(values$hbgpsoutdir) < 2) selectedHbgpsoutdir = c() else selectedHbgpsoutdir = paste(values$hbgpsoutdir$path, collapse = .Platform$file.sep) if (length(values$sleepdiaryfile) < 2) selectedSleepdiaryfile = c() else selectedSleepdiaryfile = paste(values$sleepdiaryfile$path, collapse = .Platform$file.sep) if (length(values$outputdir) < 2) selectedOutputdir = c() else selectedOutputdir = paste(values$outputdir$path, collapse = .Platform$file.sep) @@ -357,7 +368,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), observeEvent(input$page_23, { # Previous selection of directories prevPathNames = c("rawaccdir", "countaccdir", "sleepdiaryfile", "configfileGGIR", - "gpsdir", "gisdir", "gislinkfile", "palmspyoutdir", "outputdir") + "gpsdir", "gisdir", "gislinkfile", "palmspyoutdir", "hbgpsoutdir", "outputdir") prevPathNames = prevPathNames[which(prevPathNames %in% names(values))] prevPaths = values[prevPathNames] values_tmp = lapply(reactiveValuesToList(input), unclass) @@ -408,7 +419,11 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), if ("PALMSpy_out" %in% input$availabledata & "palmsplusr" %in% input$tools & as.character(input$palmspyoutdir)[1] == "0" & is.null(selectedPalmspyoutdir)) { showNotification("Select previously generated PALMS(py) output directory", type = "error") } else { - switch_page(3) + if ("hbGPS_out" %in% input$availabledata & "palmsplusr" %in% input$tools & as.character(input$hbgpsoutdir)[1] == "0" & is.null(selectedHbgpsoutdir)) { + showNotification("Select previously generated hbGPS output directory", type = "error") + } else { + switch_page(3) + } } } } @@ -421,7 +436,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), observeEvent(input$page_34, { # Previous selection of directories prevPathNames = c("rawaccdir", "countaccdir", "sleepdiaryfile", - "gpsdir", "gisdir", "gislinkfile", "palmspyoutdir", "outputdir") + "gpsdir", "gisdir", "gislinkfile", "palmspyoutdir", "hbgpsoutdir", "outputdir") prevPathNames = prevPathNames[which(prevPathNames %in% names(values))] prevPaths = values[prevPathNames] values_tmp = lapply(reactiveValuesToList(input), unclass) @@ -621,6 +636,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), shinyDirChoose(input, 'gisdir', roots = c(home = homedir)) shinyFileChoose(input, 'gislinkfile', roots = c(home = homedir)) shinyDirChoose(input, 'palmspyoutdir', roots = c(home = homedir)) # Allow for old output to be used as input + shinyDirChoose(input, 'hbgpsoutdir', roots = c(home = homedir)) # Allow for old output to be used as input shinyDirChoose(input, 'outputdir', roots = c(home = homedir)) shinyFileChoose(input, 'sleepdiaryfile', roots = c(home = homedir)) @@ -632,6 +648,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), gisdir <- reactive(input$gisdir) gislinkfile <- reactive(input$gislinkfile) palmspyoutdir <- reactive(input$palmspyoutdir) # Allow for old output to be used as input + hbgpsoutdir <- reactive(input$hbgpsoutdir) # Allow for old output to be used as input outputdir <- reactive(input$outputdir) sleepdiaryfile <- reactive(input$sleepdiaryfile) #$datapath @@ -662,10 +679,11 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), gis_in = getPrevPath(dirname = "gisdir", ifEmpty = NULL, homedir = homedir, values) gislinkfile_in = getPrevPath(dirname = "gislinkfile", ifEmpty = NULL, homedir = homedir, values) palmspyout_in = getPrevPath(dirname = "palmspyoutdir", ifEmpty = NULL, homedir = homedir, values) + hbgpsout_in = getPrevPath(dirname = "hbgpsoutdir", ifEmpty = NULL, homedir = homedir, values) sleepdiary_file = getPrevPath(dirname = "sleepdiaryfile", ifEmpty = NULL, homedir = homedir, values) } else { data_out = homedir - raw_acc_in = count_acc_in = gps_in = gis_in = gislinkfile_in = palmspyout_in = sleepdiary_file = NULL + raw_acc_in = count_acc_in = gps_in = gis_in = gislinkfile_in = palmspyout_in = hbgpsout_in = sleepdiary_file = NULL } global <- reactiveValues(data_out = cleanPath(data_out), raw_acc_in = cleanPath(raw_acc_in), @@ -674,6 +692,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), gis_in = cleanPath(gis_in), gislinkfile_in = cleanPath(gislinkfile_in), palmspyout_in = cleanPath(palmspyout_in), + hbgpsout_in = cleanPath(hbgpsout_in), sleepdiaryfile = cleanPath(sleepdiary_file)) #, pipeline = NULL) @@ -731,9 +750,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), parent_folder = "" } updateTextInput(session, "dataset_name", - # label = paste("New label", parent_folder), value = paste(parent_folder)) - }) observeEvent(ignoreNULL = TRUE, eventExpr = { @@ -755,6 +772,16 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), global$palmspyout_in <- file.path(home, paste(unlist(palmspyoutdir()$path[-1]), collapse = .Platform$file.sep)) }) + observeEvent(ignoreNULL = TRUE, + eventExpr = { + input$hbgpsoutdir # every time input$hbgpsoutdir updates ... + }, + handlerExpr = { # ... we re-assign global$hbgpsout_in + if (!"path" %in% names(hbgpsoutdir())) return() + home <- normalizePath(homedir) + global$hbgpsout_in <- + file.path(home, paste(unlist(hbgpsoutdir()$path[-1]), collapse = .Platform$file.sep)) + }) observeEvent(ignoreNULL = TRUE, eventExpr = { input$outputdir # every time input$outputdir updates ... @@ -796,6 +823,9 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), output$palmspyoutdir <- renderText({ global$palmspyout_in }) + output$hbgpsoutdir <- renderText({ + global$hbgpsout_in + }) output$outputdir <- renderText({ global$data_out }) From cb01ba99d59ed657223320bb582f5c6c70d457cc Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Wed, 6 Sep 2023 13:05:17 +0200 Subject: [PATCH 05/30] add template config_hbgps --- inst/testfiles_hbgps/config_hbgps.csv | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 inst/testfiles_hbgps/config_hbgps.csv diff --git a/inst/testfiles_hbgps/config_hbgps.csv b/inst/testfiles_hbgps/config_hbgps.csv new file mode 100644 index 0000000..f61b808 --- /dev/null +++ b/inst/testfiles_hbgps/config_hbgps.csv @@ -0,0 +1,11 @@ +argument,value,context +idloc,2,general +maxBreakLengthSeconds,120,general +minTripDur,60,general +minTripDist_m,100,general +threshold_snr,225,general +threshold_snr_ratio,50,general +tz,,general +time_format,%d/%m/%Y %H:%M:%SO,general +outputFormat,default,general +AccThresholds,,general From a54d1572aaf8b08c3bd4ebcdbb0d485fff19cab0 Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Thu, 7 Sep 2023 12:36:16 +0200 Subject: [PATCH 06/30] add functionality for handling hbGPS config files #90 --- R/check_params.R | 14 +- R/load_params.R | 16 +- inst/testfiles_ggir/config.csv | 372 +++++++++--------- .../params_description_hbGPS.tsv | 11 + inst/testfiles_hbgps/config_hbgps.csv | 12 +- .../config_palmsplusr.csv | 94 ++--- man/load_params.Rd | 2 +- tests/testthat/test_check_params.R | 21 + tests/testthat/test_load_and_update_params.R | 6 +- 9 files changed, 305 insertions(+), 243 deletions(-) create mode 100644 inst/testfiles_hbGPS/params_description_hbGPS.tsv diff --git a/R/check_params.R b/R/check_params.R index 4827613..c06a5e1 100644 --- a/R/check_params.R +++ b/R/check_params.R @@ -86,7 +86,19 @@ check_params = function(params = c(), tool = c()) { } } } - + # Check parameters which value should be timeformat + seti = which(params$class == "timeformat") + if (length(seti) > 0) { + for (i in seti) { + stmp = unlist(strsplit(params$value[i], "%")) + stmp = stmp[which(stmp != "")] + if (length(stmp) != 6) { + blocked_params$name[cnt] = rowNames[i] + blocked_params$error[cnt] = "is not a valid R time format specification." + cnt = cnt + 1 + } + } + } # Create messages if (cnt <= N) { blocked_params = blocked_params[-c(cnt:N),] diff --git a/R/load_params.R b/R/load_params.R index 9f2ddcc..1d7f1f1 100644 --- a/R/load_params.R +++ b/R/load_params.R @@ -1,7 +1,7 @@ #' load_params #' #' @param file Character to specify location of configuration file -#' @param format Character to specify format of configuration file: json_palsmpy, csv_GGIR, csv_palmsplusr +#' @param format Character to specify format of configuration file: json_palsmpy, csv_GGIR, csv_palmsplusr, csv_hbGPS #' @return list of parameters extract from the configuration file #' @importFrom jsonlite fromJSON #' @importFrom utils read.csv read.table @@ -57,6 +57,20 @@ load_params = function(file=c(), format="json_palmspy") { rownames(params_merged) = params_merged$parameter params = params_merged[, expected_tsv_columns] params = params[,-which(colnames(params) == "subfield")] + } else if (format == "csv_hbGPS") { + params = read.csv(file = file) + # remove duplicates, in case hbGPS config files have duplicates + dups = duplicated(params) + params = params[!dups,] + # Keep only parameters with a matching description in the description file + params_info_hbGPS_file = system.file("testfiles_hbGPS/params_description_hbGPS.tsv", package = "HabitusGUI")[1] + params_info_hbGPS = read.table(file = params_info_hbGPS_file, sep = "\t", header = TRUE) + params_merged = merge(params_info_hbGPS, params, by.x = "parameter", by.y = "argument") + dups = duplicated(params_merged) + params_merged = params_merged[!dups,] + rownames(params_merged) = params_merged$parameter + params = params_merged[, expected_tsv_columns] + params = params[,-which(colnames(params) == "subfield")] } else if (format == "csv_palmsplusr") { params = read.csv(file = file, sep = ",") # remove duplicates, because sometimes GGIR config files have duplicates diff --git a/inst/testfiles_ggir/config.csv b/inst/testfiles_ggir/config.csv index 9c20f63..6ea678f 100644 --- a/inst/testfiles_ggir/config.csv +++ b/inst/testfiles_ggir/config.csv @@ -1,186 +1,186 @@ -argument,value,context -datadir,c(),not applicable -do.report,"c(2,4,5)",not applicable -f0,1,not applicable -f1,1,not applicable -mode,"c(1,2,3,4,5)",not applicable -outputdir,./,not applicable -studyname,c(),not applicable -GGIRread_version,0.2.6,not applicable -GGIRversion,2.9.0,not applicable -R_version,R version 4.2.2 (2022-10-31 ucrt),not applicable -qwindow,"c(0,24)",params_247 -qlevels,c(),params_247 -qwindow_dateformat,%d-%m-%Y,params_247 -ilevels,c(),params_247 -IVIS_windowsize_minutes,60,params_247 -IVIS_epochsize_seconds,c(),params_247 -IVIS.activity.metric,2,params_247 -IVIS_acc_threshold,20,params_247 -qM5L5,c(),params_247 -MX.ig.min.dur,10,params_247 -M5L5res,10,params_247 -winhr,5,params_247 -iglevels,c(),params_247 -LUXthresholds,"c(0,100,500,1000,3000,5000,10000)",params_247 -LUX_cal_constant,c(),params_247 -LUX_cal_exponent,c(),params_247 -LUX_day_segments,c(),params_247 -window.summary.size,10,params_247 -L5M5window,"c(0,24)",params_247 -cosinor,FALSE,params_247 -includedaycrit,16,params_cleaning -ndayswindow,7,params_cleaning -strategy,1,params_cleaning -maxdur,0,params_cleaning -hrs.del.start,0,params_cleaning -hrs.del.end,0,params_cleaning -includedaycrit.part5,0.666666667,params_cleaning -excludefirstlast.part5,FALSE,params_cleaning -TimeSegments2ZeroFile,c(),params_cleaning -do.imp,TRUE,params_cleaning -data_cleaning_file,c(),params_cleaning -minimum_MM_length.part5,23,params_cleaning -excludefirstlast,FALSE,params_cleaning -includenightcrit,16,params_cleaning -excludefirst.part4,FALSE,params_cleaning -excludelast.part4,FALSE,params_cleaning -max_calendar_days,0,params_cleaning -nonWearEdgeCorrection,TRUE,params_cleaning -overwrite,FALSE,params_general -acc.metric,ENMO,params_general -maxNcores,c(),params_general -print.filename,FALSE,params_general -do.parallel,TRUE,params_general -windowsizes,"c(5,900,3600)",params_general -desiredtz,,params_general -configtz,,params_general -idloc,1,params_general -dayborder,0,params_general -part5_agg2_60seconds,FALSE,params_general -sensor.location,wrist,params_general -expand_tail_max_hours,c(),params_general -recordingEndSleepHour,c(),params_general -do.anglex,FALSE,params_metrics -do.angley,FALSE,params_metrics -do.anglez,TRUE,params_metrics -do.zcx,FALSE,params_metrics -do.zcy,FALSE,params_metrics -do.zcz,FALSE,params_metrics -do.enmo,TRUE,params_metrics -do.lfenmo,FALSE,params_metrics -do.en,FALSE,params_metrics -do.mad,FALSE,params_metrics -do.enmoa,FALSE,params_metrics -do.roll_med_acc_x,FALSE,params_metrics -do.roll_med_acc_y,FALSE,params_metrics -do.roll_med_acc_z,FALSE,params_metrics -do.dev_roll_med_acc_x,FALSE,params_metrics -do.dev_roll_med_acc_y,FALSE,params_metrics -do.dev_roll_med_acc_z,FALSE,params_metrics -do.bfen,FALSE,params_metrics -do.hfen,FALSE,params_metrics -do.hfenplus,FALSE,params_metrics -do.lfen,FALSE,params_metrics -do.lfx,FALSE,params_metrics -do.lfy,FALSE,params_metrics -do.lfz,FALSE,params_metrics -do.hfx,FALSE,params_metrics -do.hfy,FALSE,params_metrics -do.hfz,FALSE,params_metrics -do.bfx,FALSE,params_metrics -do.bfy,FALSE,params_metrics -do.bfz,FALSE,params_metrics -do.brondcounts,FALSE,params_metrics -do.neishabouricounts,FALSE,params_metrics -hb,15,params_metrics -lb,0.2,params_metrics -n,4,params_metrics -zc.lb,0.25,params_metrics -zc.hb,3,params_metrics -zc.sb,0.01,params_metrics -zc.order,2,params_metrics -zc.scale,1,params_metrics -actilife_LFE,FALSE,params_metrics -epochvalues2csv,FALSE,params_output -save_ms5rawlevels,FALSE,params_output -save_ms5raw_format,csv,params_output -save_ms5raw_without_invalid,TRUE,params_output -storefolderstructure,FALSE,params_output -timewindow,"c(MM,WW)",params_output -viewingwindow,1,params_output -dofirstpage,TRUE,params_output -visualreport,TRUE,params_output -week_weekend_aggregate.part5,FALSE,params_output -do.part3.pdf,TRUE,params_output -outliers.only,FALSE,params_output -criterror,3,params_output -do.visual,TRUE,params_output -do.sibreport,FALSE,params_output -do.part2.pdf,TRUE,params_output -mvpathreshold,100,params_phyact -boutcriter,0.8,params_phyact -mvpadur,"c(1,5,10)",params_phyact -boutcriter.in,0.9,params_phyact -boutcriter.lig,0.8,params_phyact -boutcriter.mvpa,0.8,params_phyact -threshold.lig,40,params_phyact -threshold.mod,100,params_phyact -threshold.vig,400,params_phyact -boutdur.mvpa,"c(1,5,10)",params_phyact -boutdur.in,"c(10,20,30)",params_phyact -boutdur.lig,"c(1,5,10)",params_phyact -frag.metrics,c(),params_phyact -chunksize,1,params_rawdata -spherecrit,0.3,params_rawdata -minloadcrit,72,params_rawdata -printsummary,FALSE,params_rawdata -do.cal,TRUE,params_rawdata -backup.cal.coef,retrieve,params_rawdata -dynrange,c(),params_rawdata -minimumFileSizeMB,2,params_rawdata -rmc.dec,.,params_rawdata -rmc.firstrow.acc,c(),params_rawdata -rmc.firstrow.header,c(),params_rawdata -rmc.header.length,c(),params_rawdata -rmc.col.acc,"c(1,2,3)",params_rawdata -rmc.col.temp,c(),params_rawdata -rmc.col.time,c(),params_rawdata -rmc.unit.acc,g,params_rawdata -rmc.unit.temp,C,params_rawdata -rmc.unit.time,POSIX,params_rawdata -rmc.format.time,%Y-%m-%d %H:%M:%OS,params_rawdata -rmc.bitrate,c(),params_rawdata -rmc.dynamic_range,c(),params_rawdata -rmc.unsignedbit,TRUE,params_rawdata -rmc.origin,01/01/1970,params_rawdata -rmc.desiredtz,,params_rawdata -rmc.configtz,c(),params_rawdata -rmc.sf,c(),params_rawdata -rmc.headername.sf,c(),params_rawdata -rmc.headername.sn,c(),params_rawdata -rmc.headername.recordingid,c(),params_rawdata -rmc.header.structure,c(),params_rawdata -rmc.check4timegaps,FALSE,params_rawdata -rmc.noise,13,params_rawdata -rmc.col.wear,c(),params_rawdata -rmc.doresample,FALSE,params_rawdata -interpolationType,1,params_rawdata -imputeTimegaps,TRUE,params_rawdata -anglethreshold,5,params_sleep -timethreshold,5,params_sleep -ignorenonwear,TRUE,params_sleep -constrain2range,TRUE,params_sleep -HASPT.algo,HDCZA,params_sleep -HASIB.algo,vanHees2015,params_sleep -Sadeh_axis,,params_sleep -longitudinal_axis,c(),params_sleep -HASPT.ignore.invalid,FALSE,params_sleep -loglocation,c(),params_sleep -colid,1,params_sleep -coln1,2,params_sleep -nnights,c(),params_sleep -relyonguider,FALSE,params_sleep -def.noc.sleep,1,params_sleep -sleeplogsep,",",params_sleep -sleepwindowType,SPT,params_sleep +"argument","value","context" +"datadir","c()","not applicable" +"do.report","c(2,4,5)","not applicable" +"f0","1","not applicable" +"f1","1","not applicable" +"mode","c(1,2,3,4,5)","not applicable" +"outputdir","./","not applicable" +"studyname","c()","not applicable" +"GGIRread_version","0.2.6","not applicable" +"GGIRversion","2.9.0","not applicable" +"R_version","R version 4.2.2 (2022-10-31 ucrt)","not applicable" +"qwindow","c(0,24)","params_247" +"qlevels","c()","params_247" +"qwindow_dateformat","%d-%m-%Y","params_247" +"ilevels","c()","params_247" +"IVIS_windowsize_minutes","60","params_247" +"IVIS_epochsize_seconds","c()","params_247" +"IVIS.activity.metric","2","params_247" +"IVIS_acc_threshold","20","params_247" +"qM5L5","c()","params_247" +"MX.ig.min.dur","10","params_247" +"M5L5res","10","params_247" +"winhr","5","params_247" +"iglevels","c()","params_247" +"LUXthresholds","c(0,100,500,1000,3000,5000,10000)","params_247" +"LUX_cal_constant","c()","params_247" +"LUX_cal_exponent","c()","params_247" +"LUX_day_segments","c()","params_247" +"window.summary.size","10","params_247" +"L5M5window","c(0,24)","params_247" +"cosinor","FALSE","params_247" +"includedaycrit","16","params_cleaning" +"ndayswindow","7","params_cleaning" +"strategy","1","params_cleaning" +"maxdur","0","params_cleaning" +"hrs.del.start","0","params_cleaning" +"hrs.del.end","0","params_cleaning" +"includedaycrit.part5","0.666666667","params_cleaning" +"excludefirstlast.part5","FALSE","params_cleaning" +"TimeSegments2ZeroFile","c()","params_cleaning" +"do.imp","TRUE","params_cleaning" +"data_cleaning_file","c()","params_cleaning" +"minimum_MM_length.part5","23","params_cleaning" +"excludefirstlast","FALSE","params_cleaning" +"includenightcrit","16","params_cleaning" +"excludefirst.part4","FALSE","params_cleaning" +"excludelast.part4","FALSE","params_cleaning" +"max_calendar_days","0","params_cleaning" +"nonWearEdgeCorrection","TRUE","params_cleaning" +"overwrite","FALSE","params_general" +"acc.metric","ENMO","params_general" +"maxNcores","c()","params_general" +"print.filename","FALSE","params_general" +"do.parallel","TRUE","params_general" +"windowsizes","c(5,900,3600)","params_general" +"desiredtz","","params_general" +"configtz","","params_general" +"idloc","3","params_general" +"dayborder","0","params_general" +"part5_agg2_60seconds","FALSE","params_general" +"sensor.location","wrist","params_general" +"expand_tail_max_hours","c()","params_general" +"recordingEndSleepHour","c()","params_general" +"do.anglex","FALSE","params_metrics" +"do.angley","FALSE","params_metrics" +"do.anglez","TRUE","params_metrics" +"do.zcx","FALSE","params_metrics" +"do.zcy","FALSE","params_metrics" +"do.zcz","FALSE","params_metrics" +"do.enmo","TRUE","params_metrics" +"do.lfenmo","FALSE","params_metrics" +"do.en","FALSE","params_metrics" +"do.mad","FALSE","params_metrics" +"do.enmoa","FALSE","params_metrics" +"do.roll_med_acc_x","FALSE","params_metrics" +"do.roll_med_acc_y","FALSE","params_metrics" +"do.roll_med_acc_z","FALSE","params_metrics" +"do.dev_roll_med_acc_x","FALSE","params_metrics" +"do.dev_roll_med_acc_y","FALSE","params_metrics" +"do.dev_roll_med_acc_z","FALSE","params_metrics" +"do.bfen","FALSE","params_metrics" +"do.hfen","FALSE","params_metrics" +"do.hfenplus","FALSE","params_metrics" +"do.lfen","FALSE","params_metrics" +"do.lfx","FALSE","params_metrics" +"do.lfy","FALSE","params_metrics" +"do.lfz","FALSE","params_metrics" +"do.hfx","FALSE","params_metrics" +"do.hfy","FALSE","params_metrics" +"do.hfz","FALSE","params_metrics" +"do.bfx","FALSE","params_metrics" +"do.bfy","FALSE","params_metrics" +"do.bfz","FALSE","params_metrics" +"do.brondcounts","FALSE","params_metrics" +"do.neishabouricounts","FALSE","params_metrics" +"hb","15","params_metrics" +"lb","0.2","params_metrics" +"n","4","params_metrics" +"zc.lb","0.25","params_metrics" +"zc.hb","3","params_metrics" +"zc.sb","0.01","params_metrics" +"zc.order","2","params_metrics" +"zc.scale","1","params_metrics" +"actilife_LFE","FALSE","params_metrics" +"epochvalues2csv","FALSE","params_output" +"save_ms5rawlevels","FALSE","params_output" +"save_ms5raw_format","csv","params_output" +"save_ms5raw_without_invalid","TRUE","params_output" +"storefolderstructure","FALSE","params_output" +"timewindow","c(MM,WW)","params_output" +"viewingwindow","1","params_output" +"dofirstpage","TRUE","params_output" +"visualreport","TRUE","params_output" +"week_weekend_aggregate.part5","FALSE","params_output" +"do.part3.pdf","TRUE","params_output" +"outliers.only","FALSE","params_output" +"criterror","3","params_output" +"do.visual","TRUE","params_output" +"do.sibreport","FALSE","params_output" +"do.part2.pdf","TRUE","params_output" +"mvpathreshold","100","params_phyact" +"boutcriter","0.8","params_phyact" +"mvpadur","c(1,5,10)","params_phyact" +"boutcriter.in","0.9","params_phyact" +"boutcriter.lig","0.8","params_phyact" +"boutcriter.mvpa","0.8","params_phyact" +"threshold.lig","40","params_phyact" +"threshold.mod","100","params_phyact" +"threshold.vig","400","params_phyact" +"boutdur.mvpa","c(1,5,10)","params_phyact" +"boutdur.in","c(10,20,30)","params_phyact" +"boutdur.lig","c(1,5,10)","params_phyact" +"frag.metrics","c()","params_phyact" +"chunksize","1","params_rawdata" +"spherecrit","0.3","params_rawdata" +"minloadcrit","72","params_rawdata" +"printsummary","FALSE","params_rawdata" +"do.cal","TRUE","params_rawdata" +"backup.cal.coef","retrieve","params_rawdata" +"dynrange","c()","params_rawdata" +"minimumFileSizeMB","2","params_rawdata" +"rmc.dec",".","params_rawdata" +"rmc.firstrow.acc","c()","params_rawdata" +"rmc.firstrow.header","c()","params_rawdata" +"rmc.header.length","c()","params_rawdata" +"rmc.col.acc","c(1,2,3)","params_rawdata" +"rmc.col.temp","c()","params_rawdata" +"rmc.col.time","c()","params_rawdata" +"rmc.unit.acc","g","params_rawdata" +"rmc.unit.temp","C","params_rawdata" +"rmc.unit.time","POSIX","params_rawdata" +"rmc.format.time","%Y-%m-%d %H:%M:%OS","params_rawdata" +"rmc.bitrate","c()","params_rawdata" +"rmc.dynamic_range","c()","params_rawdata" +"rmc.unsignedbit","TRUE","params_rawdata" +"rmc.origin","01/01/1970","params_rawdata" +"rmc.desiredtz","","params_rawdata" +"rmc.configtz","c()","params_rawdata" +"rmc.sf","c()","params_rawdata" +"rmc.headername.sf","c()","params_rawdata" +"rmc.headername.sn","c()","params_rawdata" +"rmc.headername.recordingid","c()","params_rawdata" +"rmc.header.structure","c()","params_rawdata" +"rmc.check4timegaps","FALSE","params_rawdata" +"rmc.noise","13","params_rawdata" +"rmc.col.wear","c()","params_rawdata" +"rmc.doresample","FALSE","params_rawdata" +"interpolationType","1","params_rawdata" +"imputeTimegaps","TRUE","params_rawdata" +"anglethreshold","5","params_sleep" +"timethreshold","5","params_sleep" +"ignorenonwear","TRUE","params_sleep" +"constrain2range","TRUE","params_sleep" +"HASPT.algo","HDCZA","params_sleep" +"HASIB.algo","vanHees2015","params_sleep" +"Sadeh_axis","","params_sleep" +"longitudinal_axis","c()","params_sleep" +"HASPT.ignore.invalid","FALSE","params_sleep" +"loglocation","c()","params_sleep" +"colid","1","params_sleep" +"coln1","2","params_sleep" +"nnights","c()","params_sleep" +"relyonguider","FALSE","params_sleep" +"def.noc.sleep","1","params_sleep" +"sleeplogsep",",","params_sleep" +"sleepwindowType","SPT","params_sleep" diff --git a/inst/testfiles_hbGPS/params_description_hbGPS.tsv b/inst/testfiles_hbGPS/params_description_hbGPS.tsv new file mode 100644 index 0000000..95333c4 --- /dev/null +++ b/inst/testfiles_hbGPS/params_description_hbGPS.tsv @@ -0,0 +1,11 @@ +parameter field subfield display class minimum maximum set priority description +idloc general TRUE set 2;6 1 +maxBreakLengthSeconds trip TRUE integer 30 300 0 "How participant identifier should be extracted: idloc=2 looks at the filename and extracts the character string preceding the first occurance of a '_', idloc = 6 '.' (dot)" +minTripDur trip TRUE integer 30 900 0 Minimum trip duration seconds +minTripDist_m trip TRUE integer 10 1000 0 Minimum trip distance in meters +threshold_snr noise TRUE integer 50 1000 0 Threshold for snr +threshold_snr_ratio noise TRUE integer 60 1000 0 Threshold for snr ratio +tz general TRUE timezone 1 "Timezone in which experiments took place according to the timezone database (https://en.wikipedia.org/wiki/Zone.tab). If empty, then local timezone where the app is run will be used." +time_format general TRUE timeformat 1 "Timestamp format in R code, e.g. %Y-%m-%d %H:%M:%S, see also https://www.stat.berkeley.edu/~s133/dates.html" +outputFormat general TRUE set default;PALMS 1 Control the output modality of hbGPS +AccThresholds accelerometer TRUE double 0 1000000 0 Acceleration thresholds corresponding to the intensity levels diff --git a/inst/testfiles_hbgps/config_hbgps.csv b/inst/testfiles_hbgps/config_hbgps.csv index f61b808..0990567 100644 --- a/inst/testfiles_hbgps/config_hbgps.csv +++ b/inst/testfiles_hbgps/config_hbgps.csv @@ -1,11 +1,11 @@ argument,value,context idloc,2,general -maxBreakLengthSeconds,120,general -minTripDur,60,general -minTripDist_m,100,general -threshold_snr,225,general -threshold_snr_ratio,50,general +maxBreakLengthSeconds,120,trip +minTripDur,60,trip +minTripDist_m,100,trip +threshold_snr,225,noise +threshold_snr_ratio,50,noise tz,,general time_format,%d/%m/%Y %H:%M:%SO,general outputFormat,default,general -AccThresholds,,general +AccThresholds,,accelerometer diff --git a/inst/testfiles_palmsplusr/config_palmsplusr.csv b/inst/testfiles_palmsplusr/config_palmsplusr.csv index 8783061..32a1d04 100755 --- a/inst/testfiles_palmsplusr/config_palmsplusr.csv +++ b/inst/testfiles_palmsplusr/config_palmsplusr.csv @@ -1,47 +1,47 @@ -context,name,formula,domain_field,after_conversion -palmsplus_field,weekday,dow < 6,FALSE,NA -palmsplus_field,weekend,dow > 5,FALSE,NA -palmsplus_field,indoors,iov == 3,FALSE,NA -palmsplus_field,outdoors,iov == 1,FALSE,NA -palmsplus_field,in_vehicle,iov == 2,FALSE,NA -palmsplus_field,inserted,fixtypecode == 6,FALSE,NA -palmsplus_field,pedestrian,tripmot == 1,FALSE,NA -palmsplus_field,bicycle,tripmot == 2,FALSE,NA -palmsplus_field,vehicle,tripmot == 3,FALSE,NA -palmsplus_field,nonwear,activityintensity < 0,TRUE,NA -palmsplus_field,wear,activityintensity >= 0,TRUE,NA -palmsplus_field,sedentary,activityintensity == 0,TRUE,NA -palmsplus_field,light,activityintensity == 1,TRUE,NA -palmsplus_field,moderate,activityintensity == 2,TRUE,NA -palmsplus_field,vigorous,activityintensity == 3,TRUE,NA -palmsplus_field,mvpa,moderate + vigorous,TRUE,NA -trajectory_field,mot,first(tripmot),NA,FALSE -trajectory_field,date,first(as.Date(datetime)),NA,FALSE -trajectory_field,start,datetime[triptype==1],NA,FALSE -trajectory_field,end,datetime[triptype==4],NA,FALSE -trajectory_field,duration,"as.numeric(difftime(end, start, units = ""secs"") + 30)",NA,FALSE -trajectory_field,nonwear,sum(activityintensity < 0) * 15,NA,FALSE -trajectory_field,wear,sum(activityintensity >= 0) * 15,NA,FALSE -trajectory_field,sedentary,sum(activityintensity == 1) * 15,NA,FALSE -trajectory_field,light,sum(activityintensity == 1) * 15,NA,FALSE -trajectory_field,moderate,sum(activityintensity == 2) * 15,NA,FALSE -trajectory_field,vigorous,sum(activityintensity == 3) * 15,NA,FALSE -trajectory_field,mvpa,moderate + vigorous,NA,FALSE -trajectory_field,length,as.numeric(st_length(geometry)),NA,TRUE -trajectory_field,speed,(length / duration) * 3.6,NA,TRUE -multimodal_field,duration,sum,NA,NA -multimodal_field,nonwear,sum,NA,NA -multimodal_field,wear,sum,NA,NA -multimodal_field,sedentary,sum,NA,NA -multimodal_field,light,sum,NA,NA -multimodal_field,moderate,sum,NA,NA -multimodal_field,vigorous,sum,NA,NA -multimodal_field,mvpa,sum,NA,NA -multimodal_field,length,sum,NA,NA -multimodal_field,speed,mean,NA,NA -palmsplus_domain,home,at_home,TRUE,NA -palmsplus_domain,school,(!at_home & at_school),TRUE,NA -palmsplus_domain,transport,!at_home & !(at_school) & (pedestrian | bicycle | vehicle),TRUE,NA -palmsplus_domain,home_nbh,!at_home & !(at_school) & (!pedestrian & !bicycle & !vehicle) & at_home_nbh,TRUE,NA -palmsplus_domain,school_nbh,!at_home & !(at_school) & (!pedestrian & !bicycle & !vehicle) & !(at_home_nbh) & at_school_nbh,TRUE,NA -palmsplus_domain,other,!at_home & !(at_school) & (!pedestrian & !bicycle & !vehicle) & !(at_home_nbh) & !(at_school_nbh),TRUE,NA +"context","name","formula","domain_field","after_conversion" +"palmsplus_field","weekday","dow < 6",FALSE,NA +"palmsplus_field","weekend","dow > 5",FALSE,NA +"palmsplus_field","indoors","iov == 3",FALSE,NA +"palmsplus_field","outdoors","iov == 1",FALSE,NA +"palmsplus_field","in_vehicle","iov == 2",FALSE,NA +"palmsplus_field","inserted","fixtypecode == 6",FALSE,NA +"palmsplus_field","pedestrian","tripmot == 1",FALSE,NA +"palmsplus_field","bicycle","tripmot == 2",FALSE,NA +"palmsplus_field","vehicle","tripmot == 3",FALSE,NA +"palmsplus_field","nonwear","activityintensity < 0",TRUE,NA +"palmsplus_field","wear","activityintensity >= 0",TRUE,NA +"palmsplus_field","sedentary","activityintensity == 0",TRUE,NA +"palmsplus_field","light","activityintensity == 1",TRUE,NA +"palmsplus_field","moderate","activityintensity == 2",TRUE,NA +"palmsplus_field","vigorous","activityintensity == 3",TRUE,NA +"palmsplus_field","mvpa","moderate + vigorous",TRUE,NA +"trajectory_field","mot","first(tripmot)",NA,FALSE +"trajectory_field","date","first(as.Date(datetime))",NA,FALSE +"trajectory_field","start","datetime[triptype==1]",NA,FALSE +"trajectory_field","end","datetime[triptype==4]",NA,FALSE +"trajectory_field","duration","as.numeric(difftime(end, start, units = ""secs"") + 30)",NA,FALSE +"trajectory_field","nonwear","sum(activityintensity < 0) * 15",NA,FALSE +"trajectory_field","wear","sum(activityintensity >= 0) * 15",NA,FALSE +"trajectory_field","sedentary","sum(activityintensity == 1) * 15",NA,FALSE +"trajectory_field","light","sum(activityintensity == 1) * 15",NA,FALSE +"trajectory_field","moderate","sum(activityintensity == 2) * 15",NA,FALSE +"trajectory_field","vigorous","sum(activityintensity == 3) * 15",NA,FALSE +"trajectory_field","mvpa","moderate + vigorous",NA,FALSE +"trajectory_field","length","as.numeric(st_length(geometry))",NA,TRUE +"trajectory_field","speed","(length / duration) * 3.6",NA,TRUE +"multimodal_field","duration","sum",NA,NA +"multimodal_field","nonwear","sum",NA,NA +"multimodal_field","wear","sum",NA,NA +"multimodal_field","sedentary","sum",NA,NA +"multimodal_field","light","sum",NA,NA +"multimodal_field","moderate","sum",NA,NA +"multimodal_field","vigorous","sum",NA,NA +"multimodal_field","mvpa","sum",NA,NA +"multimodal_field","length","sum",NA,NA +"multimodal_field","speed","mean",NA,NA +"palmsplus_domain","home","at_home",TRUE,NA +"palmsplus_domain","school","(!at_home & at_school)",TRUE,NA +"palmsplus_domain","transport","!at_home & !(at_school) & (pedestrian | bicycle | vehicle)",TRUE,NA +"palmsplus_domain","home_nbh","!at_home & !(at_school) & (!pedestrian & !bicycle & !vehicle) & at_home_nbh",TRUE,NA +"palmsplus_domain","school_nbh","!at_home & !(at_school) & (!pedestrian & !bicycle & !vehicle) & !(at_home_nbh) & at_school_nbh",TRUE,NA +"palmsplus_domain","other","!at_home & !(at_school) & (!pedestrian & !bicycle & !vehicle) & !(at_home_nbh) & !(at_school_nbh)",TRUE,NA diff --git a/man/load_params.Rd b/man/load_params.Rd index d430e99..4cc0aa5 100644 --- a/man/load_params.Rd +++ b/man/load_params.Rd @@ -9,7 +9,7 @@ load_params(file = c(), format = "json_palmspy") \arguments{ \item{file}{Character to specify location of configuration file} -\item{format}{Character to specify format of configuration file: json_palsmpy, csv_GGIR, csv_palmsplusr} +\item{format}{Character to specify format of configuration file: json_palsmpy, csv_GGIR, csv_palmsplusr, csv_hbGPS} } \value{ list of parameters extract from the configuration file diff --git a/tests/testthat/test_check_params.R b/tests/testthat/test_check_params.R index 830bdf0..959cfb7 100644 --- a/tests/testthat/test_check_params.R +++ b/tests/testthat/test_check_params.R @@ -27,4 +27,25 @@ test_that("Parameters are checked", { " numeric
Error in parameter \" i \": Value 1,2,3 numeric", " vector needs to start with c( and end with ), with values", " separated by a comma
", collapse = "")) + #=================================== + # Check timeformat seperately + params = data.frame(value = c("%Y-%m-%d %H:%M:%S", "%Y-typo-%d %H:%M:%S"), + topic = rep("topic", 2), + description = rep("description", 2), + class = c("timeformat", "timeformat"), + minimum = rep(NA, 2), + maximum = rep(NA, 2), + set = c(NA, NA)) + rownames(params) = c("j", "k") + paramcheck = check_params(params, tool = "hbGPS") + + # Check that values are as expected + expect_equal(nrow(paramcheck$blocked_params), 1) + expect_equal(paramcheck$blocked_params$name, c("k")) + expect_equal(paramcheck$blocked_params$error, "is not a valid R time format specification.") + expect_equal(paramcheck$error_message, paste0("Error in parameter \" k \": Value", + " %Y-typo-%d %H:%M:%S is not a", + " valid R time format ", + "specification.
", collapse = "")) + }) \ No newline at end of file diff --git a/tests/testthat/test_load_and_update_params.R b/tests/testthat/test_load_and_update_params.R index a7e0a47..dd0d37e 100644 --- a/tests/testthat/test_load_and_update_params.R +++ b/tests/testthat/test_load_and_update_params.R @@ -12,12 +12,16 @@ test_that("Parameters can be loaded and updated from config files", { params_palmspy = load_params(file = palmspy_config_json, format = "json_palmspy") expect_equal(ncol(params_palmspy), 10) - # Load palmsplusr .csv file palmsplusr_config_csv = system.file("testfiles_palmsplusr/config_palmsplusr.csv", package = "HabitusGUI")[1] params_palmsplusr = load_params(file = palmsplusr_config_csv, format = "csv_palmsplusr") expect_equal(ncol(params_palmsplusr), 10) + # Load hbGPS .csv file + hbGPS_config_csv = system.file("testfiles_hbGPS/config_hbGPS.csv", package = "HabitusGUI")[1] + params_hbGPS = load_params(file = hbGPS_config_csv, format = "csv_hbGPS") + expect_equal(ncol(params_hbGPS), 9) + # Update PALMSpy .json file params_palmspy$value[which(rownames(params_palmspy) == "interval")] = "25" update_params(new_params = params_palmspy, file = palmspy_config_json, format = "json_palmspy") From b2e1fb6dad1e6f27ceb1f2581c1e4e70d0561085 Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Thu, 7 Sep 2023 15:37:12 +0200 Subject: [PATCH 07/30] integrating hbGPS on page 3 of the GUI #90 --- R/modConfigServer.R | 19 ++++++ R/myApp.R | 64 +++++++++++++++++-- .../params_description_hbGPS.tsv | 4 +- 3 files changed, 80 insertions(+), 7 deletions(-) diff --git a/R/modConfigServer.R b/R/modConfigServer.R index 74fd592..76adfd8 100644 --- a/R/modConfigServer.R +++ b/R/modConfigServer.R @@ -38,6 +38,14 @@ modConfigServer = function(id, tool, homedir = getwd()) { if (config_default != file) file.copy(config_default, file) }, contentType = "text/csv") + } else if (tool() == "hbGPS") { + output$download = downloadHandler( + filename = "config_hbGPS.csv", + content <- function(file) { + config_default = system.file("testfiles_palmsplusr/config_hbGPS.csv", package = "HabitusGUI")[1] + if (config_default != file) file.copy(config_default, file) + }, + contentType = "text/csv") } }) @@ -80,6 +88,8 @@ modConfigServer = function(id, tool, homedir = getwd()) { params = load_params(file = current_config, format = "csv_ggir") #$datapath } else if (tool() == "palmsplusr") { params = load_params(file = current_config, format = "csv_palmsplusr") #$datapath + } else if (tool() == "hbGPS") { + params = load_params(file = current_config, format = "csv_hbGPS") #$datapath } # if config file is loaded, then check params params_errors = check_params(params, tool = tool()) @@ -126,6 +136,8 @@ modConfigServer = function(id, tool, homedir = getwd()) { update_params(new_params = v$params, file = current_config, format = "csv_ggir") #$datapath } else if (tool() == "palmsplusr") { update_params(new_params = v$params, file = current_config, format = "csv_palmsplusr") #$datapath + } else if (tool() == "hbGPS") { + update_params(new_params = v$params, file = current_config, format = "csv_hbGPS") #$datapath } } }) @@ -142,6 +154,8 @@ modConfigServer = function(id, tool, homedir = getwd()) { update_params(new_params = v$params, file = current_config, format = "csv_ggir") #$datapath } else if (tool() == "palmsplusr") { update_params(new_params = v$params, file = current_config, format = "csv_palmsplusr") #$datapath + } else if (tool() == "hbGPS") { + update_params(new_params = v$params, file = current_config, format = "csv_hbGPS") #$datapath } # update list with errors params_errors = check_params(params, tool = tool()) @@ -193,6 +207,9 @@ modConfigServer = function(id, tool, homedir = getwd()) { } else if (tool() == "palmsplusr") { explanation = paste0("palmsplusr takes as input PALMSpy output, GIS shape, and a GISlinkage file ", "and uses these to describe behaviour per domain.") + } else if (tool() == "hbGPS") { + explanation = paste0("hbGPS takes as input GGIR output and GPS data and uses them to estimate", + " trips.") } explanation }) @@ -204,6 +221,8 @@ modConfigServer = function(id, tool, homedir = getwd()) { config_explanation2 = "PALMSpy configuration files are in .json. If you do not have one Download a template below." } else if (tool() == "palmsplusr") { config_explanation2 = "palmsplusr configuration files are in .csv. If you do not have one Download a template below." + } else if (tool() == "hbGPS") { + config_explanation2 = "hbGPS configuration files are in .csv. If you do not have one Download a template below." } config_explanation2 }) diff --git a/R/myApp.R b/R/myApp.R index 14331d5..6b7bc31 100644 --- a/R/myApp.R +++ b/R/myApp.R @@ -20,9 +20,11 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { stdout_GGIR_tmp <- tempfile(fileext = ".log") stdout_palmsplusr_tmp <- tempfile(fileext = ".log") + stdout_hbGPS_tmp <- tempfile(fileext = ".log") stdout_PALMSpy_tmp <- tempfile(fileext = ".log") mylog_GGIR <- shiny::reactiveFileReader(500, NULL, stdout_GGIR_tmp, readLines, warn = FALSE) mylog_palmsplusr <- shiny::reactiveFileReader(500, NULL, stdout_palmsplusr_tmp, readLines, warn = FALSE) + mylog_hbGPS <- shiny::reactiveFileReader(500, NULL, stdout_hbGPS_tmp, readLines, warn = FALSE) mylog_PALMSpy <- shiny::reactiveFileReader(500, NULL, stdout_PALMSpy_tmp, readLines, warn = FALSE) ui <- function() { @@ -55,7 +57,9 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { conditionalPanel(condition = paste0("input.availabledata.indexOf(`AccRaw`) > -1 || ", # GGIR "(input.availabledata.indexOf(`ACount`) > -1 && ", # PALMSpy "input.availabledata.indexOf(`GPS`) > -1) || ", - "(input.availabledata.indexOf(`GGIR_out`) > -1 && ", # hbGPS + "(input.availabledata.indexOf(`GGIR_out`) > -1 && ", # hbGPS variant 1 + "input.availabledata.indexOf(`GPS`) > -1) || ", + "(input.availabledata.indexOf(`AccRaw`) > -1 && ", # hbGPS variant 2 "input.availabledata.indexOf(`GPS`) > -1) || ", "(input.availabledata.indexOf(`ACount`) > -1 && ", # palmsplusr variant 1 "input.availabledata.indexOf(`GPS`) > -1 && ", @@ -138,6 +142,14 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { verbatimTextOutput("palmspyoutdir", placeholder = TRUE), hr() ), + # Select input folder GGIR output data ----------------------------------- + conditionalPanel(condition = paste0("input.availabledata.indexOf(`GGIR_out`) > -1 && ", + "input.tools.includes(`hbGPS`)"), + shinyFiles::shinyDirButton("ggiroutdir", label = "Previously generated GGIR output directory...", + title = "Select GGIR output directory"), + verbatimTextOutput("ggiroutdir", placeholder = TRUE), + hr() + ), # Select input folder hbGPS output data ----------------------------------- conditionalPanel(condition = paste0("input.availabledata.indexOf(`hbGPS_out`) > -1 && ", "input.tools.includes(`palmsplusr`) && !input.tools.includes(`hbGPS`)"), @@ -192,6 +204,11 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { modConfigUI("edit_palmspy_config"), hr() ), + conditionalPanel(condition = "input.tools.includes('hbGPS')", + h2("hbGPS"), + modConfigUI("edit_hbGPS_config"), + hr() + ), conditionalPanel(condition = "input.tools.includes('palmsplusr')", h2("palmsplusr"), modConfigUI("edit_palmsplusr_config"), @@ -304,6 +321,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), if (length(values$gisdir) < 2) selectedGisdir = c() else selectedGisdir = paste(values$gisdir$path, collapse = .Platform$file.sep) if (length(values$gislinkfile) < 2) selectedGislinkfile = c() else selectedGislinkfile = paste(values$gislinkfile$path, collapse = .Platform$file.sep) if (length(values$palmspyoutdir) < 2) selectedPalmspyoutdir = c() else selectedPalmspyoutdir = paste(values$palmspyoutdir$path, collapse = .Platform$file.sep) + if (length(values$ggiroutdir) < 2) selectedGgiroutdir = c() else selectedGgiroutdir = paste(values$ggiroutdir$path, collapse = .Platform$file.sep) if (length(values$hbgpsoutdir) < 2) selectedHbgpsoutdir = c() else selectedHbgpsoutdir = paste(values$hbgpsoutdir$path, collapse = .Platform$file.sep) if (length(values$sleepdiaryfile) < 2) selectedSleepdiaryfile = c() else selectedSleepdiaryfile = paste(values$sleepdiaryfile$path, collapse = .Platform$file.sep) if (length(values$outputdir) < 2) selectedOutputdir = c() else selectedOutputdir = paste(values$outputdir$path, collapse = .Platform$file.sep) @@ -367,8 +385,10 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), observeEvent(input$page_21, switch_page(1)) observeEvent(input$page_23, { # Previous selection of directories - prevPathNames = c("rawaccdir", "countaccdir", "sleepdiaryfile", "configfileGGIR", - "gpsdir", "gisdir", "gislinkfile", "palmspyoutdir", "hbgpsoutdir", "outputdir") + prevPathNames = c("rawaccdir", "countaccdir", "sleepdiaryfile", + "configfileGGIR", "configfilepalmsplusr", "configfilehbGPS", + "gpsdir", "gisdir", "gislinkfile", + "palmspyoutdir", "hbgpsoutdir", "ggiroutdir", "outputdir") prevPathNames = prevPathNames[which(prevPathNames %in% names(values))] prevPaths = values[prevPathNames] values_tmp = lapply(reactiveValuesToList(input), unclass) @@ -422,7 +442,11 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), if ("hbGPS_out" %in% input$availabledata & "palmsplusr" %in% input$tools & as.character(input$hbgpsoutdir)[1] == "0" & is.null(selectedHbgpsoutdir)) { showNotification("Select previously generated hbGPS output directory", type = "error") } else { - switch_page(3) + if ("GGIR_out" %in% input$availabledata & "hbGPS" %in% input$tools & as.character(input$ggiroutdir)[1] == "0" & is.null(selectedHbgpsoutdir)) { + showNotification("Select previously generated hbGPS output directory", type = "error") + } else { + switch_page(3) + } } } } @@ -436,7 +460,8 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), observeEvent(input$page_34, { # Previous selection of directories prevPathNames = c("rawaccdir", "countaccdir", "sleepdiaryfile", - "gpsdir", "gisdir", "gislinkfile", "palmspyoutdir", "hbgpsoutdir", "outputdir") + "gpsdir", "gisdir", "gislinkfile", + "palmspyoutdir", "hbgpsoutdir", "ggiroutdir", "outputdir") prevPathNames = prevPathNames[which(prevPathNames %in% names(values))] prevPaths = values[prevPathNames] values_tmp = lapply(reactiveValuesToList(input), unclass) @@ -471,6 +496,11 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), configs_ready = FALSE } } + if ("hbGPS" %in% input$tools) { + if (length(paste0(configfilehbGPS())) == 0) { + configs_ready = FALSE + } + } if ("palmsplusr" %in% input$tools) { if (length(paste0(configfilepalmsplusr())) == 0) { configs_ready = FALSE @@ -522,6 +552,13 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), values$configfilePALMSpy = copyFile(from = config_from, to = config_to) } } + if ("hbGPS" %in% input$tools) { + config_from = cleanPath(configfilehbGPS()) + config_to = cleanPath(paste0(global$data_out, "/config_hbGPS.csv")) + if (length(config_from) > 0) { + values$configfilehbGPS = copyFile(from = config_from, to = config_to) + } + } if ("palmsplusr" %in% input$tools) { config_from = cleanPath(configfilepalmsplusr()) config_to = cleanPath(paste0(global$data_out, "/config_palmsplusr.csv")) @@ -637,6 +674,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), shinyFileChoose(input, 'gislinkfile', roots = c(home = homedir)) shinyDirChoose(input, 'palmspyoutdir', roots = c(home = homedir)) # Allow for old output to be used as input shinyDirChoose(input, 'hbgpsoutdir', roots = c(home = homedir)) # Allow for old output to be used as input + shinyDirChoose(input, 'ggiroutdir', roots = c(home = homedir)) # Allow for old output to be used as input shinyDirChoose(input, 'outputdir', roots = c(home = homedir)) shinyFileChoose(input, 'sleepdiaryfile', roots = c(home = homedir)) @@ -649,6 +687,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), gislinkfile <- reactive(input$gislinkfile) palmspyoutdir <- reactive(input$palmspyoutdir) # Allow for old output to be used as input hbgpsoutdir <- reactive(input$hbgpsoutdir) # Allow for old output to be used as input + ggiroutdir <- reactive(input$ggiroutdir) # Allow for old output to be used as input outputdir <- reactive(input$outputdir) sleepdiaryfile <- reactive(input$sleepdiaryfile) #$datapath @@ -680,6 +719,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), gislinkfile_in = getPrevPath(dirname = "gislinkfile", ifEmpty = NULL, homedir = homedir, values) palmspyout_in = getPrevPath(dirname = "palmspyoutdir", ifEmpty = NULL, homedir = homedir, values) hbgpsout_in = getPrevPath(dirname = "hbgpsoutdir", ifEmpty = NULL, homedir = homedir, values) + ggirout_in = getPrevPath(dirname = "ggiroutdir", ifEmpty = NULL, homedir = homedir, values) sleepdiary_file = getPrevPath(dirname = "sleepdiaryfile", ifEmpty = NULL, homedir = homedir, values) } else { data_out = homedir @@ -782,6 +822,16 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), global$hbgpsout_in <- file.path(home, paste(unlist(hbgpsoutdir()$path[-1]), collapse = .Platform$file.sep)) }) + observeEvent(ignoreNULL = TRUE, + eventExpr = { + input$ggiroutdir # every time input$ggiroutdir updates ... + }, + handlerExpr = { # ... we re-assign global$ggirout_in + if (!"path" %in% names(ggiroutdir())) return() + home <- normalizePath(homedir) + global$ggirout_in <- + file.path(home, paste(unlist(ggiroutdir()$path[-1]), collapse = .Platform$file.sep)) + }) observeEvent(ignoreNULL = TRUE, eventExpr = { input$outputdir # every time input$outputdir updates ... @@ -826,6 +876,9 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), output$hbgpsoutdir <- renderText({ global$hbgpsout_in }) + output$ggiroutdir <- renderText({ + global$ggirout_in + }) output$outputdir <- renderText({ global$data_out }) @@ -837,6 +890,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), # Check and Edit config files --------------------------------------- configfilePALMSpy <- modConfigServer("edit_palmspy_config", tool = reactive("PALMSpy"), homedir = homedir) configfileGGIR <- modConfigServer("edit_ggir_config", tool = reactive("GGIR"), homedir = homedir) + configfilehbGPS <- modConfigServer("edit_hbGPS_config", tool = reactive("hbGPS"), homedir = homedir) configfilepalmsplusr <- modConfigServer("edit_palmsplusr_config", tool = reactive("palmsplusr"), homedir = homedir) diff --git a/inst/testfiles_hbGPS/params_description_hbGPS.tsv b/inst/testfiles_hbGPS/params_description_hbGPS.tsv index 95333c4..20bf110 100644 --- a/inst/testfiles_hbGPS/params_description_hbGPS.tsv +++ b/inst/testfiles_hbGPS/params_description_hbGPS.tsv @@ -3,8 +3,8 @@ idloc general TRUE set 2;6 1 maxBreakLengthSeconds trip TRUE integer 30 300 0 "How participant identifier should be extracted: idloc=2 looks at the filename and extracts the character string preceding the first occurance of a '_', idloc = 6 '.' (dot)" minTripDur trip TRUE integer 30 900 0 Minimum trip duration seconds minTripDist_m trip TRUE integer 10 1000 0 Minimum trip distance in meters -threshold_snr noise TRUE integer 50 1000 0 Threshold for snr -threshold_snr_ratio noise TRUE integer 60 1000 0 Threshold for snr ratio +threshold_snr noise TRUE integer 0 1000 0 Threshold for snr +threshold_snr_ratio noise TRUE integer 0 1000 0 Threshold for snr ratio tz general TRUE timezone 1 "Timezone in which experiments took place according to the timezone database (https://en.wikipedia.org/wiki/Zone.tab). If empty, then local timezone where the app is run will be used." time_format general TRUE timeformat 1 "Timestamp format in R code, e.g. %Y-%m-%d %H:%M:%S, see also https://www.stat.berkeley.edu/~s133/dates.html" outputFormat general TRUE set default;PALMS 1 Control the output modality of hbGPS From 304ebe1f4590032fc4a8baaf99ec6ae3dad70c29 Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Thu, 7 Sep 2023 16:07:02 +0200 Subject: [PATCH 08/30] improve alignment of prev/next/restart buttons --- R/myApp.R | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/R/myApp.R b/R/myApp.R index 6b7bc31..0ab4571 100644 --- a/R/myApp.R +++ b/R/myApp.R @@ -85,10 +85,14 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { "palmsplusr (R package)"), choiceValues = list("GGIR", "CountConverter", "PALMSpy", "hbGPS", "palmsplusr"), width = '100%') ), - actionButton("page_12", "next"), - actionButton("restart_1", "restart", - style = "position:absolute;right:1em;") - + hr(), + actionButton("page_12", "next", style = "position:absolute;right:1em;"), + p("\n"), + fluidRow( + column(1, offset = 0, + actionButton("restart_1", "restart", class = "btn-danger") + ) + ) ), tabPanel("page_2", fluidRow(column(8, div(h1("Habitus - Data selection"), style = "height:50px")), @@ -177,12 +181,15 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { conditionalPanel(condition = "input.availabledata.indexOf(`GIS`) > -1 && input.tools.includes(`palmsplusr`)", strong(textInput("dataset_name", label = "Give your dataset a name:", value = "", width = '100%')), ), - hr(), actionButton("page_21", "prev"), - actionButton("page_23", "next"), - actionButton("restart_2", "restart", - style = "position:absolute;right:1em;") + actionButton("page_23", "next", style = "position:absolute;right:1em;"), + p("\n"), + fluidRow( + column(1, offset = 0, + actionButton("restart_2", "restart", class = "btn-danger") + ) + ) ), tabPanel("page_3", fluidRow(column(8, div(h1("Habitus - Parameter Configuration"), style = "height:50px")), @@ -214,10 +221,15 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { modConfigUI("edit_palmsplusr_config"), hr() ), + hr(), actionButton("page_32", "prev"), - actionButton("page_34", "next"), - actionButton("restart_3", "restart", - style = "position:absolute;right:1em;") + actionButton("page_34", "next", style = "position:absolute;right:1em;"), + p("\n"), + fluidRow( + column(1, offset = 0, + actionButton("restart_3", "restart", class = "btn-danger") + ) + ) ), tabPanel("page_4", # Button to start analysis --------------------------------------------- @@ -280,8 +292,13 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), hr() ), actionButton("page_43", "prev"), - actionButton("restart_4", "restart", - style = "position:absolute;right:1em;") + p("\n"), + fluidRow( + column(1, offset = 0, + actionButton("restart_4", "restart", class = "btn-danger") + ) + ) + ) ) ) From 08c522f0a4ad2e1451a81d647741dd8842c32f04 Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Thu, 7 Sep 2023 17:04:25 +0200 Subject: [PATCH 09/30] rename checkFile to checkConfigFile and include hbGPS #90 --- NAMESPACE | 4 +- R/checkConfigFile.R | 50 +++++++++++++++++ R/checkFile.R | 69 ------------------------ R/modConfigServer.R | 2 +- man/{checkFile.Rd => checkConfigFile.Rd} | 8 +-- 5 files changed, 58 insertions(+), 75 deletions(-) create mode 100644 R/checkConfigFile.R delete mode 100644 R/checkFile.R rename man/{checkFile.Rd => checkConfigFile.Rd} (74%) diff --git a/NAMESPACE b/NAMESPACE index f9cbadc..686ccf1 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,12 +3,13 @@ export(Counts2csv) export(GGIRshiny) export(PALMSpyshiny) -export(checkFile) +export(checkConfigFile) export(check_and_clean_palms_data) export(check_params) export(cleanPath) export(create_test_GGIRconfig) export(create_test_files) +export(hbGPS_shiny) export(hbt_build_days) export(hbt_build_multimodal) export(hbt_build_palmsplus) @@ -24,6 +25,7 @@ export(update_params) exportClasses(toolio) import(GGIR) import(dplyr) +import(hbGPS) import(palmsplusr) import(sf) import(shiny) diff --git a/R/checkConfigFile.R b/R/checkConfigFile.R new file mode 100644 index 0000000..950c441 --- /dev/null +++ b/R/checkConfigFile.R @@ -0,0 +1,50 @@ +#' Function to check that the config files have the expected format +#' +#' @param file data path to config file +#' @param tool either PALMSpy, GGIR, or palmsplusr for now. +#' +#' @return message with the result of the check (either ok or the description of a problem) +#' @export +checkConfigFile = function(file=c(), tool=c()) { + # intialize check object, ok by default, it would be overwritten if + # any problem is identified in the config file. + check = "ok" + + # General check on file existence and extension + if (!file.exists(file)) { + # stop("No config file found at ", path) + check = "No config file found at " + } else { + path_unlist = unlist(strsplit(x = file, split = ".", fixed = TRUE)) + path_ext = tolower(path_unlist[length(path_unlist)]) + if (tool %in% c("GGIR", "hbGPS", "palmsplusr") & path_ext != "csv") { + check = "The GGIR config file uploaded is not a csv file" + } else if (tool == "PALMSpy" & path_ext != "json") { + check = "The GGIR config file uploaded is not a json file" + } + } + + # Specific file content check + if (check == "ok") { + if (tool == "PALMSpy") { + # TO BE DEVELOPED + } else if (tool == "GGIR" || tool == "hbGPS") { + # read config file if it exists and it is a csv file + params = read.csv(file = file) + # sanity check 2: colnames of config file ---- + if (ncol(params) == 3 && tool != "palmsplusr") { + check_colnames = all.equal(colnames(params), c("argument", "value", "context")) + } else if (ncol(params) == 5 && tool == "palmsplusr") { + check_colnames = all.equal(colnames(params), c("context", "name", "formula", "domain_field", "after_conversion")) + } else { + check_colnames = FALSE + } + if (check_colnames == FALSE) { + # this automatically also checks that csv is separated by commas, as + # it would generate just one column (e.g., "argument;value;context") otherwise + check = paste0("The csv file uploaded is not a ", tool, " config file") + } + } + } + return(check) +} \ No newline at end of file diff --git a/R/checkFile.R b/R/checkFile.R deleted file mode 100644 index ae0b6d2..0000000 --- a/R/checkFile.R +++ /dev/null @@ -1,69 +0,0 @@ -#' Function to check that the config files have the expected format -#' -#' @param file data path to config file -#' @param tool either PALMSpy, GGIR, or palmsplusr for now. -#' -#' @return message with the result of the check (either ok or the description of a problem) -#' @export -checkFile = function(file=c(), tool=c()) { - # intialize check object, ok by default, it would be overwritten if - # any problem is identified in the config file. - check = "ok" - - if (tool == "PALMSpy") { - # TO BE DEVELOPED - } else if (tool == "GGIR") { - # sanity check 1: is it a csv file? ---- - if (!file.exists(file)) { - # stop("No config file found at ", path) - check = "No config file found at " - } else { - path_unlist = unlist(strsplit(x = file, split = ".", fixed = TRUE)) - path_ext = path_unlist[length(path_unlist)] - if (path_ext != "csv") { - check = "The GGIR config file uploaded is not a csv file" - } else { - # read config file if it exists and it is a csv file - params = read.csv(file = file) - # sanity check 2: colnames of config file ---- - if (ncol(params) == 3) { - check_colnames = all.equal(colnames(params), c("argument", "value", "context")) - } else { - check_colnames = FALSE - } - if (check_colnames == FALSE) { - # this automatically also checks that csv is separated by commas, as - # it would generate just one column (e.g., "argument;value;context") otherwise - check = "The csv file uploaded is not a GGIR config file" - } - } - } - } else if (tool == "palmsplusr") { - # sanity check 1: is it a csv file? ---- - if (!file.exists(file)) { - # stop("No config file found at ", path) - check = "No config file found at " - } else { - path_unlist = unlist(strsplit(x = file, split = ".", fixed = TRUE)) - path_ext = path_unlist[length(path_unlist)] - if (path_ext != "csv") { - check = "The palmsplusr config file uploaded is not a csv file" - } else { - # read config file if it exists and it is a csv file - params = read.csv(file = file) - # sanity check 2: colnames of config file ---- - if (ncol(params) == 5) { - check_colnames = all.equal(colnames(params), c("context", "name", "formula", "domain_field", "after_conversion")) - } else { - check_colnames = FALSE - } - if (check_colnames == FALSE) { - # this automatically also checks that csv is separated by commas, as - # it would generate just one column (e.g., "argument;value;context") otherwise - check = "The csv file uploaded is not a palmsplusr config file" - } - } - } - } - return(check) -} \ No newline at end of file diff --git a/R/modConfigServer.R b/R/modConfigServer.R index 76adfd8..ed6782c 100644 --- a/R/modConfigServer.R +++ b/R/modConfigServer.R @@ -66,7 +66,7 @@ modConfigServer = function(id, tool, homedir = getwd()) { if (length(current_config) > 0) { # check config file - check = checkFile(file = current_config, tool = tool()) + check = checkConfigFile(file = current_config, tool = tool()) if (check != "ok") { # Show notification and keep waiting for correct config file diff --git a/man/checkFile.Rd b/man/checkConfigFile.Rd similarity index 74% rename from man/checkFile.Rd rename to man/checkConfigFile.Rd index fa4b470..e08aa84 100644 --- a/man/checkFile.Rd +++ b/man/checkConfigFile.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/checkFile.R -\name{checkFile} -\alias{checkFile} +% Please edit documentation in R/checkConfigFile.R +\name{checkConfigFile} +\alias{checkConfigFile} \title{Function to check that the config files have the expected format} \usage{ -checkFile(file = c(), tool = c()) +checkConfigFile(file = c(), tool = c()) } \arguments{ \item{file}{data path to config file} From a9696c2cc9d6ca3c5b2912e50ab8a7e4e09be99e Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Thu, 7 Sep 2023 17:06:53 +0200 Subject: [PATCH 10/30] add hbGPS to page 4 #90 create hbGPS_shiny function --- R/GGIRshiny.R | 2 +- R/hbGPS_shiny.R | 63 +++++++++++++++++++++++ R/myApp.R | 122 ++++++++++++++++++++++++++++++++++++++++++++- man/hbGPS_shiny.Rd | 31 ++++++++++++ 4 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 R/hbGPS_shiny.R create mode 100644 man/hbGPS_shiny.Rd diff --git a/R/GGIRshiny.R b/R/GGIRshiny.R index 988f930..74fea75 100644 --- a/R/GGIRshiny.R +++ b/R/GGIRshiny.R @@ -2,8 +2,8 @@ #' #' @param rawaccdir Path to input directory #' @param outputdir Path to output directory -#' @param configfile Configfile path #' @param sleepdiary Path to sleep diary +#' @param configfile Configfile path #' @param do.Counts Boolean to indicate whether BrondCounts should be derived #' @return no object is returned, only a new file is created in the output directory #' @import GGIR diff --git a/R/hbGPS_shiny.R b/R/hbGPS_shiny.R new file mode 100644 index 0000000..fcc0b06 --- /dev/null +++ b/R/hbGPS_shiny.R @@ -0,0 +1,63 @@ +#' hbGPS_shiny +#' +#' @param ggiroutdir GGIR time series output directory +#' @param gpsdir Path to GPS directory +#' @param outputdir Path to output directory +#' @param dataset_name Name of dataset +#' @param configfile Path to configuration file +#' @return no object is returned, only a new file is created in the output directory +#' @import hbGPS +#' @export + +hbGPS_shiny = function(ggiroutdir = NULL, gpsdir = NULL, outputdir = NULL, + dataset_name = "", configfile = c()) { + # create R script with the code to run the data analysis via a command line call + # in this way turning off or restarting the app will not kill the data analysis + fileConn <- file(paste0(outputdir, "/hbgps_cmdline.R")) + writeLines(c("#!/usr/bin/env Rscript", + "args = commandArgs(trailingOnly = TRUE)", + "if (length(args) < 4) {", + "stop(\"At least four arguments are expected\", call. = FALSE)", + "}", + "hbGPS::hbGPS(gps_file = args[1], ", + "GGIRpath = args[2],", + "outputDir = args[3],", + "configFile = args[4])", + ), + fileConn) + close(fileConn) + + if (.Platform$OS.type == "windows") { + logFile = paste0(outputdir, "/hbGPS.log") + fileConn = file(logFile) + writeLines(c("Hello, this Shiny app is primarily designed for Linux.", + "In Windows OS live progress of the analysis can be followed in the RStudio console.", + "In Unix-like systems the progress would be shown here inside this window in the Shiny app."), fileConn) + close(fileConn) + + basecommand = paste0(outputdir, "/hbgps_cmdline.R ", + gps_file, " ", + GGIRpath, " ", + outputdir, " ", + configfile) + system2(command = "Rscript", args = basecommand, + stdout = "", + stderr = "", wait = TRUE) + write.table(x = paste0(""), + file = paste0(outputdir, "/hbGPS.log")) + + fileConn = file(logFile) + writeLines(c(""), fileConn) + close(fileConn) + + } else { + basecommand = paste0("cd ", outputdir, " ; nohup Rscript hbgps_cmdline.R ", + gps_file, " ", + GGIRpath, " ", + outputdir, " ", + configfile, " > ", outputdir, "/hbGPS.log 2>&1 &") + system2(command = "cd", args = gsub(pattern = "cd ", replacement = "", x = basecommand), + stdout = "", stderr = "", wait = TRUE) + } + +} \ No newline at end of file diff --git a/R/myApp.R b/R/myApp.R index 0ab4571..4ee75da 100644 --- a/R/myApp.R +++ b/R/myApp.R @@ -277,6 +277,20 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), DT::dataTableOutput("PALMSpy_file1"), hr() ), + conditionalPanel(condition = "input.tools.includes('hbGPS')", + h3("hbGPS:"), + shinyjs::useShinyjs(), + actionButton("start_hbGPS", "Start analysis", width = '300px'), + p("\n"), + verbatimTextOutput("mylog_hbGPS"), + tags$head(tags$style("#mylog_hbGPS{color:darkblue; font-size:12px; font-style:italic; +overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), + p("\n"), + htmlOutput("hbGPS_end_message"), + p("\n"), + DT::dataTableOutput("hbGPS_file1"), + hr() + ), conditionalPanel(condition = "input.tools.includes('palmsplusr')", h3("palmsplusr:"), shinyjs::useShinyjs(), @@ -1127,6 +1141,112 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), } return() }) + #======================================================================== + # Apply hbGPS after button is pressed + #======================================================================== + runhbGPS <- eventReactive(input$start_hbGPS, { + hbGPS_message = "" + if ("hbGPS" %in% input$tools) { + hbGPS_message = "Error: Contact maintainer" + # Basic check before running function: + ready_to_run_hbGPS = FALSE + # Check for GGIR output (two possible sources either from this run or from a previous run) + if (dir.exists(global$ggirout_in)) { + Nfiles_in_dir = length(dir(path = expected_ggir_results_dir, pattern = "csv", recursive = FALSE, full.names = FALSE)) + if (Nfiles_in_dir > 0) { + # also check for GPS files + if (dir.exists(global$gps_in)) { + Nfiles_in_gpsdir = length(dir(path = global$gps_in, recursive = FALSE, full.names = FALSE)) + if (Nfiles_in_gpsdir == 0) { + hbGPS_message = paste0("No files found in GPS folder: ", global$gps_in) + } else { + ready_to_run_hbGPS = TRUE + } + } else { + hbGPS_message = paste0("Folder that is supposed to hold GPS files does not exist: ", global$gps_in) + } + } else { + hbGPS_message = paste0("No files found in GGIR output folder: ", global$ggirout_in) + } + } else { + hbGPS_message = paste0("Folder that is supposed to hold GGIR output files does not exist: ", global$ggirout_in) + } + # Only run function when checks are met: + if (ready_to_run_hbGPS == TRUE) { + shinyjs::hide(id = "start_hbGPS") + id_palmsplusr = showNotification("hbGPS in progress ...", type = "message", duration = NULL, closeButton = FALSE) + + write.table(x = NULL, file = stdout_hbGPS_tmp) # initialise empty file + output$mylog_hbGPS <- renderText({ + paste(mylog_hbGPS(), collapse = '\n') + }) + + # If process somehow unexpectedly terminates, always copy tmp log + # file to actual log file for user to see + logfile = paste0(isolate(global$data_out), "/hbGPS.log") + on.exit(file.copy(from = stdout_hbGPS_tmp, to = logfile, overwrite = TRUE), add = TRUE) + + # Start hbGPS + x_hbGPS <- r_bg(func = function(hbGPS_shiny, ggiroutdir, gpsdir, + outputdir, dataset_name, + configfile){ + hbGPS_shiny(ggiroutdir, gpsdir, + outputdir, dataset_name, + configfile) + }, + args = list(hbGPS_shiny = hbGPS_shiny, + ggiroutdir = global$ggirout_in, + gpsdir = global$gps_in, + outputdir = isolate(global$data_out), + dataset_name = input$dataset_name, + configfile = paste0(global$data_out, "/config_hbGPS.csv")), + stdout = stdout_hbGPS_tmp, + stderr = "2>&1") + + observe({ + if (x_hbGPS$poll_io(0)[["process"]] != "ready") { + invalidateLater(5000) + } else { + on.exit(removeNotification(id_hbGPS), add = TRUE) + # When process is finished copy tmp log file to actual log file for user to see + if (file.exists(stdout_hbGPS_tmp)) { + file.copy(from = stdout_hbGPS_tmp, to = logfile, overwrite = TRUE) + } + # Now check whether results are correctly generated: + expected_hbGPS_folder = paste0(isolate(global$data_out), "/hbGPS_output") + if (dir.exists(expected_hbGPS_folder) == TRUE) { + csv_files_hbGPS = dir(expected_hbGPS_folder, pattern = "csv", recursive = TRUE, full.names = TRUE) + if (length(csv_files_hbGPS) > 0) { + palmsplusr_message = paste0( + "Output is stored in: ", expected_hbGPS_folder, + paste0("
The table below shows the content of ", basename(csv_files_hbGPS)[1]), + "
Log file: ", logfile) + Sys.sleep(3) + hbGPS_file1 = read.csv(file = csv_files_hbGPS[1]) + if (length(hbGPS_file1) > 0) { + output$hbGPS_file1 <- DT::renderDataTable(hbGPS_file1, options = list(scrollX = TRUE)) + } + } else { + hbGPS_message = paste0("hbGPS unsuccessful", + "
No file found inside: ", expected_hbGPS_folder, + "
Log file: ", logfile) + } + } else { + hbGPS_message = paste0("hbGPS unsuccessful", + "
No file found inside: ", expected_hbGPS_folder, + "
Log file: ", logfile) + } + output$hbGPS_end_message <- renderUI({ + HTML(paste0(hbGPS_message)) + }) + } + }) + } + } + return() + }) + + #======================================================================== # Apply palmsplusr after button is pressed #======================================================================== @@ -1172,7 +1292,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), shinyjs::hide(id = "start_palmsplusr") id_palmsplusr = showNotification("palmsplusr in progress ...", type = "message", duration = NULL, closeButton = FALSE) - write.table(x = NULL, file = stdout_PALMSpy_tmp) # initialise empty file + write.table(x = NULL, file = stdout_palmsplusr_tmp) # initialise empty file output$mylog_palmsplusr <- renderText({ paste(mylog_palmsplusr(), collapse = '\n') }) diff --git a/man/hbGPS_shiny.Rd b/man/hbGPS_shiny.Rd new file mode 100644 index 0000000..51ab46a --- /dev/null +++ b/man/hbGPS_shiny.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/hbGPS_shiny.R +\name{hbGPS_shiny} +\alias{hbGPS_shiny} +\title{hbGPS_shiny} +\usage{ +hbGPS_shiny( + ggiroutdir = NULL, + gpsdir = NULL, + outputdir = NULL, + dataset_name = "", + configfile = c() +) +} +\arguments{ +\item{ggiroutdir}{GGIR time series output directory} + +\item{gpsdir}{Path to GPS directory} + +\item{outputdir}{Path to output directory} + +\item{dataset_name}{Name of dataset} + +\item{configfile}{Path to configuration file} +} +\value{ +no object is returned, only a new file is created in the output directory +} +\description{ +hbGPS_shiny +} From c9e85b8304b2c318f8595df0d5c082979c73b0fb Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Thu, 7 Sep 2023 17:10:17 +0200 Subject: [PATCH 11/30] minor edit to name of button on last page to clarify that this is not about analysis --- R/myApp.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/myApp.R b/R/myApp.R index 4ee75da..5142ea1 100644 --- a/R/myApp.R +++ b/R/myApp.R @@ -309,7 +309,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), p("\n"), fluidRow( column(1, offset = 0, - actionButton("restart_4", "restart", class = "btn-danger") + actionButton("restart_4", "restart app", class = "btn-danger") ) ) From 7badd55767f8d9143c00b20fdf3ac6c232ff4ea6 Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Thu, 7 Sep 2023 17:22:17 +0200 Subject: [PATCH 12/30] Minor message correction --- R/GGIRshiny.R | 2 +- R/hbGPS_shiny.R | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/GGIRshiny.R b/R/GGIRshiny.R index 74fea75..746360f 100644 --- a/R/GGIRshiny.R +++ b/R/GGIRshiny.R @@ -40,7 +40,7 @@ GGIRshiny = function(rawaccdir, outputdir, sleepdiary = c(), configfile = c(), if (.Platform$OS.type == "windows") { logFile = paste0(outputdir, "/GGIR.log") fileConn = file(logFile) - writeLines(c("Hello, this Shiny app is primarily designed for Linux.", + writeLines(c("Hello, this Shiny app is primarily designed for Unix.", "In Windows OS live progress of the analysis can be followed in the RStudio console.", "In Unix-like systems the progress would be shown here inside this window in the Shiny app."), fileConn) close(fileConn) diff --git a/R/hbGPS_shiny.R b/R/hbGPS_shiny.R index fcc0b06..7c0ff50 100644 --- a/R/hbGPS_shiny.R +++ b/R/hbGPS_shiny.R @@ -30,7 +30,7 @@ hbGPS_shiny = function(ggiroutdir = NULL, gpsdir = NULL, outputdir = NULL, if (.Platform$OS.type == "windows") { logFile = paste0(outputdir, "/hbGPS.log") fileConn = file(logFile) - writeLines(c("Hello, this Shiny app is primarily designed for Linux.", + writeLines(c("Hello, this Shiny app is primarily designed for Unix.", "In Windows OS live progress of the analysis can be followed in the RStudio console.", "In Unix-like systems the progress would be shown here inside this window in the Shiny app."), fileConn) close(fileConn) From 805f1d187161fd130e536a621d72e5c6c75141fe Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 8 Sep 2023 09:52:35 +0200 Subject: [PATCH 13/30] tidy up syntax --- R/modConfigUI.R | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/R/modConfigUI.R b/R/modConfigUI.R index d0d52b1..118c3b2 100644 --- a/R/modConfigUI.R +++ b/R/modConfigUI.R @@ -12,9 +12,6 @@ modConfigUI <- function(id) { tags$p(HTML(paste(textOutput(NS(id,"config_explanation1"))))), tags$hr(), tags$p(HTML(paste(textOutput(NS(id, "config_explanation2"))))), - # fileInput(NS(id, "configfile"), NULL, label = "", width = '100%', - # accept = c(".json", ".csv"), multiple = FALSE, - # buttonLabel = "File ..."), shinyFiles::shinyFilesButton(NS(id, "configfile"), label = "Configuration file...", title = "", multiple = FALSE, fileType = c(".json", ".csv")), shiny::downloadButton(NS(id, "download"), "Download template", class = "info-xs"), @@ -22,12 +19,12 @@ modConfigUI <- function(id) { tags$hr(), ), column(9, - span(h4(textOutput(NS(id, "config_instruction"))), style="color:purple"), + span(h4(textOutput(NS(id, "config_instruction"))), style = "color:purple"), DT::DTOutput(NS(id, "mod_table")), # This line has no function locally, but seems critical for the app to work on UCloud: shiny::dataTableOutput(NS(id, 'test_shinytable1')), - span(htmlOutput(NS(id, "config_issues")), style="color:red"), - span(htmlOutput(NS(id, "config_green")), style="color:green"), + span(htmlOutput(NS(id, "config_issues")), style = "color:red"), + span(htmlOutput(NS(id, "config_green")), style = "color:green"), ) ) } \ No newline at end of file From d7dcf90081051ca71146dd57789ebac9b13743b7 Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 8 Sep 2023 09:53:17 +0200 Subject: [PATCH 14/30] minor change in order of syntax --- R/modConfigServer.R | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/R/modConfigServer.R b/R/modConfigServer.R index ed6782c..c0c144f 100644 --- a/R/modConfigServer.R +++ b/R/modConfigServer.R @@ -30,19 +30,19 @@ modConfigServer = function(id, tool, homedir = getwd()) { if (config_default != file) file.copy(config_default, file) }, contentType = "text/csv") - } else if (tool() == "palmsplusr") { + } else if (tool() == "hbGPS") { output$download = downloadHandler( - filename = "config_palmsplusr.csv", + filename = "config_hbGPS.csv", content <- function(file) { - config_default = system.file("testfiles_palmsplusr/config_palmsplusr.csv", package = "HabitusGUI")[1] + config_default = system.file("testfiles_hbGPS/config_hbGPS.csv", package = "HabitusGUI")[1] if (config_default != file) file.copy(config_default, file) }, contentType = "text/csv") - } else if (tool() == "hbGPS") { + } else if (tool() == "palmsplusr") { output$download = downloadHandler( - filename = "config_hbGPS.csv", + filename = "config_palmsplusr.csv", content <- function(file) { - config_default = system.file("testfiles_palmsplusr/config_hbGPS.csv", package = "HabitusGUI")[1] + config_default = system.file("testfiles_palmsplusr/config_palmsplusr.csv", package = "HabitusGUI")[1] if (config_default != file) file.copy(config_default, file) }, contentType = "text/csv") From 7b82fc0afc846e72dc483489a69527b45e31b036 Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 8 Sep 2023 09:53:41 +0200 Subject: [PATCH 15/30] fix memorizing GGIR output path --- R/myApp.R | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/R/myApp.R b/R/myApp.R index 5142ea1..84d2f66 100644 --- a/R/myApp.R +++ b/R/myApp.R @@ -473,7 +473,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), if ("hbGPS_out" %in% input$availabledata & "palmsplusr" %in% input$tools & as.character(input$hbgpsoutdir)[1] == "0" & is.null(selectedHbgpsoutdir)) { showNotification("Select previously generated hbGPS output directory", type = "error") } else { - if ("GGIR_out" %in% input$availabledata & "hbGPS" %in% input$tools & as.character(input$ggiroutdir)[1] == "0" & is.null(selectedHbgpsoutdir)) { + if ("GGIR_out" %in% input$availabledata & "hbGPS" %in% input$tools & as.character(input$ggiroutdir)[1] == "0" & is.null(selectedGgiroutdir)) { showNotification("Select previously generated hbGPS output directory", type = "error") } else { switch_page(3) @@ -754,7 +754,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), sleepdiary_file = getPrevPath(dirname = "sleepdiaryfile", ifEmpty = NULL, homedir = homedir, values) } else { data_out = homedir - raw_acc_in = count_acc_in = gps_in = gis_in = gislinkfile_in = palmspyout_in = hbgpsout_in = sleepdiary_file = NULL + raw_acc_in = count_acc_in = gps_in = gis_in = gislinkfile_in = palmspyout_in = hbgpsout_in = sleepdiary_file = ggirout_in = NULL } global <- reactiveValues(data_out = cleanPath(data_out), raw_acc_in = cleanPath(raw_acc_in), @@ -763,6 +763,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), gis_in = cleanPath(gis_in), gislinkfile_in = cleanPath(gislinkfile_in), palmspyout_in = cleanPath(palmspyout_in), + ggirout_in = cleanPath(ggirout_in), hbgpsout_in = cleanPath(hbgpsout_in), sleepdiaryfile = cleanPath(sleepdiary_file)) #, pipeline = NULL) From ff2d428902f1406bd72580d0bd284283bc1f4b10 Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 8 Sep 2023 11:25:29 +0200 Subject: [PATCH 16/30] hbGPS now integrated, fixes #90 --- R/hbGPS_shiny.R | 11 +++--- R/myApp.R | 57 ++++++++++++++------------- R/update_params.R | 53 ++++++++++++++----------- inst/testfiles_hbgps/config_hbgps.csv | 10 ++--- 4 files changed, 70 insertions(+), 61 deletions(-) diff --git a/R/hbGPS_shiny.R b/R/hbGPS_shiny.R index 7c0ff50..09a52c1 100644 --- a/R/hbGPS_shiny.R +++ b/R/hbGPS_shiny.R @@ -22,11 +22,10 @@ hbGPS_shiny = function(ggiroutdir = NULL, gpsdir = NULL, outputdir = NULL, "hbGPS::hbGPS(gps_file = args[1], ", "GGIRpath = args[2],", "outputDir = args[3],", - "configFile = args[4])", + "configFile = args[4])" ), fileConn) close(fileConn) - if (.Platform$OS.type == "windows") { logFile = paste0(outputdir, "/hbGPS.log") fileConn = file(logFile) @@ -36,8 +35,8 @@ hbGPS_shiny = function(ggiroutdir = NULL, gpsdir = NULL, outputdir = NULL, close(fileConn) basecommand = paste0(outputdir, "/hbgps_cmdline.R ", - gps_file, " ", - GGIRpath, " ", + gpsdir, " ", + ggiroutdir, " ", outputdir, " ", configfile) system2(command = "Rscript", args = basecommand, @@ -52,8 +51,8 @@ hbGPS_shiny = function(ggiroutdir = NULL, gpsdir = NULL, outputdir = NULL, } else { basecommand = paste0("cd ", outputdir, " ; nohup Rscript hbgps_cmdline.R ", - gps_file, " ", - GGIRpath, " ", + gpsdir, " ", + ggiroutdir, " ", outputdir, " ", configfile, " > ", outputdir, "/hbGPS.log 2>&1 &") system2(command = "cd", args = gsub(pattern = "cd ", replacement = "", x = basecommand), diff --git a/R/myApp.R b/R/myApp.R index 84d2f66..bce33af 100644 --- a/R/myApp.R +++ b/R/myApp.R @@ -157,9 +157,9 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { # Select input folder hbGPS output data ----------------------------------- conditionalPanel(condition = paste0("input.availabledata.indexOf(`hbGPS_out`) > -1 && ", "input.tools.includes(`palmsplusr`) && !input.tools.includes(`hbGPS`)"), - shinyFiles::shinyDirButton("hbgpsoutdir", label = "Previously generated hbGPS output directory...", + shinyFiles::shinyDirButton("hbGPSoutdir", label = "Previously generated hbGPS output directory...", title = "Select hbGPS output directory"), - verbatimTextOutput("hbgpsoutdir", placeholder = TRUE), + verbatimTextOutput("hbGPSoutdir", placeholder = TRUE), hr() ), # Upload sleep diary ---------------------------------------------------- @@ -351,9 +351,9 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), if (length(values$gpsdir) < 2) selectedGpsdir = c() else selectedGpsdir = paste(values$gpsdir$path, collapse = .Platform$file.sep) if (length(values$gisdir) < 2) selectedGisdir = c() else selectedGisdir = paste(values$gisdir$path, collapse = .Platform$file.sep) if (length(values$gislinkfile) < 2) selectedGislinkfile = c() else selectedGislinkfile = paste(values$gislinkfile$path, collapse = .Platform$file.sep) - if (length(values$palmspyoutdir) < 2) selectedPalmspyoutdir = c() else selectedPalmspyoutdir = paste(values$palmspyoutdir$path, collapse = .Platform$file.sep) - if (length(values$ggiroutdir) < 2) selectedGgiroutdir = c() else selectedGgiroutdir = paste(values$ggiroutdir$path, collapse = .Platform$file.sep) - if (length(values$hbgpsoutdir) < 2) selectedHbgpsoutdir = c() else selectedHbgpsoutdir = paste(values$hbgpsoutdir$path, collapse = .Platform$file.sep) + if (length(values$palmspyoutdir) < 2) selected_PALMSpyoutdir = c() else selected_PALMSpyoutdir = paste(values$palmspyoutdir$path, collapse = .Platform$file.sep) + if (length(values$ggiroutdir) < 2) selected_GGIRoutdir = c() else selected_GGIRoutdir = paste(values$ggiroutdir$path, collapse = .Platform$file.sep) + if (length(values$hbGPSoutdir) < 2) selected_hbGPSoutdir = c() else selected_hbGPSoutdir = paste(values$hbGPSoutdir$path, collapse = .Platform$file.sep) if (length(values$sleepdiaryfile) < 2) selectedSleepdiaryfile = c() else selectedSleepdiaryfile = paste(values$sleepdiaryfile$path, collapse = .Platform$file.sep) if (length(values$outputdir) < 2) selectedOutputdir = c() else selectedOutputdir = paste(values$outputdir$path, collapse = .Platform$file.sep) @@ -419,7 +419,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), prevPathNames = c("rawaccdir", "countaccdir", "sleepdiaryfile", "configfileGGIR", "configfilepalmsplusr", "configfilehbGPS", "gpsdir", "gisdir", "gislinkfile", - "palmspyoutdir", "hbgpsoutdir", "ggiroutdir", "outputdir") + "palmspyoutdir", "hbGPSoutdir", "ggiroutdir", "outputdir") prevPathNames = prevPathNames[which(prevPathNames %in% names(values))] prevPaths = values[prevPathNames] values_tmp = lapply(reactiveValuesToList(input), unclass) @@ -467,13 +467,13 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), length(current_gislinkfile) == 0) & is.null(selectedGisdir)) { showNotification("Select GIS data directory and GIS linkage file", type = "error") } else { - if ("PALMSpy_out" %in% input$availabledata & "palmsplusr" %in% input$tools & as.character(input$palmspyoutdir)[1] == "0" & is.null(selectedPalmspyoutdir)) { + if ("PALMSpy_out" %in% input$availabledata & "palmsplusr" %in% input$tools & as.character(input$palmspyoutdir)[1] == "0" & is.null(selected_PALMSpyoutdir)) { showNotification("Select previously generated PALMS(py) output directory", type = "error") } else { - if ("hbGPS_out" %in% input$availabledata & "palmsplusr" %in% input$tools & as.character(input$hbgpsoutdir)[1] == "0" & is.null(selectedHbgpsoutdir)) { + if ("hbGPS_out" %in% input$availabledata & "palmsplusr" %in% input$tools & as.character(input$hbGPSoutdir)[1] == "0" & is.null(selected_hbGPSoutdir)) { showNotification("Select previously generated hbGPS output directory", type = "error") } else { - if ("GGIR_out" %in% input$availabledata & "hbGPS" %in% input$tools & as.character(input$ggiroutdir)[1] == "0" & is.null(selectedGgiroutdir)) { + if ("GGIR_out" %in% input$availabledata & "hbGPS" %in% input$tools & as.character(input$ggiroutdir)[1] == "0" & is.null(selected_GGIRoutdir)) { showNotification("Select previously generated hbGPS output directory", type = "error") } else { switch_page(3) @@ -492,7 +492,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), # Previous selection of directories prevPathNames = c("rawaccdir", "countaccdir", "sleepdiaryfile", "gpsdir", "gisdir", "gislinkfile", - "palmspyoutdir", "hbgpsoutdir", "ggiroutdir", "outputdir") + "palmspyoutdir", "hbGPSoutdir", "ggiroutdir", "outputdir") prevPathNames = prevPathNames[which(prevPathNames %in% names(values))] prevPaths = values[prevPathNames] values_tmp = lapply(reactiveValuesToList(input), unclass) @@ -539,7 +539,6 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), } if (configs_ready == TRUE) { showNotification("Saving configuration file(s) to output folder", type = "message", duration = 2) - copyFile = function(from, to) { # Copies configuration file to output folder if: # - from and to are not the same path @@ -563,7 +562,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), if (sleepdiaryfile() != 0) { diary_from = cleanPath(as.character(parseFilePaths(c(home = homedir), sleepdiaryfile())$datapath)) diary_to = cleanPath(paste0(global$data_out, "/sleepdiary.csv")) - if (length(config_from) > 0) { + if (length(diary_from) > 0) { sleepdiaryfile_local = copyFile(from = diary_from, to = diary_to) } else { sleepdiaryfile_local = c() @@ -704,7 +703,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), shinyDirChoose(input, 'gisdir', roots = c(home = homedir)) shinyFileChoose(input, 'gislinkfile', roots = c(home = homedir)) shinyDirChoose(input, 'palmspyoutdir', roots = c(home = homedir)) # Allow for old output to be used as input - shinyDirChoose(input, 'hbgpsoutdir', roots = c(home = homedir)) # Allow for old output to be used as input + shinyDirChoose(input, 'hbGPSoutdir', roots = c(home = homedir)) # Allow for old output to be used as input shinyDirChoose(input, 'ggiroutdir', roots = c(home = homedir)) # Allow for old output to be used as input shinyDirChoose(input, 'outputdir', roots = c(home = homedir)) shinyFileChoose(input, 'sleepdiaryfile', roots = c(home = homedir)) @@ -717,7 +716,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), gisdir <- reactive(input$gisdir) gislinkfile <- reactive(input$gislinkfile) palmspyoutdir <- reactive(input$palmspyoutdir) # Allow for old output to be used as input - hbgpsoutdir <- reactive(input$hbgpsoutdir) # Allow for old output to be used as input + hbGPSoutdir <- reactive(input$hbGPSoutdir) # Allow for old output to be used as input ggiroutdir <- reactive(input$ggiroutdir) # Allow for old output to be used as input outputdir <- reactive(input$outputdir) sleepdiaryfile <- reactive(input$sleepdiaryfile) #$datapath @@ -749,12 +748,12 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), gis_in = getPrevPath(dirname = "gisdir", ifEmpty = NULL, homedir = homedir, values) gislinkfile_in = getPrevPath(dirname = "gislinkfile", ifEmpty = NULL, homedir = homedir, values) palmspyout_in = getPrevPath(dirname = "palmspyoutdir", ifEmpty = NULL, homedir = homedir, values) - hbgpsout_in = getPrevPath(dirname = "hbgpsoutdir", ifEmpty = NULL, homedir = homedir, values) + hbGPSout_in = getPrevPath(dirname = "hbGPSoutdir", ifEmpty = NULL, homedir = homedir, values) ggirout_in = getPrevPath(dirname = "ggiroutdir", ifEmpty = NULL, homedir = homedir, values) sleepdiary_file = getPrevPath(dirname = "sleepdiaryfile", ifEmpty = NULL, homedir = homedir, values) } else { data_out = homedir - raw_acc_in = count_acc_in = gps_in = gis_in = gislinkfile_in = palmspyout_in = hbgpsout_in = sleepdiary_file = ggirout_in = NULL + raw_acc_in = count_acc_in = gps_in = gis_in = gislinkfile_in = palmspyout_in = hbGPSout_in = sleepdiary_file = ggirout_in = NULL } global <- reactiveValues(data_out = cleanPath(data_out), raw_acc_in = cleanPath(raw_acc_in), @@ -764,7 +763,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), gislinkfile_in = cleanPath(gislinkfile_in), palmspyout_in = cleanPath(palmspyout_in), ggirout_in = cleanPath(ggirout_in), - hbgpsout_in = cleanPath(hbgpsout_in), + hbGPSout_in = cleanPath(hbGPSout_in), sleepdiaryfile = cleanPath(sleepdiary_file)) #, pipeline = NULL) @@ -846,13 +845,13 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), }) observeEvent(ignoreNULL = TRUE, eventExpr = { - input$hbgpsoutdir # every time input$hbgpsoutdir updates ... + input$hbGPSoutdir # every time input$hbGPSoutdir updates ... }, - handlerExpr = { # ... we re-assign global$hbgpsout_in - if (!"path" %in% names(hbgpsoutdir())) return() + handlerExpr = { # ... we re-assign global$hbGPSout_in + if (!"path" %in% names(hbGPSoutdir())) return() home <- normalizePath(homedir) - global$hbgpsout_in <- - file.path(home, paste(unlist(hbgpsoutdir()$path[-1]), collapse = .Platform$file.sep)) + global$hbGPSout_in <- + file.path(home, paste(unlist(hbGPSoutdir()$path[-1]), collapse = .Platform$file.sep)) }) observeEvent(ignoreNULL = TRUE, eventExpr = { @@ -905,8 +904,8 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), output$palmspyoutdir <- renderText({ global$palmspyout_in }) - output$hbgpsoutdir <- renderText({ - global$hbgpsout_in + output$hbGPSoutdir <- renderText({ + global$hbGPSout_in }) output$ggiroutdir <- renderText({ global$ggirout_in @@ -1152,6 +1151,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), # Basic check before running function: ready_to_run_hbGPS = FALSE # Check for GGIR output (two possible sources either from this run or from a previous run) + expected_ggir_results_dir = global$ggirout_in if (dir.exists(global$ggirout_in)) { Nfiles_in_dir = length(dir(path = expected_ggir_results_dir, pattern = "csv", recursive = FALSE, full.names = FALSE)) if (Nfiles_in_dir > 0) { @@ -1175,7 +1175,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), # Only run function when checks are met: if (ready_to_run_hbGPS == TRUE) { shinyjs::hide(id = "start_hbGPS") - id_palmsplusr = showNotification("hbGPS in progress ...", type = "message", duration = NULL, closeButton = FALSE) + id_hbGPS = showNotification("hbGPS in progress ...", type = "message", duration = NULL, closeButton = FALSE) write.table(x = NULL, file = stdout_hbGPS_tmp) # initialise empty file output$mylog_hbGPS <- renderText({ @@ -1214,11 +1214,11 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), file.copy(from = stdout_hbGPS_tmp, to = logfile, overwrite = TRUE) } # Now check whether results are correctly generated: - expected_hbGPS_folder = paste0(isolate(global$data_out), "/hbGPS_output") + expected_hbGPS_folder = paste0(isolate(global$data_out), "/hbGPSoutput") if (dir.exists(expected_hbGPS_folder) == TRUE) { csv_files_hbGPS = dir(expected_hbGPS_folder, pattern = "csv", recursive = TRUE, full.names = TRUE) if (length(csv_files_hbGPS) > 0) { - palmsplusr_message = paste0( + hbGPS_message = paste0( "Output is stored in: ", expected_hbGPS_folder, paste0("
The table below shows the content of ", basename(csv_files_hbGPS)[1]), "
Log file: ", logfile) @@ -1407,6 +1407,9 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), output$palmspy_end_message <- renderText({ message = runPALMSpy() }) + output$hbGPS_end_message <- renderText({ + message = runhbGPS() + }) output$palmsplusr_end_message <- renderText({ message = runpalmsplusr() }) diff --git a/R/update_params.R b/R/update_params.R index face37f..414b9ea 100644 --- a/R/update_params.R +++ b/R/update_params.R @@ -1,7 +1,7 @@ #' update_params #' #' @param file Character to specify location of original configuration file -#' @param format Character to specify format of configuration file: json_palsmpy, csv_GGIR, or csv_palmsplusr +#' @param format Character to specify format of configuration file: json_palsmpy, csv_GGIR, csv_hbGPS or csv_palmsplusr #' @param new_params New parameters #' @return No object returned, function only reads original data, and overwrites parameters and stores it again #' @importFrom jsonlite fromJSON toJSON @@ -9,6 +9,29 @@ #' @export update_params = function(new_params = c(), file = c(), format="json_palmspy") { + + overwriteMatchingFields = function(params, new_params, format = "") { + # Remove duplicates + dups = duplicated(params$argument) + params = params[!dups,] + rownames(params) = params$argument + # Only overwrite the matching fields of csv file + for (j in 1:nrow(new_params)) { + ind = which(rownames(params) %in% rownames(new_params)[j] == TRUE) + if (length(ind) > 0) { + if (format == "csv_palmsplusr") { + if (new_params$value[j] != params$formula[ind]) { + params$formula[ind] = new_params$value[j] + } + } else { + params$value[ind] = new_params$value[j] + } + } + } + return(params) + } + + if (format == "json_palmspy") { config = fromJSON(txt = file, simplifyDataFrame = TRUE) if ("gps" %in% names(config) & "accelerometer" %in% names(config)) { @@ -38,15 +61,7 @@ update_params = function(new_params = c(), file = c(), format="json_palmspy") { write(exportJson, file = file) } else if (format == "csv_ggir") { params = read.csv(file = file) - # remove duplicates, because sometimes GGIR config files have duplicates - dups = duplicated(params$argument) - params = params[!dups,] - rownames(params) = params$argument - # only overwrite the matching fields - for (j in 1:nrow(new_params)) { - ind = which(rownames(params) %in% rownames(new_params)[j] == TRUE) - params$value[ind] = new_params$value[j] - } + params = overwriteMatchingFields(params, new_params, format) # match acc.metric with do.metric arguments do_metrics = c("do.zcx", "do.zcy", "do.zcz", "do.en", "do.enmo", "do.enmoa", @@ -68,22 +83,14 @@ update_params = function(new_params = c(), file = c(), format="json_palmspy") { params[do_argument, "value"] = TRUE params[do_metrics[-which(do_metrics == do_argument)], "value"] = FALSE write.csv(x = params, file = file, row.names = FALSE) + } else if (format == "csv_hbGPS") { + params = read.csv(file = file) + params = overwriteMatchingFields(params, new_params, format) + write.csv(x = params, file = file, row.names = FALSE) } else if (format == "csv_palmsplusr") { params = read.csv(file = file, sep = ",") - # remove duplicates, just in case palmsplusr config files have duplicates params$argument = with(params, paste0(params$context, "__",params$name)) - dups = duplicated(params$argument) - params = params[!dups,] - rownames(params) = params$argument - # only overwrite the matching fields - for (j in 1:nrow(new_params)) { - ind = which(rownames(params) %in% rownames(new_params)[j] == TRUE) - if (length(ind) > 0) { - if (new_params$value[j] != params$formula[ind]) { - params$formula[ind] = new_params$value[j] - } - } - } + params = overwriteMatchingFields(params, new_params, format) params = params[,-which(colnames(params) %in% c("argument"))] write.csv(x = params, file = file, row.names = FALSE) } diff --git a/inst/testfiles_hbgps/config_hbgps.csv b/inst/testfiles_hbgps/config_hbgps.csv index 0990567..50d4418 100644 --- a/inst/testfiles_hbgps/config_hbgps.csv +++ b/inst/testfiles_hbgps/config_hbgps.csv @@ -1,11 +1,11 @@ argument,value,context -idloc,2,general +idloc,6,general maxBreakLengthSeconds,120,trip minTripDur,60,trip minTripDist_m,100,trip threshold_snr,225,noise threshold_snr_ratio,50,noise -tz,,general -time_format,%d/%m/%Y %H:%M:%SO,general -outputFormat,default,general -AccThresholds,,accelerometer +tz,Europe/Brussels,general +time_format,%Y/%m/%d %H:%M:%S,general +outputFormat,PALMS,general +AccThresholds,"c(8.33, 208.33, 833.33, 1250)",accelerometer From ec173c9d47fbce0c336bee5e290b9a06c5c1ef66 Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 8 Sep 2023 12:16:48 +0200 Subject: [PATCH 17/30] log window now auto-scrolls to bottom --- R/myApp.R | 61 +++++++++++++++++++++++++++++++++++--------- man/update_params.Rd | 2 +- 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/R/myApp.R b/R/myApp.R index bce33af..3349f23 100644 --- a/R/myApp.R +++ b/R/myApp.R @@ -27,6 +27,20 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { mylog_hbGPS <- shiny::reactiveFileReader(500, NULL, stdout_hbGPS_tmp, readLines, warn = FALSE) mylog_PALMSpy <- shiny::reactiveFileReader(500, NULL, stdout_PALMSpy_tmp, readLines, warn = FALSE) + js_hbGPS <- paste0("$(document).on('shiny:value', function(evt){", + "if(evt.name == 'mylog_hbGPS'){", + " setTimeout(function(){", + " var objDiv = document.getElementById('mylog_hbGPS');", + " objDiv.scrollTop = objDiv.scrollHeight - objDiv.clientHeight;", + " }, 500);", + "}", + "});") + + js_GGIR <- sub(pattern = "hbGPS", replacement = "GGIR", x = js_hbGPS) + js_palmsplusr <- sub(pattern = "hbGPS", replacement = "palmsplusr", x = js_hbGPS) + js_PALMSpy <- sub(pattern = "hbGPS", replacement = "PALMSpy", x = js_hbGPS) + + ui <- function() { fluidPage( theme = bslib::bs_theme(bootswatch = "litera"), #,"sandstone"), "sketchy" "pulse" @@ -254,9 +268,12 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { shinyjs::useShinyjs(), actionButton("start_ggir", "Start analysis", width = '300px'), p("\n"), - verbatimTextOutput("mylog_GGIR"), - tags$head(tags$style("#mylog_GGIR{color:darkblue; font-size:12px; font-style:italic; -overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), + tags$head( + tags$script(HTML(js_GGIR)) + ), + tags$style(HTML("#mylog_GGIR {color:darkblue; font-size:12px; font-style:italic; +overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), + verbatimTextOutput("mylog_GGIR", placeholder = TRUE), p("\n"), htmlOutput("ggir_end_message"), p("\n"), @@ -268,9 +285,12 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), shinyjs::useShinyjs(), actionButton("start_palmspy", "Start analysis", width = '300px'), p("\n"), - verbatimTextOutput("mylog_PALMSpy"), - tags$head(tags$style("#mylog_PALMSpy{color:darkblue; font-size:12px; font-style:italic; -overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), + tags$head( + tags$script(HTML(js_PALMSpy)) + ), + tags$style(HTML("#mylog_PALMSpy {color:darkblue; font-size:12px; font-style:italic; +overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), + verbatimTextOutput("mylog_PALMSpy", placeholder = TRUE), p("\n"), htmlOutput("palmspy_end_message"), p("\n"), @@ -282,9 +302,17 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), shinyjs::useShinyjs(), actionButton("start_hbGPS", "Start analysis", width = '300px'), p("\n"), - verbatimTextOutput("mylog_hbGPS"), - tags$head(tags$style("#mylog_hbGPS{color:darkblue; font-size:12px; font-style:italic; -overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), + tags$head( + tags$script(HTML(js_hbGPS)) + ), + tags$style(HTML("#mylog_hbGPS {color:darkblue; font-size:12px; font-style:italic; +overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), + # actionbutton("hbGPS_showlog_button", "hide/show log"), + # hidden( + # div(id = "hbGPS_log_div", + verbatimTextOutput("mylog_hbGPS", placeholder = TRUE), + # ) + # ), p("\n"), htmlOutput("hbGPS_end_message"), p("\n"), @@ -296,9 +324,12 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), shinyjs::useShinyjs(), actionButton("start_palmsplusr", "Start analysis", width = '300px'), p("\n"), - verbatimTextOutput("mylog_palmsplusr"), - tags$head(tags$style("#mylog_palmsplusr{color:darkblue; font-size:12px; font-style:italic; -overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), + tags$head( + tags$script(HTML(js_palmsplusr)) + ), + tags$style(HTML("#mylog_palmsplusr {color:darkblue; font-size:12px; font-style:italic; +overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), + verbatimTextOutput("mylog_palmsplusr", placeholder = TRUE), p("\n"), htmlOutput("palmsplusr_end_message"), p("\n"), @@ -1178,6 +1209,12 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), id_hbGPS = showNotification("hbGPS in progress ...", type = "message", duration = NULL, closeButton = FALSE) write.table(x = NULL, file = stdout_hbGPS_tmp) # initialise empty file + # observeEvent(input$hbGPS_showlog_button, { + # toggle('hbGPS_log_div') + # output$mylog_hbGPS <- renderText({ + # paste(mylog_hbGPS(), collapse = '\n') + # }) + # }) output$mylog_hbGPS <- renderText({ paste(mylog_hbGPS(), collapse = '\n') }) diff --git a/man/update_params.Rd b/man/update_params.Rd index 70caf97..cc2e8de 100644 --- a/man/update_params.Rd +++ b/man/update_params.Rd @@ -11,7 +11,7 @@ update_params(new_params = c(), file = c(), format = "json_palmspy") \item{file}{Character to specify location of original configuration file} -\item{format}{Character to specify format of configuration file: json_palsmpy, csv_GGIR, or csv_palmsplusr} +\item{format}{Character to specify format of configuration file: json_palsmpy, csv_GGIR, csv_hbGPS or csv_palmsplusr} } \value{ No object returned, function only reads original data, and overwrites parameters and stores it again From e0f10d5d51384db548c4327632f8f0cac6ad05e2 Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 8 Sep 2023 12:40:51 +0200 Subject: [PATCH 18/30] add hbGPS to DESCRIPTION file and update function name in unittest --- DESCRIPTION | 2 +- tests/testthat/test_load_wrong_GGIRconfig.R | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 98934a2..ac01bf5 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -13,7 +13,7 @@ Authors@R: License: Apache License version 2.0 | file LICENSE Imports: shiny, shinyFiles, GGIR, bslib, methods, jsonlite, DT, dplyr, magrittr, shinyjs, sf, readr, tidyr, stringr, callr, palmsplusr, - data.table, rlang, purrr, geosphere + data.table, rlang, purrr, geosphere, hbGPS Remotes: vincentvanhees/palmsplusr LazyData: true Suggests: testthat, covr, rmarkdown diff --git a/tests/testthat/test_load_wrong_GGIRconfig.R b/tests/testthat/test_load_wrong_GGIRconfig.R index 80a61af..226e593 100644 --- a/tests/testthat/test_load_wrong_GGIRconfig.R +++ b/tests/testthat/test_load_wrong_GGIRconfig.R @@ -4,7 +4,7 @@ test_that("Wrong GGIR config files trigger a message and a sample config.csv fil # Load a json file as a GGIR .csv file ---- ggir_config_wrong_json = system.file("testfiles_palmspy/palmspy-params.json", package = "HabitusGUI")[1] - check = checkFile(file = ggir_config_wrong_json, tool = "GGIR") + check = checkConfigFile(file = ggir_config_wrong_json, tool = "GGIR") # test there is an error message expect_equal(length(check), 1) @@ -12,7 +12,7 @@ test_that("Wrong GGIR config files trigger a message and a sample config.csv fil # Load a csv file that is not a GGIR config file ---- ggir_config_wrong_csv = system.file("testfiles_palmsplusr/config_palmsplusr.csv", package = "HabitusGUI")[1] - check = checkFile(file = ggir_config_wrong_csv, tool = "GGIR") + check = checkConfigFile(file = ggir_config_wrong_csv, tool = "GGIR") # test there is an error message expect_equal(length(check), 1) From d137a079b988bb10339f098e37d8fceaf5f62dbb Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 8 Sep 2023 12:41:16 +0200 Subject: [PATCH 19/30] Allow for hiding progress log, fixes #46 --- R/myApp.R | 73 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/R/myApp.R b/R/myApp.R index 3349f23..0811038 100644 --- a/R/myApp.R +++ b/R/myApp.R @@ -273,7 +273,12 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { ), tags$style(HTML("#mylog_GGIR {color:darkblue; font-size:12px; font-style:italic; overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), - verbatimTextOutput("mylog_GGIR", placeholder = TRUE), + checkboxInput("GGIR_showlog", "hide log", value = FALSE), + shinyjs::hidden( + div(id = "GGIR_log_div", + verbatimTextOutput("mylog_GGIR", placeholder = TRUE) + ) + ), p("\n"), htmlOutput("ggir_end_message"), p("\n"), @@ -290,7 +295,12 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), ), tags$style(HTML("#mylog_PALMSpy {color:darkblue; font-size:12px; font-style:italic; overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), - verbatimTextOutput("mylog_PALMSpy", placeholder = TRUE), + checkboxInput("PALMSpy_showlog", "hide log", value = FALSE), + shinyjs::hidden( + div(id = "PALMSpy_log_div", + verbatimTextOutput("mylog_PALMSpy", placeholder = TRUE) + ) + ), p("\n"), htmlOutput("palmspy_end_message"), p("\n"), @@ -307,12 +317,12 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), ), tags$style(HTML("#mylog_hbGPS {color:darkblue; font-size:12px; font-style:italic; overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), - # actionbutton("hbGPS_showlog_button", "hide/show log"), - # hidden( - # div(id = "hbGPS_log_div", - verbatimTextOutput("mylog_hbGPS", placeholder = TRUE), - # ) - # ), + checkboxInput("hbGPS_showlog", "hide log", value = FALSE), + shinyjs::hidden( + div(id = "hbGPS_log_div", + verbatimTextOutput("mylog_hbGPS", placeholder = TRUE) + ) + ), p("\n"), htmlOutput("hbGPS_end_message"), p("\n"), @@ -329,7 +339,12 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), ), tags$style(HTML("#mylog_palmsplusr {color:darkblue; font-size:12px; font-style:italic; overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), - verbatimTextOutput("mylog_palmsplusr", placeholder = TRUE), + checkboxInput("palmsplusr_showlog", "hide log", value = FALSE), + shinyjs::hidden( + div(id = "palmsplusr_log_div", + verbatimTextOutput("mylog_palmsplusr", placeholder = TRUE) + ) + ), p("\n"), htmlOutput("palmsplusr_end_message"), p("\n"), @@ -1006,8 +1021,12 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), # on.exit(file.copy(from = stdout_GGIR_tmp, to = logfile, overwrite = TRUE)) write.table(x = NULL, file = stdout_GGIR_tmp) # initialise empty file - output$mylog_GGIR <- renderText({ - paste(mylog_GGIR(), collapse = '\n') + + observeEvent(input$GGIR_showlog, { + shinyjs::toggle('GGIR_log_div') + output$mylog_GGIR <- renderText({ + paste(mylog_GGIR(), collapse = '\n') + }) }) # Start GGIR x_ggir <- r_bg(func = function(GGIRshiny, rawaccdir, outputdir, @@ -1120,11 +1139,13 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), write.table(x = NULL, file = stdout_PALMSpy_tmp) # initialise empty file - output$mylog_PALMSpy <- renderText({ - paste(mylog_PALMSpy(), collapse = '\n') + observeEvent(input$PALMSpy_showlog, { + shinyjs::toggle('PALMSpy_log_div') + output$mylog_PALMSpy <- renderText({ + paste(mylog_PALMSpy(), collapse = '\n') + }) }) - # # Start PALMSpy x_palmspy <- r_bg(func = function(PALMSpyshiny, outputdir, gpsdir, count_file_location, envConda) { PALMSpyshiny(outputdir, gpsdir, count_file_location, envConda) @@ -1209,16 +1230,13 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), id_hbGPS = showNotification("hbGPS in progress ...", type = "message", duration = NULL, closeButton = FALSE) write.table(x = NULL, file = stdout_hbGPS_tmp) # initialise empty file - # observeEvent(input$hbGPS_showlog_button, { - # toggle('hbGPS_log_div') - # output$mylog_hbGPS <- renderText({ - # paste(mylog_hbGPS(), collapse = '\n') - # }) - # }) - output$mylog_hbGPS <- renderText({ - paste(mylog_hbGPS(), collapse = '\n') + observeEvent(input$hbGPS_showlog, { + shinyjs::toggle('hbGPS_log_div') + output$mylog_hbGPS <- renderText({ + paste(mylog_hbGPS(), collapse = '\n') + }) }) - + # If process somehow unexpectedly terminates, always copy tmp log # file to actual log file for user to see logfile = paste0(isolate(global$data_out), "/hbGPS.log") @@ -1331,10 +1349,13 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), id_palmsplusr = showNotification("palmsplusr in progress ...", type = "message", duration = NULL, closeButton = FALSE) write.table(x = NULL, file = stdout_palmsplusr_tmp) # initialise empty file - output$mylog_palmsplusr <- renderText({ - paste(mylog_palmsplusr(), collapse = '\n') - }) + observeEvent(input$palmsplusr_showlog, { + shinyjs::toggle('palmsplusr_log_div') + output$mylog_palmsplusr <- renderText({ + paste(mylog_palmsplusr(), collapse = '\n') + }) + }) # If process somehow unexpectedly terminates, always copy tmp log # file to actual log file for user to see logfile = paste0(isolate(global$data_out), "/palmsplusr.log") From b97be6fdf89708e261229fb4fabd8997595fa8c9 Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 8 Sep 2023 12:49:19 +0200 Subject: [PATCH 20/30] Update version number and date for this branch --- DESCRIPTION | 4 ++-- inst/NEWS.Rd | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index a37c7d7..8975006 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Package: HabitusGUI Title: R Shiny App for Processing Behavioural Data Description: Shiny app to ease processing behavioural data with research software such as GGIR, activityCounts, PALMSpy,and palmsplusr. -Version: 0.1.9 -Date: 2022-09-08 +Version: 0.2.0 +Date: 2022-09-30 Authors@R: c(person(given = "Vincent", family = "van Hees", diff --git a/inst/NEWS.Rd b/inst/NEWS.Rd index 5a01f6e..45b49b9 100755 --- a/inst/NEWS.Rd +++ b/inst/NEWS.Rd @@ -1,6 +1,12 @@ \name{NEWS} \title{News for Package \pkg{HabitusGUI}} \newcommand{\cpkg}{\href{http://CRAN.R-project.org/package=#1}{\pkg{#1}}} +\section{Changes in version 0.2.0 (GitHub-only-release date: ??-??-2023)}{ + \itemize{ + \item Embed hbGPS #90 + \item Allow for hiding progress log view #46 + } +} \section{Changes in version 0.1.9 (GitHub-only-release date: 08-09-2023)}{ \itemize{ \item Deprecate saving configfile in Shiny state as currently functionality From de792e7b9ae59145e0887b73b99a940bac49f84d Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 8 Sep 2023 13:31:52 +0200 Subject: [PATCH 21/30] Delete config_hbgps.csv --- inst/testfiles_hbgps/config_hbgps.csv | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 inst/testfiles_hbgps/config_hbgps.csv diff --git a/inst/testfiles_hbgps/config_hbgps.csv b/inst/testfiles_hbgps/config_hbgps.csv deleted file mode 100644 index 50d4418..0000000 --- a/inst/testfiles_hbgps/config_hbgps.csv +++ /dev/null @@ -1,11 +0,0 @@ -argument,value,context -idloc,6,general -maxBreakLengthSeconds,120,trip -minTripDur,60,trip -minTripDist_m,100,trip -threshold_snr,225,noise -threshold_snr_ratio,50,noise -tz,Europe/Brussels,general -time_format,%Y/%m/%d %H:%M:%S,general -outputFormat,PALMS,general -AccThresholds,"c(8.33, 208.33, 833.33, 1250)",accelerometer From 999d139ad2ae0c136cdfc14b38684cee9562c0f6 Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 8 Sep 2023 13:32:02 +0200 Subject: [PATCH 22/30] Create config_hbGPS.csv --- inst/testfiles_hbGPS/config_hbGPS.csv | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 inst/testfiles_hbGPS/config_hbGPS.csv diff --git a/inst/testfiles_hbGPS/config_hbGPS.csv b/inst/testfiles_hbGPS/config_hbGPS.csv new file mode 100644 index 0000000..ad1ac89 --- /dev/null +++ b/inst/testfiles_hbGPS/config_hbGPS.csv @@ -0,0 +1,11 @@ +argument,value,context +idloc,6,general +maxBreakLengthSeconds,120,trip +minTripDur,60,trip +minTripDist_m,100,trip +threshold_snr,225,noise +threshold_snr_ratio,50,noise +tz,Europe/Amsterdam,general +time_format,%Y/%m/%d %H:%M:%S,general +outputFormat,PALMS,general +AccThresholds,"c(8.33, 208.33, 833.33, 1250)",accelerometer From 8e1cfebec5c3ecba92e56e4f3ccbeb812fe6914f Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 15 Sep 2023 10:32:34 +0200 Subject: [PATCH 23/30] App now able to use hbGPS output and force user to choose between hbGPS output or PALMSpy output when working with palmsplusr --- R/myApp.R | 74 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/R/myApp.R b/R/myApp.R index 0811038..bbd61b9 100644 --- a/R/myApp.R +++ b/R/myApp.R @@ -28,19 +28,18 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { mylog_PALMSpy <- shiny::reactiveFileReader(500, NULL, stdout_PALMSpy_tmp, readLines, warn = FALSE) js_hbGPS <- paste0("$(document).on('shiny:value', function(evt){", - "if(evt.name == 'mylog_hbGPS'){", - " setTimeout(function(){", - " var objDiv = document.getElementById('mylog_hbGPS');", - " objDiv.scrollTop = objDiv.scrollHeight - objDiv.clientHeight;", - " }, 500);", - "}", - "});") + "if(evt.name == 'mylog_hbGPS'){", + " setTimeout(function(){", + " var objDiv = document.getElementById('mylog_hbGPS');", + " objDiv.scrollTop = objDiv.scrollHeight - objDiv.clientHeight;", + " }, 500);", + "}", + "});") js_GGIR <- sub(pattern = "hbGPS", replacement = "GGIR", x = js_hbGPS) js_palmsplusr <- sub(pattern = "hbGPS", replacement = "palmsplusr", x = js_hbGPS) js_PALMSpy <- sub(pattern = "hbGPS", replacement = "PALMSpy", x = js_hbGPS) - ui <- function() { fluidPage( theme = bslib::bs_theme(bootswatch = "litera"), #,"sandstone"), "sketchy" "pulse" @@ -402,7 +401,6 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), if (length(values$hbGPSoutdir) < 2) selected_hbGPSoutdir = c() else selected_hbGPSoutdir = paste(values$hbGPSoutdir$path, collapse = .Platform$file.sep) if (length(values$sleepdiaryfile) < 2) selectedSleepdiaryfile = c() else selectedSleepdiaryfile = paste(values$sleepdiaryfile$path, collapse = .Platform$file.sep) if (length(values$outputdir) < 2) selectedOutputdir = c() else selectedOutputdir = paste(values$outputdir$path, collapse = .Platform$file.sep) - observeEvent(input$page_12, { values_tmp = lapply(reactiveValuesToList(input), unclass) if (exists("values") & length(values) > 10) { @@ -448,7 +446,13 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), if ("CountConverter" %in% input$tools == TRUE & "AccRaw" %in% input$availabledata == FALSE) { showNotification("CountConverter not possible without access to raw accelerometer data", type = "error") } else { - switch_page(2) + if ("PALMSpy_out" %in% input$availabledata & "hbGPS_out" %in% input$availabledata) { + showNotification(paste0("You cannot select previously generated", + " output from both PALMS(py) and hbGPS.", + " Please select one of them."), type = "error") + } else { + switch_page(2) + } } } } @@ -929,7 +933,6 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), global$sleepdiaryfile <- as.character(parseFilePaths(c(home = homedir), sleepdiaryfile())$datapath) }) - # Send directories to UI -------------------------------------------- output$rawaccdir <- renderText({ @@ -975,7 +978,6 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), # Apply GGIR / CountConverter after button is pressed #======================================================================== runGGIR <- eventReactive(input$start_ggir, { - GGIRCounts_message = "" if ("GGIR" %in% input$tools | "CountConverter" %in% input$tools) { @@ -1021,7 +1023,6 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), # on.exit(file.copy(from = stdout_GGIR_tmp, to = logfile, overwrite = TRUE)) write.table(x = NULL, file = stdout_GGIR_tmp) # initialise empty file - observeEvent(input$GGIR_showlog, { shinyjs::toggle('GGIR_log_div') output$mylog_GGIR <- renderText({ @@ -1236,7 +1237,6 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), paste(mylog_hbGPS(), collapse = '\n') }) }) - # If process somehow unexpectedly terminates, always copy tmp log # file to actual log file for user to see logfile = paste0(isolate(global$data_out), "/hbGPS.log") @@ -1244,11 +1244,11 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), # Start hbGPS x_hbGPS <- r_bg(func = function(hbGPS_shiny, ggiroutdir, gpsdir, - outputdir, dataset_name, - configfile){ + outputdir, dataset_name, + configfile){ hbGPS_shiny(ggiroutdir, gpsdir, - outputdir, dataset_name, - configfile) + outputdir, dataset_name, + configfile) }, args = list(hbGPS_shiny = hbGPS_shiny, ggiroutdir = global$ggirout_in, @@ -1258,7 +1258,6 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), configfile = paste0(global$data_out, "/config_hbGPS.csv")), stdout = stdout_hbGPS_tmp, stderr = "2>&1") - observe({ if (x_hbGPS$poll_io(0)[["process"]] != "ready") { invalidateLater(5000) @@ -1284,13 +1283,13 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), } } else { hbGPS_message = paste0("hbGPS unsuccessful", - "
No file found inside: ", expected_hbGPS_folder, - "
Log file: ", logfile) + "
No file found inside: ", expected_hbGPS_folder, + "
Log file: ", logfile) } } else { hbGPS_message = paste0("hbGPS unsuccessful", - "
No file found inside: ", expected_hbGPS_folder, - "
Log file: ", logfile) + "
No file found inside: ", expected_hbGPS_folder, + "
Log file: ", logfile) } output$hbGPS_end_message <- renderUI({ HTML(paste0(hbGPS_message)) @@ -1314,13 +1313,21 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), # Basic check before running function: ready_to_run_palmsplusr = FALSE # Check for PALMSpy output (two possible sources either from this run or from a previous run) - if (dir.exists(global$palmspyout_in)) { - expected_palmspy_results_dir = global$palmspyout_in - } else { - expected_palmspy_results_dir = paste0(global$data_out,"/PALMSpy_output") + if ("PALMSpy_out" %in% input$availabledata) { + if (dir.exists(global$palmspyout_in)) { + expected_results_dir = global$palmspyout_in + } else { + expected_results_dir = paste0(global$data_out,"/PALMSpy_output") + } + } else if ("hbGPS_out" %in% input$availabledata) { + if (dir.exists(global$hbGPSout_in)) { + expected_results_dir = global$hbGPSout_in + } else { + expected_results_dir = paste0(global$data_out,"/hbGPS_output") + } } - if (dir.exists(expected_palmspy_results_dir)) { - Nfiles_in_dir = length(dir(path = expected_palmspy_results_dir, pattern = "csv", recursive = FALSE, full.names = FALSE)) + if (dir.exists(expected_results_dir)) { + Nfiles_in_dir = length(dir(path = expected_results_dir, pattern = "csv", recursive = FALSE, full.names = FALSE)) if (Nfiles_in_dir > 0) { # also check for GIS files if (dir.exists(global$gis_in)) { @@ -1338,10 +1345,11 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), palmsplusr_message = paste0("Folder that is supposed to hold GIS files does not exist: ", global$gis_in) } } else { - palmsplusr_message = paste0("No files found in PALMSpy output folder: ", expected_palmspy_results_dir) + nameinput = ifelse(test = "PALMSpy_out" %in% input$availabledata, yes = "PALMSpy", no = "hbGPS") + palmsplusr_message = paste0("No files found in ", nameinput, " output folder: ", expected_results_dir) } } else { - palmsplusr_message = paste0("Folder that is supposed to hold acceleration files does not exist: ", expected_palmspy_results_dir) + palmsplusr_message = paste0("Folder that is supposed to hold acceleration files does not exist: ", expected_results_dir) } # Only run function when checks are met: if (ready_to_run_palmsplusr == TRUE) { @@ -1387,7 +1395,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), }, args = list(palmsplusr_shiny = palmsplusr_shiny, gisdir = global$gis_in, - palmsdir = expected_palmspy_results_dir, + palmsdir = expected_results_dir, gislinkfile = global$gislinkfile_in, outputdir = isolate(global$data_out), dataset_name = input$dataset_name, @@ -1474,4 +1482,4 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), } # Run the application shinyApp(ui, server, enableBookmarking = "server") -} \ No newline at end of file +} From 838323fbb5cc05b3ed461749daa554abe094c8f8 Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 15 Sep 2023 10:33:10 +0200 Subject: [PATCH 24/30] make sure palmsplusr can find timestamps and minor clean up of code --- NAMESPACE | 1 - R/hbt_build_days.R | 5 +++-- R/palmsplusr_shiny.R | 19 ++++++++++--------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 686ccf1..488b644 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -41,7 +41,6 @@ importFrom(methods,new) importFrom(methods,setClass) importFrom(purrr,reduce) importFrom(readr,read_csv) -importFrom(readr,write_csv) importFrom(rlang,UQ) importFrom(rlang,parse_expr) importFrom(stats,aggregate) diff --git a/R/hbt_build_days.R b/R/hbt_build_days.R index 367027c..9e249de 100644 --- a/R/hbt_build_days.R +++ b/R/hbt_build_days.R @@ -64,9 +64,10 @@ hbt_build_days <- function(data = NULL, verbose = TRUE, mutate_if(is.logical, as.integer) fields <- palmsplus_fields %>% filter(domain_field == TRUE) %>% pull(name) - data <- data %>% + + data <- data %>% st_set_geometry(NULL) %>% - dplyr::select(identifier, datetime, domain_names, all_of(fields)) %>% + dplyr::select(identifier, datetime, any_of(domain_names), all_of(fields)) %>% mutate(duration = 1) %>% mutate_at(vars(-identifier,-datetime), ~ . * palms_epoch(data) / 60) %>% group_by(identifier, date = as.Date(datetime)) %>% diff --git a/R/palmsplusr_shiny.R b/R/palmsplusr_shiny.R index 680ab7a..9f43fbe 100644 --- a/R/palmsplusr_shiny.R +++ b/R/palmsplusr_shiny.R @@ -10,7 +10,7 @@ #' @return palms_to_clean_lower object #' @importFrom stats end start formula as.formula #' @importFrom tidyr pivot_wider -#' @importFrom readr write_csv read_csv +#' @importFrom readr read_csv #' @import palmsplusr #' @import dplyr #' @importFrom utils head tail @@ -96,6 +96,7 @@ palmsplusr_shiny <- function(gisdir = "", if (verbose) cat(paste0("\nCheck PALMS_reduced_file: ", PALMS_reduced_file)) write.csv(palms_reduced_cleaned, PALMS_reduced_file) palms = palmsplusr::read_palms(PALMS_reduced_file) + palms$datetime = as.POSIXct(palms$datetime, format = "%d/%m/%Y %H:%M:%S", tz = "") # Helper function to find shape files find_file = function(path, namelowercase) { @@ -241,13 +242,11 @@ palmsplusr_shiny <- function(gisdir = "", loca = loca, participant_basis = participant_basis, verbose = verbose) - write_csv(palmsplus, file = fns[1]) + data.table::fwrite(palmsplus, file = fns[1]) if (verbose) cat(">>>\n") } else { if (verbose) cat("skipped because insufficient input data>>>\n") } - - if (verbose) cat("\n<<< building days...") if (length(palmsplus) > 0 & length(palmsplus_domains) > 0 & length(palmsplus_fields) & all(Nlocation_objects > 0) & length(participant_basis) > 0) { @@ -257,18 +256,20 @@ palmsplusr_shiny <- function(gisdir = "", loca = loca, participant_basis = participant_basis, verbose = verbose) + if (length(days) > 0) { if (verbose) cat(paste0(" N rows in days object: ", nrow(days))) + data.table::fwrite(x = days, file = fns[2]) } else { if (verbose) cat(paste0(" WARNING: no days object produced.")) } - write_csv(days, file = fns[2]) + # sf::st_write(palmsplus, dsn = paste0(palmsplus_folder, "/", dataset_name, "_palmsplus.shp"), append = FALSE) - if (verbose) cat(">>>\n") + } else { if (verbose) cat("skipped because insufficient input data>>>\n") } - + if (verbose) cat(">>>\n") trajectory_locations = trajectory_locations[order(trajectory_locations$name),] if (verbose) cat("\n<<< building trajectories...\n") if (length(palmsplus) > 0 & length(trajectory_fields) > 0) { @@ -277,7 +278,7 @@ palmsplusr_shiny <- function(gisdir = "", trajectory_fields = trajectory_fields, trajectory_locations = trajectory_locations) if (length(trajectories) > 0) { - write_csv(trajectories, file = fns[3]) + data.table::fwrite(trajectories, file = fns[3]) shp_file = paste0(palmsplus_folder, "/", dataset_name, "_trajecories.shp") if (file.exists(shp_file)) file.remove(shp_file) # remove because st_write does not know how to overwrite @@ -301,7 +302,7 @@ palmsplusr_shiny <- function(gisdir = "", verbose = verbose) if (length(multimodal) > 0) { - write_csv(multimodal, file = fns[4]) + data.table::fwrite(multimodal, file = fns[4]) shp_file = paste0(palmsplus_folder, "/", dataset_name, "_multimodal.shp") if (file.exists(shp_file)) file.remove(shp_file) # remove because st_write does not know how to overwrite sf::st_write(obj = multimodal, dsn = shp_file) From f03802e18f2cf510edb17b873be8a63f5f51ba99 Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 15 Sep 2023 13:10:46 +0200 Subject: [PATCH 25/30] turn off attempt to auto-scroll as it was not working --- R/myApp.R | 45 +++++++++++---------------------------------- 1 file changed, 11 insertions(+), 34 deletions(-) diff --git a/R/myApp.R b/R/myApp.R index bbd61b9..cc01236 100644 --- a/R/myApp.R +++ b/R/myApp.R @@ -27,26 +27,20 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { mylog_hbGPS <- shiny::reactiveFileReader(500, NULL, stdout_hbGPS_tmp, readLines, warn = FALSE) mylog_PALMSpy <- shiny::reactiveFileReader(500, NULL, stdout_PALMSpy_tmp, readLines, warn = FALSE) - js_hbGPS <- paste0("$(document).on('shiny:value', function(evt){", - "if(evt.name == 'mylog_hbGPS'){", - " setTimeout(function(){", - " var objDiv = document.getElementById('mylog_hbGPS');", - " objDiv.scrollTop = objDiv.scrollHeight - objDiv.clientHeight;", - " }, 500);", - "}", - "});") - - js_GGIR <- sub(pattern = "hbGPS", replacement = "GGIR", x = js_hbGPS) - js_palmsplusr <- sub(pattern = "hbGPS", replacement = "palmsplusr", x = js_hbGPS) - js_PALMSpy <- sub(pattern = "hbGPS", replacement = "PALMSpy", x = js_hbGPS) - + logViewStyle = paste0("color:darkblue; ", + "overflow-y:scroll; font-size:12px; font-style:italic; ;", + "max-height: 300px; background: ghostwhite;", + "position:relative; align: centre;") ui <- function() { + fluidPage( theme = bslib::bs_theme(bootswatch = "litera"), #,"sandstone"), "sketchy" "pulse" # preview examples: https://bootswatch.com/ # “cerulean”, “cosmo”, “cyborg”, “darkly”, “flatly”, “journal”, “litera”, “lumen”, # “lux”, “materia”, “minty”, “morph”, “pulse”, “quartz”, “sandstone”, “simplex”, # “sketchy”, “slate”, “solar”, “spacelab”, “superhero”, “united”, “vapor”, “yeti”, “zephyr” + + tabsetPanel( id = "wizard", type = "hidden", @@ -267,11 +261,7 @@ myApp <- function(homedir=getwd(), envConda = "~/miniconda3/bin/conda", ...) { shinyjs::useShinyjs(), actionButton("start_ggir", "Start analysis", width = '300px'), p("\n"), - tags$head( - tags$script(HTML(js_GGIR)) - ), - tags$style(HTML("#mylog_GGIR {color:darkblue; font-size:12px; font-style:italic; -overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), + tags$style(HTML("#mylog_GGIR {", logViewStyle, "}")), br(), checkboxInput("GGIR_showlog", "hide log", value = FALSE), shinyjs::hidden( div(id = "GGIR_log_div", @@ -289,11 +279,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), shinyjs::useShinyjs(), actionButton("start_palmspy", "Start analysis", width = '300px'), p("\n"), - tags$head( - tags$script(HTML(js_PALMSpy)) - ), - tags$style(HTML("#mylog_PALMSpy {color:darkblue; font-size:12px; font-style:italic; -overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), + tags$style(HTML("#mylog_PALMSpy {", logViewStyle, "}")), br(), checkboxInput("PALMSpy_showlog", "hide log", value = FALSE), shinyjs::hidden( div(id = "PALMSpy_log_div", @@ -311,11 +297,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), shinyjs::useShinyjs(), actionButton("start_hbGPS", "Start analysis", width = '300px'), p("\n"), - tags$head( - tags$script(HTML(js_hbGPS)) - ), - tags$style(HTML("#mylog_hbGPS {color:darkblue; font-size:12px; font-style:italic; -overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), + tags$style(HTML("#mylog_hbGPS {", logViewStyle, "}")), br(), checkboxInput("hbGPS_showlog", "hide log", value = FALSE), shinyjs::hidden( div(id = "hbGPS_log_div", @@ -333,11 +315,7 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), shinyjs::useShinyjs(), actionButton("start_palmsplusr", "Start analysis", width = '300px'), p("\n"), - tags$head( - tags$script(HTML(js_palmsplusr)) - ), - tags$style(HTML("#mylog_palmsplusr {color:darkblue; font-size:12px; font-style:italic; -overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), + tags$style(HTML(paste0("#mylog_palmsplusr {", logViewStyle, "}"))), br(), checkboxInput("palmsplusr_showlog", "hide log", value = FALSE), shinyjs::hidden( div(id = "palmsplusr_log_div", @@ -1357,7 +1335,6 @@ overflow-y:scroll; max-height: 300px; background: ghostwhite;}")), br(), id_palmsplusr = showNotification("palmsplusr in progress ...", type = "message", duration = NULL, closeButton = FALSE) write.table(x = NULL, file = stdout_palmsplusr_tmp) # initialise empty file - observeEvent(input$palmsplusr_showlog, { shinyjs::toggle('palmsplusr_log_div') output$mylog_palmsplusr <- renderText({ From 8e37e0cb55c45c7f528ca76a79dd54898fa8d945 Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 15 Sep 2023 13:10:59 +0200 Subject: [PATCH 26/30] Update check_and_clean_palms_data.R --- R/check_and_clean_palms_data.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/check_and_clean_palms_data.R b/R/check_and_clean_palms_data.R index 94d9f5a..dc1c107 100644 --- a/R/check_and_clean_palms_data.R +++ b/R/check_and_clean_palms_data.R @@ -40,7 +40,7 @@ check_and_clean_palms_data <- function(palms_to_clean, country_name){ # Saving the new 'clean' dataset - %>% --------------------------------------- # write_csv(palms, str_replace(link_to_csv, pattern = '.csv', '_cleaned.csv'), na = "") - write_csv(error_list, paste(country_name,"error_list.csv", sep = "_")) + data.table::fwrite(error_list, paste(country_name,"error_list.csv", sep = "_")) return(palms_to_clean_lower) } From b268a556ba4d1013900ea885095c5ac651c69cf1 Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 15 Sep 2023 13:11:07 +0200 Subject: [PATCH 27/30] prepare new release --- inst/NEWS.Rd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inst/NEWS.Rd b/inst/NEWS.Rd index 45b49b9..d491145 100755 --- a/inst/NEWS.Rd +++ b/inst/NEWS.Rd @@ -1,7 +1,7 @@ \name{NEWS} \title{News for Package \pkg{HabitusGUI}} \newcommand{\cpkg}{\href{http://CRAN.R-project.org/package=#1}{\pkg{#1}}} -\section{Changes in version 0.2.0 (GitHub-only-release date: ??-??-2023)}{ +\section{Changes in version 0.2.0 (GitHub-only-release date: 15-09-2023)}{ \itemize{ \item Embed hbGPS #90 \item Allow for hiding progress log view #46 From 0e5962c4353606633993cdc80550d82251464a5f Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 15 Sep 2023 13:20:25 +0200 Subject: [PATCH 28/30] palmsplusr error log should be stored in outputdir and not in workingdir. --- R/check_and_clean_palms_data.R | 5 +++-- R/palmsplusr_shiny.R | 2 +- man/check_and_clean_palms_data.Rd | 4 +++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/R/check_and_clean_palms_data.R b/R/check_and_clean_palms_data.R index dc1c107..a1dd05e 100644 --- a/R/check_and_clean_palms_data.R +++ b/R/check_and_clean_palms_data.R @@ -2,10 +2,11 @@ #' #' @param palms_to_clean palms_to_clean #' @param country_name country_name +#' @param outputdir outputdir #' @return palms_to_clean_lower object #' @import dplyr #' @export -check_and_clean_palms_data <- function(palms_to_clean, country_name){ +check_and_clean_palms_data <- function(palms_to_clean, country_name, outputdir = NULL){ dif = datetime = identifier = tripnumber = triptype = tt1 = tt4 = NULL miss_end = miss_start = multi_start = multi_end = error = NULL # Create error list ------------------------------------------------------- @@ -40,7 +41,7 @@ check_and_clean_palms_data <- function(palms_to_clean, country_name){ # Saving the new 'clean' dataset - %>% --------------------------------------- # write_csv(palms, str_replace(link_to_csv, pattern = '.csv', '_cleaned.csv'), na = "") - data.table::fwrite(error_list, paste(country_name,"error_list.csv", sep = "_")) + data.table::fwrite(error_list, paste(outputdir, "/", country_name,"error_list.csv", sep = "_")) return(palms_to_clean_lower) } diff --git a/R/palmsplusr_shiny.R b/R/palmsplusr_shiny.R index 9f43fbe..f09adfd 100644 --- a/R/palmsplusr_shiny.R +++ b/R/palmsplusr_shiny.R @@ -86,7 +86,7 @@ palmsplusr_shiny <- function(gisdir = "", # Data cleaning: if (verbose) cat("\nstart cleaning\n") PALMS_reduced <- subset(PALMS_combined, lon > -180) - palms_reduced_cleaned <- check_and_clean_palms_data(PALMS_reduced, dataset_name) + palms_reduced_cleaned <- check_and_clean_palms_data(PALMS_reduced, dataset_name, outputdir) if (verbose) cat("\ncleaning completed\n") PALMS_reduced$dateTime = as.POSIXct(PALMS_reduced$dateTime, format = "%d/%m/%Y %H:%M:%S", tz = "") diff --git a/man/check_and_clean_palms_data.Rd b/man/check_and_clean_palms_data.Rd index 41977b4..0d90039 100644 --- a/man/check_and_clean_palms_data.Rd +++ b/man/check_and_clean_palms_data.Rd @@ -4,12 +4,14 @@ \alias{check_and_clean_palms_data} \title{check_and_clean_palms_data} \usage{ -check_and_clean_palms_data(palms_to_clean, country_name) +check_and_clean_palms_data(palms_to_clean, country_name, outputdir = NULL) } \arguments{ \item{palms_to_clean}{palms_to_clean} \item{country_name}{country_name} + +\item{outputdir}{outputdir} } \value{ palms_to_clean_lower object From 1fa194aac8faa7b21b0590f5d6c86e20fbdc6c0b Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 15 Sep 2023 13:26:22 +0200 Subject: [PATCH 29/30] add reference to hbGPS location on GitHub --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 8975006..383607a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -14,7 +14,7 @@ License: Apache License version 2.0 | file LICENSE Imports: shiny, shinyFiles, GGIR, bslib, methods, jsonlite, DT, dplyr, magrittr, shinyjs, sf, readr, tidyr, stringr, callr, palmsplusr, data.table, rlang, purrr, geosphere, hbGPS -Remotes: vincentvanhees/palmsplusr +Remotes: vincentvanhees/palmsplusr, habitus-eu/hbGPS LazyData: true Suggests: testthat, covr, rmarkdown Depends: stats, utils, R (>= 3.5.0) From 7f6c83f69d3efd04f7b5fe49187bd2f7cbf938cb Mon Sep 17 00:00:00 2001 From: Vincent van Hees Date: Fri, 15 Sep 2023 15:12:59 +0200 Subject: [PATCH 30/30] fix bug in commit from earlier today --- R/check_and_clean_palms_data.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/check_and_clean_palms_data.R b/R/check_and_clean_palms_data.R index a1dd05e..b258b1b 100644 --- a/R/check_and_clean_palms_data.R +++ b/R/check_and_clean_palms_data.R @@ -41,7 +41,7 @@ check_and_clean_palms_data <- function(palms_to_clean, country_name, outputdir = # Saving the new 'clean' dataset - %>% --------------------------------------- # write_csv(palms, str_replace(link_to_csv, pattern = '.csv', '_cleaned.csv'), na = "") - data.table::fwrite(error_list, paste(outputdir, "/", country_name,"error_list.csv", sep = "_")) + data.table::fwrite(error_list, paste(outputdir, country_name,"error_list.csv", sep = "_")) return(palms_to_clean_lower) }