From 9099d068788094f76087b09f81838968c7985974 Mon Sep 17 00:00:00 2001 From: Amandine Grappe Date: Wed, 12 Nov 2025 13:29:24 +0100 Subject: [PATCH] Retrieve extra mouse info to add more stats --- imotionsApi/R/imotionsApi.R | 17 ++++---- .../testthat/test-getAOIRespondentData.R | 41 ++++++++++--------- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/imotionsApi/R/imotionsApi.R b/imotionsApi/R/imotionsApi.R index 75e0d4c..8053bf0 100644 --- a/imotionsApi/R/imotionsApi.R +++ b/imotionsApi/R/imotionsApi.R @@ -1492,14 +1492,14 @@ privateDownloadData <- function(study, sensor, signalsName = NULL) { -#' Get the inOutGaze information, inOutMouseClick information and AOI's intervals for a specific AOI/respondent +#' Get the inOutGaze information, inOutMouse information and AOI's intervals for a specific AOI/respondent #' combination. Note that imAOI object, by definition, are linked to a specific stimulus. #' #' The inOutGaze data.table has a IsGazeInAOI column that is TRUE when a gaze was recorded inside the AOI and FALSE if #' outside (timestamps correspond to the actual gazepoint Timestamp). To reduce the size of the file created, #' only timestamps where a change of value occur are given. If the AOI was never active, the table is empty. #' -#' The inOutMouseClick data.table has a IsMouseInAOI column that is TRUE when a click was recorded inside the AOI and +#' The inOutMouse data.table has a IsMouseInAOI column that is TRUE when a click was recorded inside the AOI and #' FALSE if outside (timestamps correspond to the actual Timestamp of each click). If no click was recorded or if the #' AOI was never active, the table is empty. #' @@ -1508,7 +1508,7 @@ privateDownloadData <- function(study, sensor, signalsName = NULL) { #' @param respondent An imRespondent object as returned from \code{\link{getRespondents}}. #' #' @importFrom dplyr mutate_at %>% -#' @return A list with inOutGaze/inOutMouseClick information for the specific AOI/respondent combination and an +#' @return A list with inOutGaze/inOutMouse information for the specific AOI/respondent combination and an #' imIntervalList object (data.table) composed of the start, end, duration, id and name of this AOI. #' @export #' @examples @@ -1573,8 +1573,8 @@ getAOIRespondentData <- function(study, AOI, respondent) { intervals <- data.table(fragments.start = NA_real_, fragments.end = NA_real_) inOutGaze <- data.table(matrix(data = NA_integer_, ncol = 2, nrow = 0)) names(inOutGaze) <- c("Timestamp", "IsGazeInAOI") - inOutMouseClick <- data.table(matrix(data = NA_integer_, ncol = 2, nrow = 0)) - names(inOutMouseClick) <- c("Timestamp", "IsMouseInAOI") + inOutMouse <- data.table(matrix(data = NA_integer_, ncol = 3, nrow = 0)) + names(inOutMouse) <- c("Timestamp", "IsMouseInAOI", "IsMouseDown") } else { data$id <- AOI$id @@ -1588,7 +1588,10 @@ getAOIRespondentData <- function(study, AOI, respondent) { inOutGaze <- inOutGaze[, c("Timestamp", "IsGazeInAOI")] # Get clicks events - inOutMouseClick <- data[(data$IsMouseDown), c("Timestamp", "IsMouseInAOI")] + mouseChange <- data[, c(IsMouseInAOI = unique(IsMouseInAOI), .SD[1]), by = rleid(IsMouseInAOI)][, -1] + mouseChange <- rbind(mouseChange, data[IsMouseDown == TRUE, ]) + + inOutMouse <- mouseChange[order(Timestamp), c("Timestamp", "IsMouseInAOI", "IsMouseDown")] } intervals <- intervals[, let(fragments.duration = intervals$fragments.end - intervals$fragments.start, @@ -1598,7 +1601,7 @@ getAOIRespondentData <- function(study, AOI, respondent) { intervals[is.na(intervals$fragments.duration), ]$fragments.duration <- 0 intervals <- createImObject(intervals, "Interval") - return(list(inOutGaze = inOutGaze, inOutMouseClick = inOutMouseClick, intervals = intervals)) + return(list(inOutGaze = inOutGaze, inOutMouse = inOutMouse, intervals = intervals)) } diff --git a/imotionsApi/tests/testthat/test-getAOIRespondentData.R b/imotionsApi/tests/testthat/test-getAOIRespondentData.R index e85e9e3..a8188c9 100644 --- a/imotionsApi/tests/testthat/test-getAOIRespondentData.R +++ b/imotionsApi/tests/testthat/test-getAOIRespondentData.R @@ -274,14 +274,15 @@ test_that("local return - inOutGaze for a specific AOI/respondent pair", { 1e-2, info = "wrong in/off Timestamp") }) -test_that("local return - inOutMouseClick for a specific AOI/respondent pair", { - inOutMouseClick <- mockedGetAOIRespondentData(study, AOI, respondent, AOIDetailsFile)$inOutMouseClick - - # Check inOutMouseClick data.table - expect_named(inOutMouseClick, c("Timestamp", "IsMouseInAOI"), info = "wrong column names") - expect_equal(nrow(inOutMouseClick), 1, info = "should have 1 in/off values") - expect_true(inOutMouseClick$IsMouseInAOI, "wrong value") - expect_equal(inOutMouseClick$Timestamp, 22109.5, 1e-2, info = "wrong in/off Timestamp") +test_that("local return - inOutMouse for a specific AOI/respondent pair", { + inOutMouse <- mockedGetAOIRespondentData(study, AOI, respondent, AOIDetailsFile)$inOutMouse + + # Check inOutMouse data.table + expect_named(inOutMouse, c("Timestamp", "IsMouseInAOI", "IsMouseDown"), info = "wrong column names") + expect_equal(nrow(inOutMouse[(IsMouseDown), ]), 1, info = "should have 1 mouse click value") + expect_equal(nrow(inOutMouse[!(IsMouseDown), ]), 7, info = "should have 7 mouse in/out values") + expect_true(inOutMouse[(IsMouseDown), ]$IsMouseInAOI, "wrong value") + expect_equal(inOutMouse[(IsMouseDown), ]$Timestamp, 22109.5, 1e-2, info = "wrong in/off Timestamp") }) # Modify AOIDetailsFile so it returns an empty event (no gaze, mouse click in AOI) @@ -290,8 +291,8 @@ AOIDetailsFile$fileId <- "../data/aoiEmptyRespondentData.pbin" test_that("local check - work if no gazes or mouse click in", { resultList <- mockedGetAOIRespondentData(study, AOI, respondent, AOIDetailsFile) - expect_named(resultList, c("inOutGaze", "inOutMouseClick", "intervals"), info = "wrong names") - expect_equal(nrow(resultList$inOutMouseClick), 0, info = "should be empty (no click)") + expect_named(resultList, c("inOutGaze", "inOutMouse", "intervals"), info = "wrong names") + expect_equal(nrow(resultList$inOutMouse), 1, info = "should only have one value (no mouse in or mouse click)") expect_equal(nrow(resultList$inOutGaze), 1, info = "should only have one value (no gaze in)") expect_false(resultList$inOutGaze$IsGazeInAOI, info = "wrong value") }) @@ -302,11 +303,11 @@ AOIDetailsFile$fileId <- "../data/AOInotDefined.pbin" test_that("local check - work if no AOI exposure", { resultList <- mockedGetAOIRespondentData(study, AOI, respondent, AOIDetailsFile) - expect_named(resultList, c("inOutGaze", "inOutMouseClick", "intervals"), info = "wrong names") + expect_named(resultList, c("inOutGaze", "inOutMouse", "intervals"), info = "wrong names") - # Check inOutMouseClick - expect_named(resultList$inOutMouseClick, c("Timestamp", "IsMouseInAOI"), info = "wrong column names") - expect_equal(nrow(resultList$inOutMouseClick), 0, info = "should be empty (no exposure)") + # Check inOutMouse + expect_named(resultList$inOutMouse, c("Timestamp", "IsMouseInAOI", "IsMouseDown"), info = "wrong column names") + expect_equal(nrow(resultList$inOutMouse), 0, info = "should be empty (no exposure)") # Check inOutGaze expect_named(resultList$inOutGaze, c("Timestamp", "IsGazeInAOI"), info = "wrong column names") @@ -382,12 +383,12 @@ test_that("remote return - inOutGaze for a specific AOI/respondent pair", { expect_equal(inOutGaze$Timestamp, c(44639, 46091, 46639, 49919, 50381), 1e-2, info = "wrong in/off Timestamp") }) -test_that("remote return - inOutMouseClick for a specific AOI/respondent pair", { - inOutMouseClick <- mockedGetAOIRespondentData(study_cloud, AOI_cloud, respondent, aoiDetails)$inOutMouseClick +test_that("remote return - inOutMouse for a specific AOI/respondent pair", { + inOutMouse <- mockedGetAOIRespondentData(study_cloud, AOI_cloud, respondent, aoiDetails)$inOutMouse - # Check inOutMouseClick data.table - expect_named(inOutMouseClick, c("Timestamp", "IsMouseInAOI"), info = "wrong column names") - expect_equal(nrow(inOutMouseClick), 0, info = "should have 0 in/off values") + # Check inOutMouse data.table + expect_named(inOutMouse, c("Timestamp", "IsMouseInAOI", "IsMouseDown"), info = "wrong column names") + expect_equal(nrow(inOutMouse), 1, info = "should have only one value (never entered)") }) AOIDetailsPath_cloud <- "../data/AOIDetails_cloud_dynamic.json" @@ -407,5 +408,5 @@ test_that("remote return - intervals should work on dynamic AOIs", { test_that("remote return - should work when inout data is already loaded", { resultList <- mockedGetAOIRespondentData(study_cloud, AOI_cloud_inout, respondent, aoiDetails_inout) - expect_named(resultList, c("inOutGaze", "inOutMouseClick", "intervals"), info = "wrong names") + expect_named(resultList, c("inOutGaze", "inOutMouse", "intervals"), info = "wrong names") })