From 2da29c8bcb58b8d68bc2c5e8109e36021f105b0b Mon Sep 17 00:00:00 2001 From: anderstorstensson Date: Mon, 16 Feb 2026 08:21:21 +0100 Subject: [PATCH 1/2] feat: separate SQLite database path from output folder The output folder is often on a network drive shared between team members, but SQLite databases are not safe on network filesystems due to unreliable file locking. This introduces a separate, configurable database directory that defaults to a local user-level path (tools::R_user_dir("ClassiPyR", "data")), keeping the database safe while the output folder can remain on a network drive. - Add get_default_db_dir() returning a persistent local directory - Rename get_db_path() parameter from output_folder to db_folder - Add db_folder parameter to save_sample_annotations() and rescan_file_index() - Add Database Folder setting with browse button and network warning - Update all get_db_path() calls in server.R to use config$db_folder - Add migration FAQ for transferring annotations between machines - Update vignettes with new settings table entries and network notes - Add _pkgdown.yml entry for get_default_db_dir Co-Authored-By: Claude Opus 4.6 --- DESCRIPTION | 4 +- NAMESPACE | 21 + NEWS.md | 17 +- R/database.R | 625 +++++++++++++++++++++ R/sample_loading.R | 21 + R/sample_saving.R | 68 ++- R/utils.R | 56 +- README.md | 26 +- _pkgdown.yml | 16 + inst/app/global.R | 2 + inst/app/server.R | 407 ++++++++++---- man/export_all_db_to_mat.Rd | 27 + man/export_all_db_to_png.Rd | 35 ++ man/export_db_to_mat.Rd | 29 + man/export_db_to_png.Rd | 38 ++ man/figures/settings-dialog.png | Bin 160200 -> 83806 bytes man/get_db_path.Rd | 32 ++ man/get_default_db_dir.Rd | 25 + man/import_all_mat_to_db.Rd | 32 ++ man/import_mat_to_db.Rd | 42 ++ man/init_db_schema.Rd | 19 + man/list_annotated_samples_db.Rd | 23 + man/load_annotations_db.Rd | 31 ++ man/load_from_db.Rd | 30 + man/rescan_file_index.Rd | 9 +- man/save_annotations_db.Rd | 41 ++ man/save_sample_annotations.Rd | 29 +- man/update_annotator.Rd | 41 ++ tests/testthat/test-database.R | 814 ++++++++++++++++++++++++++++ tests/testthat/test-sample_saving.R | 152 +++++- tests/testthat/test-utils.R | 20 + vignettes/class-management.Rmd | 2 +- vignettes/faq.Rmd | 154 +++++- vignettes/getting-started.Rmd | 23 +- vignettes/user-guide.Rmd | 50 +- 35 files changed, 2771 insertions(+), 190 deletions(-) create mode 100644 R/database.R create mode 100644 man/export_all_db_to_mat.Rd create mode 100644 man/export_all_db_to_png.Rd create mode 100644 man/export_db_to_mat.Rd create mode 100644 man/export_db_to_png.Rd create mode 100644 man/get_db_path.Rd create mode 100644 man/get_default_db_dir.Rd create mode 100644 man/import_all_mat_to_db.Rd create mode 100644 man/import_mat_to_db.Rd create mode 100644 man/init_db_schema.Rd create mode 100644 man/list_annotated_samples_db.Rd create mode 100644 man/load_annotations_db.Rd create mode 100644 man/load_from_db.Rd create mode 100644 man/save_annotations_db.Rd create mode 100644 man/update_annotator.Rd create mode 100644 tests/testthat/test-database.R diff --git a/DESCRIPTION b/DESCRIPTION index 1d749c0..7107604 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -24,7 +24,9 @@ Imports: dplyr, DT, jsonlite, - reticulate + reticulate, + DBI, + RSQLite Suggests: testthat (>= 3.0.0), covr, diff --git a/NAMESPACE b/NAMESPACE index 32f8a83..9577f2a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,29 +3,50 @@ export(copy_images_to_class_folders) export(create_empty_changes_log) export(create_new_classifications) +export(export_all_db_to_mat) +export(export_all_db_to_png) +export(export_db_to_mat) +export(export_db_to_png) export(filter_to_extracted) export(get_config_dir) +export(get_db_path) +export(get_default_db_dir) export(get_file_index_path) export(get_sample_paths) export(get_settings_path) +export(import_all_mat_to_db) +export(import_mat_to_db) export(init_python_env) export(is_valid_sample_name) +export(list_annotated_samples_db) +export(load_annotations_db) export(load_class_list) export(load_file_index) export(load_from_classifier_mat) export(load_from_csv) +export(load_from_db) export(load_from_mat) export(read_roi_dimensions) export(rescan_file_index) export(run_app) export(sanitize_string) +export(save_annotations_db) export(save_file_index) export(save_sample_annotations) export(save_validation_statistics) +export(update_annotator) +importFrom(DBI,dbConnect) +importFrom(DBI,dbDisconnect) +importFrom(DBI,dbExecute) +importFrom(DBI,dbGetQuery) +importFrom(DBI,dbWriteTable) importFrom(DT,renderDT) +importFrom(RSQLite,SQLite) importFrom(bslib,bs_theme) importFrom(dplyr,filter) importFrom(iRfcb,ifcb_annotate_samples) +importFrom(iRfcb,ifcb_create_manual_file) +importFrom(iRfcb,ifcb_extract_pngs) importFrom(iRfcb,ifcb_get_mat_variable) importFrom(jsonlite,fromJSON) importFrom(reticulate,py_available) diff --git a/NEWS.md b/NEWS.md index 46b4a5c..61f390a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,16 @@ ## Features +### SQLite Database Backend +- Annotations are now stored in a local SQLite database (`annotations.sqlite`) by default +- Works out of the box with no Python dependency - only R packages (RSQLite, DBI) are needed +- MATLAB `.mat` file export is still available as an opt-in for ifcb-analysis compatibility +- Storage format configurable in Settings: "SQLite" (default), "MAT file", or "Both" +- Existing `.mat` annotations continue to work and can be loaded as before +- `import_mat_to_db()` utility for bulk migration of existing `.mat` files to SQLite +- Sample discovery scans both `.mat` files and the SQLite database +- When loading a sample, SQLite is checked first (faster), with `.mat` fallback + ### Sample Management - Load samples from ROI files with automatic year/month filtering - Support for validation mode (existing classifications) and annotation mode (new samples) @@ -54,7 +64,9 @@ - Visual warnings for classes in classifications not in class2use list ### Output -- Save annotations as MATLAB-compatible .mat files (using iRfcb) +- Save annotations to SQLite database (default, no Python needed) +- Optional: save annotations as MATLAB-compatible .mat files (using iRfcb, requires Python) +- Configurable storage format: SQLite only, MAT only, or both - Save validation statistics as CSV (in `validation_statistics/` subfolder) - Organize output PNGs by class folder (for CNN training) - Auto-save when navigating between samples @@ -76,7 +88,8 @@ - Switch between annotation/validation modes for dual-mode samples ## Technical Notes -- Requires Python with scipy for MAT file writing (optional - only for ifcb-analysis compatibility) +- SQLite is the default annotation storage - works out of the box with RSQLite (no external dependencies) +- Python with scipy is optional - only needed for MAT file export (ifcb-analysis compatibility) - Uses iRfcb package for IFCB data handling - Session cache preserves work when switching samples - File index cache reduces startup time by avoiding redundant folder scans diff --git a/R/database.R b/R/database.R new file mode 100644 index 0000000..448c52b --- /dev/null +++ b/R/database.R @@ -0,0 +1,625 @@ +# SQLite database backend for ClassiPyR annotations +# +# Provides functions to store and retrieve annotations in a local SQLite +# database as an alternative to .mat files. SQLite is the default storage +# backend - it works out of the box with no Python dependency. + +#' @importFrom DBI dbConnect dbDisconnect dbWriteTable dbGetQuery dbExecute +#' @importFrom RSQLite SQLite +#' @importFrom iRfcb ifcb_create_manual_file ifcb_extract_pngs +NULL + +#' Get path to the annotations SQLite database +#' +#' Returns the path to \code{annotations.sqlite} in the given database +#' directory. The database directory should be on a local filesystem, not a +#' network drive, because +#' \href{https://www.sqlite.org/useovernet.html}{SQLite file locking is +#' unreliable over network filesystems}. +#' +#' @param db_folder Path to the database directory. Defaults to +#' \code{\link{get_default_db_dir}()}, a persistent local directory. +#' @return Path to the SQLite database file +#' @export +#' @seealso \code{\link{get_default_db_dir}} for the default database directory +#' @examples +#' # Use the default local database directory +#' get_db_path(get_default_db_dir()) +#' +#' # Or specify a custom directory +#' get_db_path("/data/local_db") +get_db_path <- function(db_folder) { + file.path(db_folder, "annotations.sqlite") +} + +#' Initialize the annotations database schema +#' +#' Creates the \code{annotations} and \code{class_lists} tables if they do not +#' already exist. +#' +#' @param con A DBI connection object +#' @return NULL (called for side effects) +#' @keywords internal +init_db_schema <- function(con) { + dbExecute(con, " + CREATE TABLE IF NOT EXISTS annotations ( + sample_name TEXT NOT NULL, + roi_number INTEGER NOT NULL, + class_name TEXT NOT NULL, + annotator TEXT, + timestamp TEXT DEFAULT (datetime('now')), + PRIMARY KEY (sample_name, roi_number) + ) + ") + + dbExecute(con, " + CREATE TABLE IF NOT EXISTS class_lists ( + sample_name TEXT NOT NULL, + class_index INTEGER NOT NULL, + class_name TEXT NOT NULL, + PRIMARY KEY (sample_name, class_index) + ) + ") + + invisible(NULL) +} + +#' Save annotations to the SQLite database +#' +#' Writes (or replaces) annotations for a single sample. The existing rows for +#' the sample are deleted first so that re-saving acts as an upsert. +#' +#' @param db_path Path to the SQLite database file +#' @param sample_name Sample name (e.g., \code{"D20230101T120000_IFCB134"}) +#' @param classifications Data frame with at least \code{file_name} and +#' \code{class_name} columns +#' @param class2use Character vector of class names (preserves index order for +#' .mat export) +#' @param annotator Annotator name +#' @return TRUE on success, FALSE on failure +#' @export +#' @examples +#' \dontrun{ +#' db_path <- get_db_path("/data/manual") +#' save_annotations_db(db_path, "D20230101T120000_IFCB134", +#' classifications, class2use, "Jane") +#' } +save_annotations_db <- function(db_path, sample_name, classifications, + class2use, annotator = "Unknown") { + if (is.null(classifications) || nrow(classifications) == 0) { + return(FALSE) + } + + dir.create(dirname(db_path), recursive = TRUE, showWarnings = FALSE) + + con <- dbConnect(SQLite(), db_path) + on.exit(dbDisconnect(con), add = TRUE) + + init_db_schema(con) + + # Extract ROI numbers from file_name (e.g., "D20230101T120000_IFCB134_00001.png" -> 1) + roi_numbers <- as.integer(gsub(".*_(\\d+)\\.png$", "\\1", classifications$file_name)) + + annotations_df <- data.frame( + sample_name = sample_name, + roi_number = roi_numbers, + class_name = classifications$class_name, + annotator = annotator, + timestamp = format(Sys.time(), "%Y-%m-%d %H:%M:%S"), + stringsAsFactors = FALSE + ) + + tryCatch({ + dbExecute(con, "BEGIN TRANSACTION") + + # Delete existing annotations for this sample (upsert semantics) + dbExecute(con, "DELETE FROM annotations WHERE sample_name = ?", + params = list(sample_name)) + dbWriteTable(con, "annotations", annotations_df, append = TRUE) + + # Save class list for this sample (preserves index order for .mat export) + dbExecute(con, "DELETE FROM class_lists WHERE sample_name = ?", + params = list(sample_name)) + if (length(class2use) > 0) { + class_list_df <- data.frame( + sample_name = sample_name, + class_index = seq_along(class2use), + class_name = class2use, + stringsAsFactors = FALSE + ) + dbWriteTable(con, "class_lists", class_list_df, append = TRUE) + } + + dbExecute(con, "COMMIT") + TRUE + }, error = function(e) { + tryCatch(dbExecute(con, "ROLLBACK"), error = function(e2) NULL) + warning("Failed to save annotations to database: ", e$message) + FALSE + }) +} + +#' Load annotations from the SQLite database +#' +#' Reads annotations for a single sample and returns a data frame in the same +#' format as \code{\link{load_from_mat}}. +#' +#' @param db_path Path to the SQLite database file +#' @param sample_name Sample name +#' @param roi_dimensions Data frame from \code{\link{read_roi_dimensions}} with +#' columns \code{roi_number}, \code{width}, \code{height}, \code{area} +#' @return Data frame with columns: file_name, class_name, score, width, height, +#' roi_area. Returns NULL if the sample has no annotations. +#' @export +#' @examples +#' \dontrun{ +#' dims <- read_roi_dimensions("/data/raw/2023/D20230101/D20230101T120000_IFCB134.adc") +#' db_path <- get_db_path("/data/manual") +#' classifications <- load_annotations_db(db_path, "D20230101T120000_IFCB134", dims) +#' } +load_annotations_db <- function(db_path, sample_name, roi_dimensions) { + if (!file.exists(db_path)) { + return(NULL) + } + + con <- dbConnect(SQLite(), db_path) + on.exit(dbDisconnect(con), add = TRUE) + + rows <- dbGetQuery(con, + "SELECT roi_number, class_name FROM annotations WHERE sample_name = ? ORDER BY roi_number", + params = list(sample_name) + ) + + if (nrow(rows) == 0) { + return(NULL) + } + + # Match ROI dimensions by roi_number (safe lookup with NA fallback) + roi_data <- lapply(rows$roi_number, function(rn) { + idx <- which(roi_dimensions$roi_number == rn) + if (length(idx) > 0) { + list(width = roi_dimensions$width[idx], + height = roi_dimensions$height[idx], + area = roi_dimensions$area[idx]) + } else { + list(width = NA_real_, height = NA_real_, area = NA_real_) + } + }) + + classifications <- data.frame( + file_name = sprintf("%s_%05d.png", sample_name, rows$roi_number), + class_name = rows$class_name, + score = NA_real_, + width = vapply(roi_data, `[[`, numeric(1), "width"), + height = vapply(roi_data, `[[`, numeric(1), "height"), + roi_area = vapply(roi_data, `[[`, numeric(1), "area"), + stringsAsFactors = FALSE + ) + + # Sort by area (descending) - consistent with load_from_mat + classifications[order(-classifications$roi_area), ] +} + +#' List samples with annotations in the database +#' +#' @param db_path Path to the SQLite database file +#' @return Character vector of sample names that have annotations +#' @export +#' @examples +#' \dontrun{ +#' db_path <- get_db_path("/data/manual") +#' samples <- list_annotated_samples_db(db_path) +#' } +list_annotated_samples_db <- function(db_path) { + if (!file.exists(db_path)) { + return(character()) + } + + con <- dbConnect(SQLite(), db_path) + on.exit(dbDisconnect(con), add = TRUE) + + # Check that the annotations table exists + tables <- dbGetQuery(con, "SELECT name FROM sqlite_master WHERE type='table'") + if (!"annotations" %in% tables$name) { + return(character()) + } + + result <- dbGetQuery(con, "SELECT DISTINCT sample_name FROM annotations ORDER BY sample_name") + result$sample_name +} + +#' Update the annotator name for one or more samples +#' +#' Changes the annotator field for all annotations belonging to the specified +#' sample(s). This is useful for correcting the annotator after bulk imports +#' or when transferring ownership of annotations. +#' +#' @param db_path Path to the SQLite database file +#' @param sample_names Character vector of sample names to update +#' @param annotator New annotator name +#' @return Named integer vector with the number of rows updated per sample. +#' Samples not found in the database are included with a count of 0. +#' @export +#' @examples +#' \dontrun{ +#' db_path <- get_db_path("/data/manual") +#' +#' # Update a single sample +#' update_annotator(db_path, "D20230101T120000_IFCB134", "Jane") +#' +#' # Update multiple samples at once +#' update_annotator(db_path, +#' c("D20230101T120000_IFCB134", "D20230202T080000_IFCB134"), +#' "Jane") +#' +#' # Update all annotated samples +#' all_samples <- list_annotated_samples_db(db_path) +#' update_annotator(db_path, all_samples, "Jane") +#' } +update_annotator <- function(db_path, sample_names, annotator) { + if (!file.exists(db_path)) { + stop("Database not found: ", db_path) + } + if (length(sample_names) == 0) { + return(integer(0)) + } + if (!is.character(annotator) || length(annotator) != 1 || is.na(annotator)) { + stop("annotator must be a single non-NA character string") + } + + con <- dbConnect(SQLite(), db_path) + on.exit(dbDisconnect(con), add = TRUE) + + counts <- vapply(sample_names, function(sn) { + res <- dbExecute(con, + "UPDATE annotations SET annotator = ? WHERE sample_name = ?", + params = list(annotator, sn) + ) + as.integer(res) + }, integer(1)) + + counts +} + +#' Import a .mat annotation file into the SQLite database +#' +#' Reads an existing .mat annotation file and writes its data into the SQLite +#' database. Useful for migrating existing annotations to the new backend. +#' +#' @param mat_path Path to the .mat annotation file +#' @param db_path Path to the SQLite database file +#' @param sample_name Sample name +#' @param class2use Character vector of class names +#' @param annotator Annotator name (defaults to \code{"imported"}) +#' @return TRUE on success, FALSE on failure +#' @export +#' @examples +#' \dontrun{ +#' import_mat_to_db( +#' mat_path = "/data/manual/D20230101T120000_IFCB134.mat", +#' db_path = get_db_path("/data/manual"), +#' sample_name = "D20230101T120000_IFCB134", +#' class2use = load_class_list("/data/class2use.mat") +#' ) +#' } +import_mat_to_db <- function(mat_path, db_path, sample_name, class2use, + annotator = "imported") { + if (!file.exists(mat_path)) { + warning("MAT file not found: ", mat_path) + return(FALSE) + } + + tryCatch({ + classlist <- ifcb_get_mat_variable(mat_path, variable_name = "classlist") + roi_numbers <- classlist[, 1] + class_indices <- classlist[, 2] + + class_names <- vapply(class_indices, function(idx) { + if (is.na(idx) || idx < 1 || idx > length(class2use)) { + "unclassified" + } else { + class2use[idx] + } + }, character(1)) + + # Build a classifications-like data frame for save_annotations_db + classifications <- data.frame( + file_name = sprintf("%s_%05d.png", sample_name, roi_numbers), + class_name = class_names, + stringsAsFactors = FALSE + ) + + save_annotations_db(db_path, sample_name, classifications, class2use, annotator) + }, error = function(e) { + warning("Failed to import MAT file: ", e$message) + FALSE + }) +} + +#' Export annotations from SQLite to a .mat file +#' +#' Reads annotations for a single sample from the database and writes a +#' MATLAB-compatible annotation file using \code{iRfcb::ifcb_create_manual_file}. +#' Requires Python with scipy. +#' +#' @param db_path Path to the SQLite database file +#' @param sample_name Sample name +#' @param output_folder Folder where the .mat file will be written +#' @return TRUE on success, FALSE on failure +#' @export +#' @examples +#' \dontrun{ +#' db_path <- get_db_path("/data/manual") +#' export_db_to_mat(db_path, "D20230101T120000_IFCB134", "/data/manual") +#' } +export_db_to_mat <- function(db_path, sample_name, output_folder) { + if (!file.exists(db_path)) { + warning("Database not found: ", db_path) + return(FALSE) + } + + con <- dbConnect(SQLite(), db_path) + on.exit(dbDisconnect(con), add = TRUE) + + # Get annotations for this sample + rows <- dbGetQuery(con, + "SELECT roi_number, class_name FROM annotations WHERE sample_name = ? ORDER BY roi_number", + params = list(sample_name) + ) + + if (nrow(rows) == 0) { + warning("No annotations found for sample: ", sample_name) + return(FALSE) + } + + # Get class list for this sample + class_list <- dbGetQuery(con, + "SELECT class_index, class_name FROM class_lists WHERE sample_name = ? ORDER BY class_index", + params = list(sample_name) + ) + + if (nrow(class_list) == 0) { + warning("No class list found for sample: ", sample_name) + return(FALSE) + } + + class2use <- class_list$class_name + + # Build classlist integer vector: map class names to indices + classlist_indices <- match(rows$class_name, class2use) + # Any unmatched classes default to 1 (typically "unclassified") + classlist_indices[is.na(classlist_indices)] <- 1L + + output_file <- file.path(output_folder, paste0(sample_name, ".mat")) + + tryCatch({ + dir.create(output_folder, recursive = TRUE, showWarnings = FALSE) + ifcb_create_manual_file( + roi_length = nrow(rows), + class2use = class2use, + output_file = output_file, + classlist = classlist_indices + ) + TRUE + }, error = function(e) { + warning("Failed to export to MAT: ", e$message) + FALSE + }) +} + +#' Bulk import .mat annotation files into the SQLite database +#' +#' Scans a folder for \code{.mat} annotation files (excluding classifier output +#' files matching \code{*_class*.mat}) and imports each into the database. +#' +#' @param mat_folder Folder containing .mat annotation files +#' @param db_path Path to the SQLite database file +#' @param class2use Character vector of class names +#' @param annotator Annotator name (defaults to \code{"imported"}) +#' @return Named list with counts: \code{success}, \code{failed}, \code{skipped} +#' @export +#' @examples +#' \dontrun{ +#' db_path <- get_db_path("/data/manual") +#' class2use <- load_class_list("/data/class2use.mat") +#' result <- import_all_mat_to_db("/data/manual", db_path, class2use) +#' cat(result$success, "imported,", result$failed, "failed,", result$skipped, "skipped\n") +#' } +import_all_mat_to_db <- function(mat_folder, db_path, class2use, + annotator = "imported") { + mat_files <- list.files(mat_folder, pattern = "\\.mat$", full.names = TRUE) + # Exclude classifier output files (*_class*.mat) and class2use files + mat_files <- mat_files[!grepl("_class", basename(mat_files))] + mat_files <- mat_files[!grepl("^class2use", basename(mat_files))] + + counts <- list(success = 0L, failed = 0L, skipped = 0L) + + if (length(mat_files) == 0) { + return(counts) + } + + # Get already-imported samples to allow skipping + existing <- list_annotated_samples_db(db_path) + + for (mat_path in mat_files) { + sample_name <- tools::file_path_sans_ext(basename(mat_path)) + + if (sample_name %in% existing) { + counts$skipped <- counts$skipped + 1L + next + } + + ok <- import_mat_to_db(mat_path, db_path, sample_name, class2use, annotator) + if (isTRUE(ok)) { + counts$success <- counts$success + 1L + } else { + counts$failed <- counts$failed + 1L + } + } + + counts +} + +#' Bulk export all annotated samples from SQLite to .mat files +#' +#' Exports every sample in the database to a MATLAB-compatible annotation file. +#' Requires Python with scipy. +#' +#' @param db_path Path to the SQLite database file +#' @param output_folder Folder where .mat files will be written +#' @return Named list with counts: \code{success}, \code{failed} +#' @export +#' @examples +#' \dontrun{ +#' db_path <- get_db_path("/data/manual") +#' result <- export_all_db_to_mat(db_path, "/data/manual") +#' cat(result$success, "exported,", result$failed, "failed\n") +#' } +export_all_db_to_mat <- function(db_path, output_folder) { + samples <- list_annotated_samples_db(db_path) + + counts <- list(success = 0L, failed = 0L) + + if (length(samples) == 0) { + return(counts) + } + + for (sample_name in samples) { + ok <- export_db_to_mat(db_path, sample_name, output_folder) + if (isTRUE(ok)) { + counts$success <- counts$success + 1L + } else { + counts$failed <- counts$failed + 1L + } + } + + counts +} + +#' Export annotated images from SQLite to class-organized PNG folders +#' +#' Reads annotations for a single sample from the database and extracts PNG +#' images from the ROI file, placing each image into a subfolder named after +#' its assigned class. +#' +#' @param db_path Path to the SQLite database file +#' @param sample_name Sample name +#' @param roi_path Path to the \code{.roi} file for this sample +#' @param png_folder Base output folder. Images are written to +#' \code{png_folder//} +#' @param skip_class Character vector of class names to exclude from export +#' (e.g. \code{"unclassified"}). Default \code{NULL} exports all classes. +#' @return TRUE on success, FALSE on failure +#' @export +#' @examples +#' \dontrun{ +#' db_path <- get_db_path("/data/manual") +#' export_db_to_png(db_path, "D20230101T120000_IFCB134", +#' "/data/raw/2023/D20230101/D20230101T120000_IFCB134.roi", +#' "/data/png_output", +#' skip_class = "unclassified") +#' } +export_db_to_png <- function(db_path, sample_name, roi_path, png_folder, + skip_class = NULL) { + if (!file.exists(db_path)) { + warning("Database not found: ", db_path) + return(FALSE) + } + if (!file.exists(roi_path)) { + warning("ROI file not found: ", roi_path) + return(FALSE) + } + + con <- dbConnect(SQLite(), db_path) + on.exit(dbDisconnect(con), add = TRUE) + + rows <- dbGetQuery(con, + "SELECT roi_number, class_name FROM annotations WHERE sample_name = ? ORDER BY roi_number", + params = list(sample_name) + ) + + if (nrow(rows) == 0) { + warning("No annotations found for sample: ", sample_name) + return(FALSE) + } + + # Filter out skipped classes + if (!is.null(skip_class) && length(skip_class) > 0) { + rows <- rows[!rows$class_name %in% skip_class, ] + if (nrow(rows) == 0) { + return(TRUE) # All ROIs were in skipped classes — nothing to export + } + } + + dir.create(png_folder, recursive = TRUE, showWarnings = FALSE) + + # Group ROIs by class name and extract each group with taxaname for subfolder + classes <- unique(rows$class_name) + + tryCatch({ + for (cls in classes) { + roi_numbers <- rows$roi_number[rows$class_name == cls] + ifcb_extract_pngs( + roi_file = roi_path, + out_folder = png_folder, + ROInumbers = roi_numbers, + taxaname = cls, + verbose = FALSE + ) + } + TRUE + }, error = function(e) { + warning("Failed to export PNGs for ", sample_name, ": ", e$message) + FALSE + }) +} + +#' Bulk export all annotated samples from SQLite to class-organized PNGs +#' +#' Exports every annotated sample in the database to PNG images organized +#' into class subfolders. +#' +#' @param db_path Path to the SQLite database file +#' @param png_folder Base output folder for PNGs +#' @param roi_path_map Named list mapping sample names to \code{.roi} file +#' paths. Samples without an entry are skipped. +#' @param skip_class Character vector of class names to exclude from export +#' (e.g. \code{"unclassified"}). Default \code{NULL} exports all classes. +#' @return Named list with counts: \code{success}, \code{failed}, \code{skipped} +#' @export +#' @examples +#' \dontrun{ +#' db_path <- get_db_path("/data/manual") +#' roi_map <- list("D20230101T120000_IFCB134" = "/data/raw/.../D20230101T120000_IFCB134.roi") +#' result <- export_all_db_to_png(db_path, "/data/png_output", roi_map, +#' skip_class = "unclassified") +#' cat(result$success, "exported,", result$failed, "failed,", result$skipped, "skipped\n") +#' } +export_all_db_to_png <- function(db_path, png_folder, roi_path_map, + skip_class = NULL) { + samples <- list_annotated_samples_db(db_path) + + counts <- list(success = 0L, failed = 0L, skipped = 0L) + + if (length(samples) == 0) { + return(counts) + } + + for (sample_name in samples) { + roi_path <- roi_path_map[[sample_name]] + if (is.null(roi_path) || !file.exists(roi_path)) { + counts$skipped <- counts$skipped + 1L + next + } + + ok <- export_db_to_png(db_path, sample_name, roi_path, png_folder, + skip_class = skip_class) + if (isTRUE(ok)) { + counts$success <- counts$success + 1L + } else { + counts$failed <- counts$failed + 1L + } + } + + counts +} diff --git a/R/sample_loading.R b/R/sample_loading.R index 3d3138e..ae94158 100644 --- a/R/sample_loading.R +++ b/R/sample_loading.R @@ -55,6 +55,27 @@ load_from_csv <- function(csv_path) { classifications } +#' Load classifications from SQLite database +#' +#' Reads annotations for a sample from the SQLite database and returns a data +#' frame in the same format as \code{\link{load_from_mat}}. +#' +#' @param db_path Path to the SQLite database file +#' @param sample_name Sample name (e.g., "D20230101T120000_IFCB134") +#' @param roi_dimensions Data frame from \code{\link{read_roi_dimensions}} +#' @return Data frame with columns: file_name, class_name, score, width, height, +#' roi_area. Returns NULL if the sample has no annotations in the database. +#' @export +#' @examples +#' \dontrun{ +#' dims <- read_roi_dimensions("/data/raw/2023/D20230101/D20230101T120000_IFCB134.adc") +#' db_path <- get_db_path("/data/manual") +#' classifications <- load_from_db(db_path, "D20230101T120000_IFCB134", dims) +#' } +load_from_db <- function(db_path, sample_name, roi_dimensions) { + load_annotations_db(db_path, sample_name, roi_dimensions) +} + #' Load classifications from existing MAT annotation file #' #' Reads a MATLAB annotation file (created by ClassiPyR or ifcb-analysis) diff --git a/R/sample_saving.R b/R/sample_saving.R index ee212b6..0857a87 100644 --- a/R/sample_saving.R +++ b/R/sample_saving.R @@ -3,31 +3,38 @@ #' @importFrom iRfcb ifcb_annotate_samples NULL -#' Save sample annotations to MAT and statistics files +#' Save sample annotations #' -#' Saves the current annotations for a sample, including: -#' - MAT file compatible with ifcb-analysis (requires Python) -#' - Validation statistics CSV files -#' - PNG images organized by class +#' Saves the current annotations for a sample. By default annotations are +#' stored in a local SQLite database (\code{annotations.sqlite} in the database +#' folder). Optionally, a MATLAB-compatible \code{.mat} file can also be +#' written (requires Python + scipy). #' #' @param sample_name Sample name (e.g., "D20230101T120000_IFCB134") #' @param classifications Current classifications data frame #' @param original_classifications Original classifications data frame (for comparison) #' @param changes_log Changes log data frame from \code{\link{create_empty_changes_log}} #' @param temp_png_folder Path to temporary folder with extracted PNG images -#' @param output_folder Output folder path for MAT files +#' @param output_folder Output folder path for MAT files and statistics #' @param png_output_folder PNG output folder path (organized by class) #' @param roi_folder ROI folder path (for ADC file location, used as fallback) #' @param class2use_path Path to class2use file +#' @param class2use Character vector of class names. When NULL (default), loaded +#' from \code{class2use_path}. #' @param annotator Annotator name for statistics #' @param adc_folder Direct path to the ADC folder. When provided, this is used #' instead of constructing the path via \code{\link{get_sample_paths}}. #' This supports non-standard folder structures. +#' @param save_format One of \code{"sqlite"} (default), \code{"mat"}, or +#' \code{"both"}. Controls which backend(s) are written. +#' @param db_folder Path to the database folder for SQLite storage. Defaults to +#' \code{\link{get_default_db_dir}()}. Should be a local filesystem path, +#' not a network drive. #' @return TRUE on success, FALSE on failure #' @export #' @examples #' \dontrun{ -#' # Save annotations for a sample +#' # Save annotations for a sample (default: SQLite) #' success <- save_sample_annotations( #' sample_name = "D20230101T120000_IFCB134", #' classifications = current_classifications, @@ -50,8 +57,11 @@ save_sample_annotations <- function(sample_name, png_output_folder, roi_folder, class2use_path, + class2use = NULL, annotator = "Unknown", - adc_folder = NULL) { + adc_folder = NULL, + save_format = "sqlite", + db_folder = get_default_db_dir()) { if (is.null(sample_name) || is.null(classifications) || is.null(class2use_path)) { return(FALSE) @@ -76,11 +86,10 @@ save_sample_annotations <- function(sample_name, dir.create(png_output_folder, recursive = TRUE) } - # Create temporary PNG folder structure for ifcb_annotate_samples + # Copy images to class subfolders temp_annotate_folder <- tempfile(pattern = "ifcb_annotate_") dir.create(temp_annotate_folder, recursive = TRUE) - # Copy images to class subfolders copy_images_to_class_folders( classifications = classifications, src_folder = file.path(temp_png_folder, sample_name), @@ -88,21 +97,34 @@ save_sample_annotations <- function(sample_name, output_folder = png_output_folder ) - # Find ADC folder: use provided path, or fall back to get_sample_paths() - if (is.null(adc_folder)) { - paths <- get_sample_paths(sample_name, roi_folder) - adc_folder <- paths$adc_folder + # Save to SQLite (fast, no Python needed) + if (save_format %in% c("sqlite", "both")) { + # Load class list if not provided + c2u <- class2use + if (is.null(c2u)) { + c2u <- load_class_list(class2use_path) + } + db_path <- get_db_path(db_folder) + save_annotations_db(db_path, sample_name, classifications, c2u, annotator) } - # Run annotation - save MAT to output folder directly - ifcb_annotate_samples( - png_folder = temp_annotate_folder, - adc_folder = adc_folder, - class2use_file = class2use_path, - output_folder = output_folder, - sample_names = sample_name, - remove_trailing_numbers = FALSE - ) + # Save to .mat (requires Python + scipy) + if (save_format %in% c("mat", "both")) { + # Find ADC folder: use provided path, or fall back to get_sample_paths() + if (is.null(adc_folder)) { + paths <- get_sample_paths(sample_name, roi_folder) + adc_folder <- paths$adc_folder + } + + ifcb_annotate_samples( + png_folder = temp_annotate_folder, + adc_folder = adc_folder, + class2use_file = class2use_path, + output_folder = output_folder, + sample_names = sample_name, + remove_trailing_numbers = FALSE + ) + } # Save statistics save_validation_statistics( diff --git a/R/utils.R b/R/utils.R index 315b297..d8c3717 100644 --- a/R/utils.R +++ b/R/utils.R @@ -11,6 +11,8 @@ #' @importFrom jsonlite fromJSON #' @importFrom reticulate py_available #' @importFrom dplyr filter +#' @importFrom DBI dbConnect dbDisconnect dbGetQuery dbWriteTable dbExecute +#' @importFrom RSQLite SQLite NULL #' Get ClassiPyR configuration directory @@ -33,6 +35,28 @@ get_config_dir <- function() { tools::R_user_dir("ClassiPyR", "config") } +#' Get default database directory +#' +#' Returns the default path for the SQLite annotations database. This is a +#' persistent, local, user-level directory that survives package reinstalls. +#' The database should be stored on a local filesystem, not on a network +#' drive, because SQLite file locking is unreliable over network filesystems. +#' +#' @return Path to the default database directory +#' @export +#' @seealso \code{\link{get_db_path}} for the full database file path +#' @examples +#' # Get the default database directory +#' db_dir <- get_default_db_dir() +#' print(db_dir) +get_default_db_dir <- function() { + # Check if running under R CMD check + if (nzchar(Sys.getenv("_R_CHECK_PACKAGE_NAME_", ""))) { + return(file.path(tempdir(), "ClassiPyR", "db")) + } + tools::R_user_dir("ClassiPyR", "data") +} + #' Get path to settings file #' #' Returns the path to the settings JSON file, creating the configuration @@ -113,8 +137,11 @@ load_file_index <- function() { #' #' @param roi_folder Path to ROI data folder. If NULL, read from saved settings. #' @param csv_folder Path to classification folder (CSV/MAT). If NULL, read from saved settings. -#' @param output_folder Path to output folder for annotations. If NULL, read from saved settings. +#' @param output_folder Path to output folder for MAT annotations. If NULL, read from saved settings. #' @param verbose If TRUE, print progress messages. Default TRUE. +#' @param db_folder Path to the database folder for SQLite annotations. If NULL, +#' read from saved settings; if not found in settings, defaults to +#' \code{\link{get_default_db_dir}()}. #' @return Invisibly returns the file index list, or NULL if roi_folder is invalid. #' @export #' @examples @@ -133,9 +160,11 @@ load_file_index <- function() { #' # Rscript -e 'ClassiPyR::rescan_file_index()' #' } rescan_file_index <- function(roi_folder = NULL, csv_folder = NULL, - output_folder = NULL, verbose = TRUE) { + output_folder = NULL, verbose = TRUE, + db_folder = NULL) { # Read from saved settings if not provided - if (is.null(roi_folder) || is.null(csv_folder) || is.null(output_folder)) { + if (is.null(roi_folder) || is.null(csv_folder) || is.null(output_folder) || + is.null(db_folder)) { settings_path <- get_settings_path() if (file.exists(settings_path)) { saved <- tryCatch( @@ -145,9 +174,15 @@ rescan_file_index <- function(roi_folder = NULL, csv_folder = NULL, if (is.null(roi_folder)) roi_folder <- saved$roi_folder if (is.null(csv_folder)) csv_folder <- saved$csv_folder if (is.null(output_folder)) output_folder <- saved$output_folder + if (is.null(db_folder)) db_folder <- saved$db_folder } } + # Fall back to default db folder if still NULL + if (is.null(db_folder)) { + db_folder <- get_default_db_dir() + } + # Validate ROI folder roi_valid <- !is.null(roi_folder) && length(roi_folder) == 1 && !isTRUE(is.na(roi_folder)) && nzchar(roi_folder) && dir.exists(roi_folder) @@ -219,15 +254,24 @@ rescan_file_index <- function(roi_folder = NULL, csv_folder = NULL, if (verbose) message(" Found ", length(classified), " classified samples") } - # Scan output folder for manual annotations + # Scan output folder for manual annotations (.mat files + SQLite database) annotated <- character() if (output_valid) { if (verbose) message("Scanning output folder: ", output_folder) + + # Scan .mat files output_mat_files <- list.files(output_folder, pattern = "\\.mat$", full.names = FALSE) manual_mat_files <- output_mat_files[!grepl("_class", output_mat_files)] - annotated <- tools::file_path_sans_ext(manual_mat_files) - annotated <- annotated[annotated %in% sample_names] + annotated_mat <- tools::file_path_sans_ext(manual_mat_files) + annotated_mat <- annotated_mat[annotated_mat %in% sample_names] + + # Scan SQLite database + db_path <- get_db_path(db_folder) + annotated_db <- list_annotated_samples_db(db_path) + annotated_db <- annotated_db[annotated_db %in% sample_names] + + annotated <- unique(c(annotated_mat, annotated_db)) if (verbose) message(" Found ", length(annotated), " annotated samples") } diff --git a/README.md b/README.md index 1624124..65a6a1d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # ClassiPyR ClassiPyR website [![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![R-CMD-check](https://github.com/EuropeanIFCBGroup/ClassiPyR/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/EuropeanIFCBGroup/ClassiPyR/actions/workflows/R-CMD-check.yaml) [![codecov](https://codecov.io/gh/EuropeanIFCBGroup/ClassiPyR/branch/main/graph/badge.svg)](https://app.codecov.io/gh/EuropeanIFCBGroup/ClassiPyR) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.18414999.svg)](https://doi.org/10.5281/zenodo.18414999) @@ -18,28 +17,41 @@ A Shiny application for manual (human) image classification and validation of Im - **Dual Mode**: Validate existing classifications or annotate from scratch - **Multiple Formats**: Load from CSV or MATLAB classifier output +- **SQLite Storage**: Annotations stored in a local SQLite database by default - no Python needed - **Efficient Workflow**: Drag-select, batch relabeling, class filtering -- **MATLAB Compatible**: Export for [ifcb-analysis](https://github.com/hsosik/ifcb-analysis) toolbox +- **MATLAB Compatible**: Optional `.mat` export for [ifcb-analysis](https://github.com/hsosik/ifcb-analysis) toolbox - **CNN Training Ready**: Organized PNG output by class - **Measure Tool**: Built-in ruler for image measurements -- **Cross-Platform**: Web-based folder browser works on all platforms +- **Cross-Platform**: Works on all platforms with no external dependencies ## Installation +Install the latest release from GitHub using the `remotes` package: + ```r -install.packages("remotes") -remotes::install_github("EuropeanIFCBGroup/ClassiPyR") +# Install remotes +if (!requireNamespace("remotes", quietly = TRUE)) install.packages("remotes") + +# Install ClassiPyR +remotes::install_github("EuropeanIFCBGroup/ClassiPyR", + ref = remotes::github_release()) ``` `ClassiPyR` depends on [iRfcb](https://github.com/EuropeanIFCBGroup/iRfcb) for IFCB data handling, which is installed automatically. -### Python Setup +### Python Setup (optional) + +Python is **not required** for the default workflow. Annotations are stored in a local SQLite database that works out of the box. -Python is required for saving annotations as MATLAB .mat files. If you only need to read existing .mat files or work with CSV files, this step is optional. +Python is only needed if you want to export annotations as MATLAB `.mat` files for use with [ifcb-analysis](https://github.com/hsosik/ifcb-analysis). To set up using `iRfcb`: ```r library(iRfcb) + +# Define a path were the venv will be installed venv_path = "/path/to/your/venv" + +# Install the venv ifcb_py_install(venv_path) ``` diff --git a/_pkgdown.yml b/_pkgdown.yml index d36c266..dda7f38 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -49,6 +49,7 @@ reference: - load_from_classifier_mat - load_from_csv - load_from_mat + - load_from_db - create_new_classifications - filter_to_extracted - title: Sample Saving @@ -57,6 +58,21 @@ reference: - save_sample_annotations - save_validation_statistics - copy_images_to_class_folders +- title: Database Backend + desc: SQLite database functions for annotation storage + contents: + - get_default_db_dir + - get_db_path + - save_annotations_db + - load_annotations_db + - list_annotated_samples_db + - update_annotator + - import_mat_to_db + - import_all_mat_to_db + - export_db_to_mat + - export_all_db_to_mat + - export_db_to_png + - export_all_db_to_png - title: File Index Cache desc: Functions for managing the file index cache for faster startup contents: diff --git a/inst/app/global.R b/inst/app/global.R index 32d00af..22e4a26 100644 --- a/inst/app/global.R +++ b/inst/app/global.R @@ -15,6 +15,8 @@ suppressPackageStartupMessages({ library(DT) library(jsonlite) library(reticulate) + library(DBI) + library(RSQLite) }) # Get version from package diff --git a/inst/app/server.R b/inst/app/server.R index 29eb1fc..69545ee 100644 --- a/inst/app/server.R +++ b/inst/app/server.R @@ -97,11 +97,13 @@ server <- function(input, output, session) { roi_folder = startup_wd, output_folder = startup_wd, png_output_folder = startup_wd, + db_folder = get_default_db_dir(), use_threshold = TRUE, pixels_per_micron = 3.4, # IFCB default resolution auto_sync = TRUE, # Automatically sync folders on startup class2use_path = NULL, # Path to class2use file for auto-loading - python_venv_path = NULL # NULL = use ./venv in working directory + python_venv_path = NULL, # NULL = use ./venv in working directory + save_format = "sqlite" # "sqlite" (default), "mat", or "both" ) if (file.exists(settings_file)) { @@ -151,10 +153,12 @@ server <- function(input, output, session) { roi_folder = saved_settings$roi_folder, output_folder = saved_settings$output_folder, png_output_folder = saved_settings$png_output_folder, + db_folder = saved_settings$db_folder, use_threshold = saved_settings$use_threshold, pixels_per_micron = saved_settings$pixels_per_micron, auto_sync = saved_settings$auto_sync, - python_venv_path = saved_settings$python_venv_path + python_venv_path = saved_settings$python_venv_path, + save_format = saved_settings$save_format ) # Initialize class dropdown with default class list on startup @@ -214,11 +218,10 @@ server <- function(input, output, session) { title = "Settings", size = "l", easyClose = TRUE, - - fileInput("class2use_file", "Class List File (.mat or .txt)", - accept = c(".mat", ".txt")), - - # Classification Folder (CSV and MAT) + + # ── Folder Paths ────────────────────────────────────────────── + h5("Folder Paths"), + div( style = "display: flex; gap: 5px; align-items: flex-end; margin-bottom: 15px;", div(style = "flex: 1;", @@ -227,8 +230,7 @@ server <- function(input, output, session) { shinyDirButton("browse_csv_folder", "Browse", "Select Classification Folder", class = "btn-outline-secondary", style = "margin-bottom: 15px;") ), - - # ROI Folder + div( style = "display: flex; gap: 5px; align-items: flex-end; margin-bottom: 15px;", div(style = "flex: 1;", @@ -237,18 +239,16 @@ server <- function(input, output, session) { shinyDirButton("browse_roi_folder", "Browse", "Select ROI Data Folder", class = "btn-outline-secondary", style = "margin-bottom: 15px;") ), - - # Output Folder + div( style = "display: flex; gap: 5px; align-items: flex-end; margin-bottom: 15px;", div(style = "flex: 1;", - textInput("cfg_output_folder", "Output Folder (MAT & CSV)", + textInput("cfg_output_folder", "Output Folder (MAT/statistics)", value = config$output_folder, width = "100%")), shinyDirButton("browse_output_folder", "Browse", "Select Output Folder", class = "btn-outline-secondary", style = "margin-bottom: 15px;") ), - - # PNG Output Folder + div( style = "display: flex; gap: 5px; align-items: flex-end; margin-bottom: 15px;", div(style = "flex: 1;", @@ -257,47 +257,105 @@ server <- function(input, output, session) { shinyDirButton("browse_png_folder", "Browse", "Select PNG Output Folder", class = "btn-outline-secondary", style = "margin-bottom: 15px;") ), - - hr(), - - # Sync options + + div( + style = "display: flex; gap: 5px; align-items: flex-end; margin-bottom: 5px;", + div(style = "flex: 1;", + textInput("cfg_db_folder", "Database Folder (SQLite)", + value = config$db_folder, width = "100%")), + shinyDirButton("browse_db_folder", "Browse", "Select Database Folder", + class = "btn-outline-secondary", style = "margin-bottom: 15px;") + ), + tags$small(class = "text-muted", style = "display: block; margin-bottom: 15px;", + "Must be a local drive. SQLite databases are", + tags$a(href = "https://www.sqlite.org/useovernet.html", target = "_blank", + "not safe on network filesystems"), + "due to unreliable file locking."), + checkboxInput("cfg_auto_sync", "Sync folders automatically on startup", value = config$auto_sync), tags$small(class = "text-muted", "When disabled, the app loads from cache on startup. Use the sync button to update manually."), - + hr(), - - # Classifier options - h5("Classifier Options"), + + # ── Class List ──────────────────────────────────────────────── + h5("Class List"), + + fileInput("class2use_file", "Load class list file (.mat or .txt)", + accept = c(".mat", ".txt")), + + div( + style = "display: flex; align-items: center; gap: 10px;", + actionButton("open_class_editor", "Edit Class List", + icon = icon("list"), class = "btn-outline-primary"), + tags$span(class = "text-muted", style = "font-size: 12px;", + textOutput("class_count_text", inline = TRUE)) + ), + + hr(), + + # ── Annotation Storage ──────────────────────────────────────── + h5("Annotation Storage"), + + selectInput("cfg_save_format", "Storage Format", + choices = c( + "SQLite (recommended)" = "sqlite", + "MAT file (MATLAB compatible)" = "mat", + "Both SQLite and MAT" = "both" + ), + selected = config$save_format), + tags$small(class = "text-muted", + "SQLite works out of the box. MAT files require Python and are only needed for ifcb-analysis compatibility."), + + hr(), + + # ── Import / Export ──────────────────────────────────────────── + h5("Import / Export"), + + div( + style = "display: flex; gap: 10px; margin-bottom: 8px;", + actionButton("import_mat_to_db_btn", "Import .mat \u2192 SQLite", + icon = icon("database"), class = "btn-outline-secondary btn-sm"), + actionButton("export_db_to_mat_btn", "Export SQLite \u2192 .mat", + icon = icon("file-export"), class = "btn-outline-secondary btn-sm"), + actionButton("export_db_to_png_btn", "Export SQLite \u2192 PNG", + icon = icon("image"), class = "btn-outline-secondary btn-sm") + ), + tags$small(class = "text-muted", + "Bulk import/export all annotated samples between storage formats.", + "PNG export extracts images into class-name subfolders."), + + div( + style = "margin-top: 8px;", + textInput("cfg_skip_class_png", "Skip class in PNG export", + value = if (!is.null(rv$class2use) && length(rv$class2use) > 0) rv$class2use[1] else "", + width = "250px"), + tags$small(class = "text-muted", + "Images with this class are excluded from PNG export.", + "Pre-filled with the first class in your class list.", + "Leave empty to export all classes.") + ), + + hr(), + + # ── IFCB Options ────────────────────────────────────────────── + h5("IFCB Options"), + checkboxInput("cfg_use_threshold", "Apply classification threshold", value = config$use_threshold), tags$small(class = "text-muted", - "When enabled, classifications below the confidence threshold are marked as 'unclassified'"), - - hr(), - - # Image resolution setting - h5("Image Resolution"), + "Only applies to ifcb-analysis MATLAB classifier output (*_class*.mat).", + "When enabled, classifications below the confidence threshold are marked as 'unclassified'."), + div( - style = "display: flex; gap: 10px; align-items: center;", + style = "display: flex; gap: 10px; align-items: center; margin-top: 10px;", numericInput("cfg_pixels_per_micron", "Pixels per micron", value = config$pixels_per_micron, min = 0.1, max = 20, step = 0.1, width = "150px"), - tags$small(class = "text-muted", "IFCB default: 3.4 px/µm") - ), - - hr(), - - # Class list editor button - div( - style = "display: flex; align-items: center; gap: 10px;", - actionButton("open_class_editor", "Edit Class List", - icon = icon("list"), class = "btn-outline-primary"), - tags$span(class = "text-muted", style = "font-size: 12px;", - textOutput("class_count_text", inline = TRUE)) + tags$small(class = "text-muted", "Scale calibration for the measuring tool. IFCB default: 3.4 px/\u00b5m.") ), - + footer = tagList( modalButton("Cancel"), actionButton("save_settings", "Save Settings", class = "btn-primary") @@ -313,6 +371,8 @@ server <- function(input, output, session) { roots = make_dynamic_roots("cfg_roi_folder"), session = session) shinyDirChoose(input, "browse_output_folder", roots = make_dynamic_roots("cfg_output_folder"), session = session) + shinyDirChoose(input, "browse_db_folder", + roots = make_dynamic_roots("cfg_db_folder"), session = session) shinyDirChoose(input, "browse_png_folder", roots = make_dynamic_roots("cfg_png_output_folder"), session = session) @@ -344,6 +404,15 @@ server <- function(input, output, session) { } }) + observeEvent(input$browse_db_folder, { + if (!is.integer(input$browse_db_folder)) { + folder <- parseDirPath(get_browse_volumes(input$cfg_db_folder), input$browse_db_folder) + if (length(folder) > 0) { + updateTextInput(session, "cfg_db_folder", value = as.character(folder)) + } + } + }) + observeEvent(input$browse_png_folder, { if (!is.integer(input$browse_png_folder)) { folder <- parseDirPath(get_browse_volumes(input$cfg_png_output_folder), input$browse_png_folder) @@ -614,10 +683,12 @@ server <- function(input, output, session) { config$roi_folder <- input$cfg_roi_folder config$output_folder <- input$cfg_output_folder config$png_output_folder <- input$cfg_png_output_folder + config$db_folder <- input$cfg_db_folder config$use_threshold <- input$cfg_use_threshold config$pixels_per_micron <- input$cfg_pixels_per_micron config$auto_sync <- input$cfg_auto_sync - + config$save_format <- input$cfg_save_format + # Persist settings to file for next session # python_venv_path is kept from config (set via run_app() or previous save) persist_settings(list( @@ -625,9 +696,11 @@ server <- function(input, output, session) { roi_folder = input$cfg_roi_folder, output_folder = input$cfg_output_folder, png_output_folder = input$cfg_png_output_folder, + db_folder = input$cfg_db_folder, use_threshold = input$cfg_use_threshold, pixels_per_micron = input$cfg_pixels_per_micron, auto_sync = input$cfg_auto_sync, + save_format = input$cfg_save_format, class2use_path = rv$class2use_path, python_venv_path = config$python_venv_path )) @@ -644,7 +717,112 @@ server <- function(input, output, session) { rescan_trigger(rescan_trigger() + 1) } }) - + + # Import .mat -> SQLite bulk handler + observeEvent(input$import_mat_to_db_btn, { + if (is.null(config$output_folder) || config$output_folder == "") { + showNotification("Output folder is not configured. Set it in Settings first.", + type = "error") + return() + } + if (is.null(rv$class2use) || length(rv$class2use) == 0) { + showNotification("No class list loaded. Load a class list first.", + type = "error") + return() + } + + db_path <- get_db_path(config$db_folder) + annotator <- if (!is.null(input$annotator_name) && nzchar(input$annotator_name)) { + input$annotator_name + } else { + "imported" + } + + withProgress(message = "Importing .mat files to SQLite...", { + result <- import_all_mat_to_db(config$output_folder, db_path, + rv$class2use, annotator) + }) + + showNotification( + sprintf("Import complete: %d imported, %d failed, %d skipped (already in DB).", + result$success, result$failed, result$skipped), + type = if (result$failed > 0) "warning" else "message", + duration = 8 + ) + + # Trigger file index rescan to update sample list + if (result$success > 0) { + rescan_trigger(rescan_trigger() + 1) + } + }) + + # Export SQLite -> .mat bulk handler + observeEvent(input$export_db_to_mat_btn, { + if (is.null(config$output_folder) || config$output_folder == "") { + showNotification("Output folder is not configured. Set it in Settings first.", + type = "error") + return() + } + if (!python_available) { + showNotification("Python is not available. Export to .mat requires Python with scipy.", + type = "error") + return() + } + + db_path <- get_db_path(config$db_folder) + + withProgress(message = "Exporting SQLite to .mat files...", { + result <- export_all_db_to_mat(db_path, config$output_folder) + }) + + showNotification( + sprintf("Export complete: %d exported, %d failed.", result$success, result$failed), + type = if (result$failed > 0) "warning" else "message", + duration = 8 + ) + }) + + # Export SQLite -> PNG bulk handler + observeEvent(input$export_db_to_png_btn, { + if (is.null(config$png_output_folder) || config$png_output_folder == "") { + showNotification("PNG Output Folder is not configured. Set it in Settings first.", + type = "error") + return() + } + if (is.null(config$output_folder) || config$output_folder == "") { + showNotification("Output folder is not configured. Set it in Settings first.", + type = "error") + return() + } + + db_path <- get_db_path(config$db_folder) + current_roi_map <- roi_path_map() + + if (length(current_roi_map) == 0) { + showNotification("No ROI file index available. Click Sync first.", + type = "error") + return() + } + + skip <- if (!is.null(input$cfg_skip_class_png) && nzchar(input$cfg_skip_class_png)) { + input$cfg_skip_class_png + } else { + NULL + } + + withProgress(message = "Exporting PNGs from SQLite...", { + result <- export_all_db_to_png(db_path, config$png_output_folder, + current_roi_map, skip_class = skip) + }) + + showNotification( + sprintf("PNG export complete: %d exported, %d failed, %d skipped (ROI not found).", + result$success, result$failed, result$skipped), + type = if (result$failed > 0) "warning" else "message", + duration = 8 + ) + }) + # ============================================================================ # UI Outputs - Warnings and Indicators # ============================================================================ @@ -673,15 +851,17 @@ server <- function(input, output, session) { }) output$python_warning <- renderUI({ - if (!python_available) { + needs_python <- config$save_format %in% c("mat", "both") + if (!python_available && needs_python) { div( class = "alert alert-warning", style = "margin-top: 10px; padding: 8px; font-size: 12px;", "Python not available. Saving .mat files will not work. ", - "This is only required if you use the ", + "Switch to SQLite storage format in Settings, or install Python: ", + "run ifcb_py_install() in R console. ", + "MAT files are only needed for ", tags$a(href = "https://github.com/hsosik/ifcb-analysis", target = "_blank", "ifcb-analysis"), - " MATLAB toolbox (Sosik & Olson, 2007). ", - "Run ifcb_py_install() in R console to enable." + " compatibility." ) } }) @@ -859,7 +1039,7 @@ server <- function(input, output, session) { # Switch from validation mode to annotation mode observeEvent(input$switch_to_annotation, { req(rv$current_sample, rv$has_both_modes) - + sample_name <- rv$current_sample roi_path <- roi_path_map()[[sample_name]] if (is.null(roi_path)) { @@ -867,12 +1047,21 @@ server <- function(input, output, session) { return() } adc_path <- sub("\\.roi$", ".adc", roi_path) + + # Try SQLite first, then .mat + db_path <- get_db_path(config$db_folder) annotation_mat_path <- file.path(config$output_folder, paste0(sample_name, ".mat")) - - if (file.exists(annotation_mat_path)) { + has_db <- sample_name %in% list_annotated_samples_db(db_path) + has_mat <- file.exists(annotation_mat_path) + + if (has_db || has_mat) { roi_dims <- read_roi_dimensions(adc_path) - classifications <- load_from_mat(annotation_mat_path, sample_name, rv$class2use, roi_dims) - + if (has_db) { + classifications <- load_from_db(db_path, sample_name, roi_dims) + } else { + classifications <- load_from_mat(annotation_mat_path, sample_name, rv$class2use, roi_dims) + } + rv$original_classifications <- classifications rv$classifications <- classifications rv$is_annotation_mode <- TRUE @@ -880,7 +1069,7 @@ server <- function(input, output, session) { rv$selected_images <- character() rv$current_page <- 1 rv$changes_log <- create_empty_changes_log() - + # Update class filter dropdown available_classes <- sort(unique(classifications$class_name)) unmatched <- setdiff(available_classes, c(rv$class2use, "unclassified")) @@ -890,7 +1079,7 @@ server <- function(input, output, session) { updateSelectInput(session, "class_filter", choices = c("All" = "all", setNames(available_classes, display_names)), selected = "all") - + showNotification("Switched to Manual annotation mode", type = "message") } else { showNotification("No manual annotation file found", type = "warning") @@ -966,6 +1155,7 @@ server <- function(input, output, session) { roi_folder = config$roi_folder, output_folder = config$output_folder, png_output_folder = config$png_output_folder, + db_folder = config$db_folder, use_threshold = config$use_threshold, class2use_path = persistent_path )) @@ -1374,8 +1564,11 @@ server <- function(input, output, session) { png_output_folder = config$png_output_folder, roi_folder = config$roi_folder, class2use_path = rv$class2use_path, + class2use = rv$class2use, annotator = input$annotator_name, - adc_folder = adc_folder_for_save + adc_folder = adc_folder_for_save, + save_format = config$save_format, + db_folder = config$db_folder ) # Only update annotated samples list if changes were actually saved if (isTRUE(saved)) { @@ -1416,30 +1609,39 @@ server <- function(input, output, session) { tryCatch({ annotation_mat_path <- file.path(config$output_folder, paste0(sample_name, ".mat")) - has_existing_annotation <- file.exists(annotation_mat_path) + db_path <- get_db_path(config$db_folder) + has_db_annotation <- sample_name %in% list_annotated_samples_db(db_path) + has_mat_annotation <- file.exists(annotation_mat_path) + has_existing_annotation <- has_db_annotation || has_mat_annotation has_classification <- has_csv || has_classifier_mat - + # Track if sample has both modes available rv$has_both_modes <- has_existing_annotation && has_classification rv$using_manual_mode <- has_existing_annotation # Default to manual if available - + # Variable to hold mode message for notification (shown after filtering) mode_message <- NULL - + # Priority: Manual annotation > Classification > New annotation + # Within manual annotations: SQLite first (faster), then .mat fallback if (has_existing_annotation) { # ANNOTATION MODE - from existing manual annotation (priority when both exist) if (!file.exists(adc_path)) { showNotification(paste("ADC file not found:", adc_path), type = "error") return(FALSE) } - + roi_dims <- read_roi_dimensions(adc_path) - classifications <- load_from_mat(annotation_mat_path, sample_name, rv$class2use, roi_dims) + + if (has_db_annotation) { + classifications <- load_from_db(db_path, sample_name, roi_dims) + } else { + classifications <- load_from_mat(annotation_mat_path, sample_name, rv$class2use, roi_dims) + } rv$is_annotation_mode <- TRUE - + mode_message <- if (rv$has_both_modes) "Manual mode (switch available)" else "Resumed" - + } else if (has_csv) { # VALIDATION MODE - from CSV classifications <- load_from_csv(csv_path) @@ -2044,63 +2246,54 @@ server <- function(input, output, session) { } tryCatch({ - output_folder <- config$output_folder - stats_folder <- file.path(config$output_folder, "validation_statistics") - png_output_folder <- config$png_output_folder - - if (!dir.exists(output_folder)) dir.create(output_folder, recursive = TRUE) - if (!dir.exists(stats_folder)) dir.create(stats_folder, recursive = TRUE) - if (!dir.exists(png_output_folder)) dir.create(png_output_folder, recursive = TRUE) - - temp_annotate_folder <- tempfile(pattern = "ifcb_annotate_") - dir.create(temp_annotate_folder, recursive = TRUE) - - withProgress(message = "Copying images...", { - copy_images_to_class_folders( - classifications = rv$classifications, - src_folder = file.path(rv$temp_png_folder, rv$current_sample), - temp_folder = temp_annotate_folder, - output_folder = png_output_folder - ) - }) - roi_path <- roi_path_map()[[rv$current_sample]] adc_folder <- if (!is.null(roi_path)) dirname(roi_path) else NULL if (is.null(adc_folder)) { showNotification("Cannot find ROI data folder for this sample", type = "error") return() } - - withProgress(message = "Saving MAT file...", { - result <- ifcb_annotate_samples( - png_folder = temp_annotate_folder, + + save_fmt <- config$save_format + progress_msg <- switch(save_fmt, + sqlite = "Saving to database...", + mat = "Saving MAT file...", + both = "Saving annotations...", + "Saving..." + ) + + withProgress(message = progress_msg, { + result <- save_sample_annotations( + sample_name = rv$current_sample, + classifications = rv$classifications, + original_classifications = rv$original_classifications, + changes_log = rv$changes_log, + temp_png_folder = rv$temp_png_folder, + output_folder = config$output_folder, + png_output_folder = config$png_output_folder, + roi_folder = config$roi_folder, + class2use_path = rv$class2use_path, + class2use = rv$class2use, + annotator = annotator, adc_folder = adc_folder, - class2use_file = rv$class2use_path, - output_folder = output_folder, - sample_names = rv$current_sample, - remove_trailing_numbers = FALSE + save_format = save_fmt, + db_folder = config$db_folder ) }) - - save_validation_statistics( - sample_name = rv$current_sample, - classifications = rv$classifications, - original_classifications = rv$original_classifications, - stats_folder = stats_folder, - annotator = annotator - ) - - unlink(temp_annotate_folder, recursive = TRUE) - + + if (!isTRUE(result)) { + showNotification("Save returned no changes", type = "warning") + return() + } + # Update annotated samples list to reflect new manual annotation current_annotated <- annotated_samples() if (!rv$current_sample %in% current_annotated) { annotated_samples(c(current_annotated, rv$current_sample)) update_current_sample_status(rv$current_sample) } - + showNotification(paste("Saved to", config$output_folder), type = "message") - + }, error = function(e) { showNotification(paste("Error saving:", e$message), type = "error") }) @@ -2384,7 +2577,9 @@ server <- function(input, output, session) { png_output_folder = png_output_folder, roi_folder = roi_folder, class2use_path = class2use_path, - annotator = annotator + annotator = annotator, + save_format = isolate(config$save_format), + db_folder = isolate(config$db_folder) ) }, error = function(e) { message("Failed to auto-save ", sample_name, " on session end: ", e$message) diff --git a/man/export_all_db_to_mat.Rd b/man/export_all_db_to_mat.Rd new file mode 100644 index 0000000..4209e06 --- /dev/null +++ b/man/export_all_db_to_mat.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/database.R +\name{export_all_db_to_mat} +\alias{export_all_db_to_mat} +\title{Bulk export all annotated samples from SQLite to .mat files} +\usage{ +export_all_db_to_mat(db_path, output_folder) +} +\arguments{ +\item{db_path}{Path to the SQLite database file} + +\item{output_folder}{Folder where .mat files will be written} +} +\value{ +Named list with counts: \code{success}, \code{failed} +} +\description{ +Exports every sample in the database to a MATLAB-compatible annotation file. +Requires Python with scipy. +} +\examples{ +\dontrun{ +db_path <- get_db_path("/data/manual") +result <- export_all_db_to_mat(db_path, "/data/manual") +cat(result$success, "exported,", result$failed, "failed\n") +} +} diff --git a/man/export_all_db_to_png.Rd b/man/export_all_db_to_png.Rd new file mode 100644 index 0000000..633ea75 --- /dev/null +++ b/man/export_all_db_to_png.Rd @@ -0,0 +1,35 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/database.R +\name{export_all_db_to_png} +\alias{export_all_db_to_png} +\title{Bulk export all annotated samples from SQLite to class-organized PNGs} +\usage{ +export_all_db_to_png(db_path, png_folder, roi_path_map, skip_class = NULL) +} +\arguments{ +\item{db_path}{Path to the SQLite database file} + +\item{png_folder}{Base output folder for PNGs} + +\item{roi_path_map}{Named list mapping sample names to \code{.roi} file +paths. Samples without an entry are skipped.} + +\item{skip_class}{Character vector of class names to exclude from export +(e.g. \code{"unclassified"}). Default \code{NULL} exports all classes.} +} +\value{ +Named list with counts: \code{success}, \code{failed}, \code{skipped} +} +\description{ +Exports every annotated sample in the database to PNG images organized +into class subfolders. +} +\examples{ +\dontrun{ +db_path <- get_db_path("/data/manual") +roi_map <- list("D20230101T120000_IFCB134" = "/data/raw/.../D20230101T120000_IFCB134.roi") +result <- export_all_db_to_png(db_path, "/data/png_output", roi_map, + skip_class = "unclassified") +cat(result$success, "exported,", result$failed, "failed,", result$skipped, "skipped\n") +} +} diff --git a/man/export_db_to_mat.Rd b/man/export_db_to_mat.Rd new file mode 100644 index 0000000..64f7f04 --- /dev/null +++ b/man/export_db_to_mat.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/database.R +\name{export_db_to_mat} +\alias{export_db_to_mat} +\title{Export annotations from SQLite to a .mat file} +\usage{ +export_db_to_mat(db_path, sample_name, output_folder) +} +\arguments{ +\item{db_path}{Path to the SQLite database file} + +\item{sample_name}{Sample name} + +\item{output_folder}{Folder where the .mat file will be written} +} +\value{ +TRUE on success, FALSE on failure +} +\description{ +Reads annotations for a single sample from the database and writes a +MATLAB-compatible annotation file using \code{iRfcb::ifcb_create_manual_file}. +Requires Python with scipy. +} +\examples{ +\dontrun{ +db_path <- get_db_path("/data/manual") +export_db_to_mat(db_path, "D20230101T120000_IFCB134", "/data/manual") +} +} diff --git a/man/export_db_to_png.Rd b/man/export_db_to_png.Rd new file mode 100644 index 0000000..5696220 --- /dev/null +++ b/man/export_db_to_png.Rd @@ -0,0 +1,38 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/database.R +\name{export_db_to_png} +\alias{export_db_to_png} +\title{Export annotated images from SQLite to class-organized PNG folders} +\usage{ +export_db_to_png(db_path, sample_name, roi_path, png_folder, skip_class = NULL) +} +\arguments{ +\item{db_path}{Path to the SQLite database file} + +\item{sample_name}{Sample name} + +\item{roi_path}{Path to the \code{.roi} file for this sample} + +\item{png_folder}{Base output folder. Images are written to +\code{png_folder//}} + +\item{skip_class}{Character vector of class names to exclude from export +(e.g. \code{"unclassified"}). Default \code{NULL} exports all classes.} +} +\value{ +TRUE on success, FALSE on failure +} +\description{ +Reads annotations for a single sample from the database and extracts PNG +images from the ROI file, placing each image into a subfolder named after +its assigned class. +} +\examples{ +\dontrun{ +db_path <- get_db_path("/data/manual") +export_db_to_png(db_path, "D20230101T120000_IFCB134", + "/data/raw/2023/D20230101/D20230101T120000_IFCB134.roi", + "/data/png_output", + skip_class = "unclassified") +} +} diff --git a/man/figures/settings-dialog.png b/man/figures/settings-dialog.png index ca617f5d03f6ff326b8c7f5c4726f98b776f46e9..b9db08e94f1a9b093ea407b516dfe163eb05f71a 100644 GIT binary patch literal 83806 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 aSUc9Qzt6Dv>~t4SMd)iAX%%VQxc3j1L6cJe literal 160200 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%0D5SdnKlP0D$sk@d2C^X~5M?M(QY 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

@O`JksN7*#X)Q9_lA= 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 SaB1YGJE<4);`yTbzW)acW7gdO diff --git a/man/get_db_path.Rd b/man/get_db_path.Rd new file mode 100644 index 0000000..0ddedd3 --- /dev/null +++ b/man/get_db_path.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/database.R +\name{get_db_path} +\alias{get_db_path} +\title{Get path to the annotations SQLite database} +\usage{ +get_db_path(db_folder) +} +\arguments{ +\item{db_folder}{Path to the database directory. Defaults to +\code{\link{get_default_db_dir}()}, a persistent local directory.} +} +\value{ +Path to the SQLite database file +} +\description{ +Returns the path to \code{annotations.sqlite} in the given database +directory. The database directory should be on a local filesystem, not a +network drive, because +\href{https://www.sqlite.org/useovernet.html}{SQLite file locking is +unreliable over network filesystems}. +} +\examples{ +# Use the default local database directory +get_db_path(get_default_db_dir()) + +# Or specify a custom directory +get_db_path("/data/local_db") +} +\seealso{ +\code{\link{get_default_db_dir}} for the default database directory +} diff --git a/man/get_default_db_dir.Rd b/man/get_default_db_dir.Rd new file mode 100644 index 0000000..6e954de --- /dev/null +++ b/man/get_default_db_dir.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{get_default_db_dir} +\alias{get_default_db_dir} +\title{Get default database directory} +\usage{ +get_default_db_dir() +} +\value{ +Path to the default database directory +} +\description{ +Returns the default path for the SQLite annotations database. This is a +persistent, local, user-level directory that survives package reinstalls. +The database should be stored on a local filesystem, not on a network +drive, because SQLite file locking is unreliable over network filesystems. +} +\examples{ +# Get the default database directory +db_dir <- get_default_db_dir() +print(db_dir) +} +\seealso{ +\code{\link{get_db_path}} for the full database file path +} diff --git a/man/import_all_mat_to_db.Rd b/man/import_all_mat_to_db.Rd new file mode 100644 index 0000000..f7e31c7 --- /dev/null +++ b/man/import_all_mat_to_db.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/database.R +\name{import_all_mat_to_db} +\alias{import_all_mat_to_db} +\title{Bulk import .mat annotation files into the SQLite database} +\usage{ +import_all_mat_to_db(mat_folder, db_path, class2use, annotator = "imported") +} +\arguments{ +\item{mat_folder}{Folder containing .mat annotation files} + +\item{db_path}{Path to the SQLite database file} + +\item{class2use}{Character vector of class names} + +\item{annotator}{Annotator name (defaults to \code{"imported"})} +} +\value{ +Named list with counts: \code{success}, \code{failed}, \code{skipped} +} +\description{ +Scans a folder for \code{.mat} annotation files (excluding classifier output +files matching \code{*_class*.mat}) and imports each into the database. +} +\examples{ +\dontrun{ +db_path <- get_db_path("/data/manual") +class2use <- load_class_list("/data/class2use.mat") +result <- import_all_mat_to_db("/data/manual", db_path, class2use) +cat(result$success, "imported,", result$failed, "failed,", result$skipped, "skipped\n") +} +} diff --git a/man/import_mat_to_db.Rd b/man/import_mat_to_db.Rd new file mode 100644 index 0000000..8389b49 --- /dev/null +++ b/man/import_mat_to_db.Rd @@ -0,0 +1,42 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/database.R +\name{import_mat_to_db} +\alias{import_mat_to_db} +\title{Import a .mat annotation file into the SQLite database} +\usage{ +import_mat_to_db( + mat_path, + db_path, + sample_name, + class2use, + annotator = "imported" +) +} +\arguments{ +\item{mat_path}{Path to the .mat annotation file} + +\item{db_path}{Path to the SQLite database file} + +\item{sample_name}{Sample name} + +\item{class2use}{Character vector of class names} + +\item{annotator}{Annotator name (defaults to \code{"imported"})} +} +\value{ +TRUE on success, FALSE on failure +} +\description{ +Reads an existing .mat annotation file and writes its data into the SQLite +database. Useful for migrating existing annotations to the new backend. +} +\examples{ +\dontrun{ +import_mat_to_db( + mat_path = "/data/manual/D20230101T120000_IFCB134.mat", + db_path = get_db_path("/data/manual"), + sample_name = "D20230101T120000_IFCB134", + class2use = load_class_list("/data/class2use.mat") +) +} +} diff --git a/man/init_db_schema.Rd b/man/init_db_schema.Rd new file mode 100644 index 0000000..efc8a94 --- /dev/null +++ b/man/init_db_schema.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/database.R +\name{init_db_schema} +\alias{init_db_schema} +\title{Initialize the annotations database schema} +\usage{ +init_db_schema(con) +} +\arguments{ +\item{con}{A DBI connection object} +} +\value{ +NULL (called for side effects) +} +\description{ +Creates the \code{annotations} and \code{class_lists} tables if they do not +already exist. +} +\keyword{internal} diff --git a/man/list_annotated_samples_db.Rd b/man/list_annotated_samples_db.Rd new file mode 100644 index 0000000..c007624 --- /dev/null +++ b/man/list_annotated_samples_db.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/database.R +\name{list_annotated_samples_db} +\alias{list_annotated_samples_db} +\title{List samples with annotations in the database} +\usage{ +list_annotated_samples_db(db_path) +} +\arguments{ +\item{db_path}{Path to the SQLite database file} +} +\value{ +Character vector of sample names that have annotations +} +\description{ +List samples with annotations in the database +} +\examples{ +\dontrun{ +db_path <- get_db_path("/data/manual") +samples <- list_annotated_samples_db(db_path) +} +} diff --git a/man/load_annotations_db.Rd b/man/load_annotations_db.Rd new file mode 100644 index 0000000..62eea24 --- /dev/null +++ b/man/load_annotations_db.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/database.R +\name{load_annotations_db} +\alias{load_annotations_db} +\title{Load annotations from the SQLite database} +\usage{ +load_annotations_db(db_path, sample_name, roi_dimensions) +} +\arguments{ +\item{db_path}{Path to the SQLite database file} + +\item{sample_name}{Sample name} + +\item{roi_dimensions}{Data frame from \code{\link{read_roi_dimensions}} with +columns \code{roi_number}, \code{width}, \code{height}, \code{area}} +} +\value{ +Data frame with columns: file_name, class_name, score, width, height, + roi_area. Returns NULL if the sample has no annotations. +} +\description{ +Reads annotations for a single sample and returns a data frame in the same +format as \code{\link{load_from_mat}}. +} +\examples{ +\dontrun{ +dims <- read_roi_dimensions("/data/raw/2023/D20230101/D20230101T120000_IFCB134.adc") +db_path <- get_db_path("/data/manual") +classifications <- load_annotations_db(db_path, "D20230101T120000_IFCB134", dims) +} +} diff --git a/man/load_from_db.Rd b/man/load_from_db.Rd new file mode 100644 index 0000000..50ffe55 --- /dev/null +++ b/man/load_from_db.Rd @@ -0,0 +1,30 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/sample_loading.R +\name{load_from_db} +\alias{load_from_db} +\title{Load classifications from SQLite database} +\usage{ +load_from_db(db_path, sample_name, roi_dimensions) +} +\arguments{ +\item{db_path}{Path to the SQLite database file} + +\item{sample_name}{Sample name (e.g., "D20230101T120000_IFCB134")} + +\item{roi_dimensions}{Data frame from \code{\link{read_roi_dimensions}}} +} +\value{ +Data frame with columns: file_name, class_name, score, width, height, + roi_area. Returns NULL if the sample has no annotations in the database. +} +\description{ +Reads annotations for a sample from the SQLite database and returns a data +frame in the same format as \code{\link{load_from_mat}}. +} +\examples{ +\dontrun{ +dims <- read_roi_dimensions("/data/raw/2023/D20230101/D20230101T120000_IFCB134.adc") +db_path <- get_db_path("/data/manual") +classifications <- load_from_db(db_path, "D20230101T120000_IFCB134", dims) +} +} diff --git a/man/rescan_file_index.Rd b/man/rescan_file_index.Rd index 8b2018d..30876d0 100644 --- a/man/rescan_file_index.Rd +++ b/man/rescan_file_index.Rd @@ -8,7 +8,8 @@ rescan_file_index( roi_folder = NULL, csv_folder = NULL, output_folder = NULL, - verbose = TRUE + verbose = TRUE, + db_folder = NULL ) } \arguments{ @@ -16,9 +17,13 @@ rescan_file_index( \item{csv_folder}{Path to classification folder (CSV/MAT). If NULL, read from saved settings.} -\item{output_folder}{Path to output folder for annotations. If NULL, read from saved settings.} +\item{output_folder}{Path to output folder for MAT annotations. If NULL, read from saved settings.} \item{verbose}{If TRUE, print progress messages. Default TRUE.} + +\item{db_folder}{Path to the database folder for SQLite annotations. If NULL, +read from saved settings; if not found in settings, defaults to +\code{\link{get_default_db_dir}()}.} } \value{ Invisibly returns the file index list, or NULL if roi_folder is invalid. diff --git a/man/save_annotations_db.Rd b/man/save_annotations_db.Rd new file mode 100644 index 0000000..babdd6c --- /dev/null +++ b/man/save_annotations_db.Rd @@ -0,0 +1,41 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/database.R +\name{save_annotations_db} +\alias{save_annotations_db} +\title{Save annotations to the SQLite database} +\usage{ +save_annotations_db( + db_path, + sample_name, + classifications, + class2use, + annotator = "Unknown" +) +} +\arguments{ +\item{db_path}{Path to the SQLite database file} + +\item{sample_name}{Sample name (e.g., \code{"D20230101T120000_IFCB134"})} + +\item{classifications}{Data frame with at least \code{file_name} and +\code{class_name} columns} + +\item{class2use}{Character vector of class names (preserves index order for +.mat export)} + +\item{annotator}{Annotator name} +} +\value{ +TRUE on success, FALSE on failure +} +\description{ +Writes (or replaces) annotations for a single sample. The existing rows for +the sample are deleted first so that re-saving acts as an upsert. +} +\examples{ +\dontrun{ +db_path <- get_db_path("/data/manual") +save_annotations_db(db_path, "D20230101T120000_IFCB134", + classifications, class2use, "Jane") +} +} diff --git a/man/save_sample_annotations.Rd b/man/save_sample_annotations.Rd index ac00201..24bb82f 100644 --- a/man/save_sample_annotations.Rd +++ b/man/save_sample_annotations.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/sample_saving.R \name{save_sample_annotations} \alias{save_sample_annotations} -\title{Save sample annotations to MAT and statistics files} +\title{Save sample annotations} \usage{ save_sample_annotations( sample_name, @@ -14,8 +14,11 @@ save_sample_annotations( png_output_folder, roi_folder, class2use_path, + class2use = NULL, annotator = "Unknown", - adc_folder = NULL + adc_folder = NULL, + save_format = "sqlite", + db_folder = get_default_db_dir() ) } \arguments{ @@ -29,7 +32,7 @@ save_sample_annotations( \item{temp_png_folder}{Path to temporary folder with extracted PNG images} -\item{output_folder}{Output folder path for MAT files} +\item{output_folder}{Output folder path for MAT files and statistics} \item{png_output_folder}{PNG output folder path (organized by class)} @@ -37,24 +40,34 @@ save_sample_annotations( \item{class2use_path}{Path to class2use file} +\item{class2use}{Character vector of class names. When NULL (default), loaded +from \code{class2use_path}.} + \item{annotator}{Annotator name for statistics} \item{adc_folder}{Direct path to the ADC folder. When provided, this is used instead of constructing the path via \code{\link{get_sample_paths}}. This supports non-standard folder structures.} + +\item{save_format}{One of \code{"sqlite"} (default), \code{"mat"}, or +\code{"both"}. Controls which backend(s) are written.} + +\item{db_folder}{Path to the database folder for SQLite storage. Defaults to +\code{\link{get_default_db_dir}()}. Should be a local filesystem path, +not a network drive.} } \value{ TRUE on success, FALSE on failure } \description{ -Saves the current annotations for a sample, including: -- MAT file compatible with ifcb-analysis (requires Python) -- Validation statistics CSV files -- PNG images organized by class +Saves the current annotations for a sample. By default annotations are +stored in a local SQLite database (\code{annotations.sqlite} in the database +folder). Optionally, a MATLAB-compatible \code{.mat} file can also be +written (requires Python + scipy). } \examples{ \dontrun{ -# Save annotations for a sample +# Save annotations for a sample (default: SQLite) success <- save_sample_annotations( sample_name = "D20230101T120000_IFCB134", classifications = current_classifications, diff --git a/man/update_annotator.Rd b/man/update_annotator.Rd new file mode 100644 index 0000000..f40757b --- /dev/null +++ b/man/update_annotator.Rd @@ -0,0 +1,41 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/database.R +\name{update_annotator} +\alias{update_annotator} +\title{Update the annotator name for one or more samples} +\usage{ +update_annotator(db_path, sample_names, annotator) +} +\arguments{ +\item{db_path}{Path to the SQLite database file} + +\item{sample_names}{Character vector of sample names to update} + +\item{annotator}{New annotator name} +} +\value{ +Named integer vector with the number of rows updated per sample. + Samples not found in the database are included with a count of 0. +} +\description{ +Changes the annotator field for all annotations belonging to the specified +sample(s). This is useful for correcting the annotator after bulk imports +or when transferring ownership of annotations. +} +\examples{ +\dontrun{ +db_path <- get_db_path("/data/manual") + +# Update a single sample +update_annotator(db_path, "D20230101T120000_IFCB134", "Jane") + +# Update multiple samples at once +update_annotator(db_path, + c("D20230101T120000_IFCB134", "D20230202T080000_IFCB134"), + "Jane") + +# Update all annotated samples +all_samples <- list_annotated_samples_db(db_path) +update_annotator(db_path, all_samples, "Jane") +} +} diff --git a/tests/testthat/test-database.R b/tests/testthat/test-database.R new file mode 100644 index 0000000..1b11cc7 --- /dev/null +++ b/tests/testthat/test-database.R @@ -0,0 +1,814 @@ +# Tests for SQLite database backend + +library(testthat) + +test_that("get_db_path returns correct path", { + expect_equal( + get_db_path("/data/local_db"), + file.path("/data/local_db", "annotations.sqlite") + ) +}) + +test_that("save_annotations_db creates database with correct schema", { + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + classifications <- data.frame( + file_name = c("D20230101T120000_IFCB134_00001.png", + "D20230101T120000_IFCB134_00002.png"), + class_name = c("Diatom", "Ciliate"), + stringsAsFactors = FALSE + ) + class2use <- c("unclassified", "Diatom", "Ciliate", "Dinoflagellate") + + result <- save_annotations_db(db_path, "D20230101T120000_IFCB134", + classifications, class2use, "TestUser") + + expect_true(result) + expect_true(file.exists(db_path)) + + # Verify schema + con <- DBI::dbConnect(RSQLite::SQLite(), db_path) + on.exit(DBI::dbDisconnect(con)) + + tables <- DBI::dbGetQuery(con, "SELECT name FROM sqlite_master WHERE type='table'") + expect_true("annotations" %in% tables$name) + expect_true("class_lists" %in% tables$name) + + # Verify annotations data + annotations <- DBI::dbGetQuery(con, "SELECT * FROM annotations ORDER BY roi_number") + expect_equal(nrow(annotations), 2) + expect_equal(annotations$sample_name, rep("D20230101T120000_IFCB134", 2)) + expect_equal(annotations$roi_number, c(1L, 2L)) + expect_equal(annotations$class_name, c("Diatom", "Ciliate")) + expect_equal(annotations$annotator, rep("TestUser", 2)) + + # Verify class list data + class_list <- DBI::dbGetQuery(con, "SELECT * FROM class_lists ORDER BY class_index") + expect_equal(nrow(class_list), length(class2use)) + expect_equal(class_list$class_name, class2use) + expect_equal(class_list$class_index, seq_along(class2use)) + + unlink(db_dir, recursive = TRUE) +}) + +test_that("save_annotations_db returns FALSE for empty classifications", { + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + result <- save_annotations_db(db_path, "sample", + data.frame(file_name = character(), + class_name = character()), + c("unclassified"), "TestUser") + expect_false(result) + + result2 <- save_annotations_db(db_path, "sample", NULL, c("unclassified"), "TestUser") + expect_false(result2) + + unlink(db_dir, recursive = TRUE) +}) + +test_that("save_annotations_db upserts (re-saving replaces data)", { + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + sample_name <- "D20230101T120000_IFCB134" + class2use <- c("unclassified", "Diatom", "Ciliate") + + # First save + classifications_v1 <- data.frame( + file_name = paste0(sample_name, "_00001.png"), + class_name = "Diatom", + stringsAsFactors = FALSE + ) + save_annotations_db(db_path, sample_name, classifications_v1, class2use, "User1") + + # Second save with different data + classifications_v2 <- data.frame( + file_name = paste0(sample_name, "_00001.png"), + class_name = "Ciliate", + stringsAsFactors = FALSE + ) + save_annotations_db(db_path, sample_name, classifications_v2, class2use, "User2") + + # Verify only latest version exists + con <- DBI::dbConnect(RSQLite::SQLite(), db_path) + on.exit(DBI::dbDisconnect(con)) + + annotations <- DBI::dbGetQuery(con, + "SELECT * FROM annotations WHERE sample_name = ?", + params = list(sample_name)) + expect_equal(nrow(annotations), 1) + expect_equal(annotations$class_name, "Ciliate") + expect_equal(annotations$annotator, "User2") + + unlink(db_dir, recursive = TRUE) +}) + +test_that("load_annotations_db returns correct data frame format", { + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + sample_name <- "D20230101T120000_IFCB134" + class2use <- c("unclassified", "Diatom", "Ciliate") + + classifications <- data.frame( + file_name = c(paste0(sample_name, "_00001.png"), + paste0(sample_name, "_00002.png"), + paste0(sample_name, "_00003.png")), + class_name = c("Diatom", "Ciliate", "Diatom"), + stringsAsFactors = FALSE + ) + save_annotations_db(db_path, sample_name, classifications, class2use, "TestUser") + + # Load with ROI dimensions + roi_dims <- data.frame( + roi_number = 1:3, + width = c(100, 150, 80), + height = c(80, 100, 60), + area = c(8000, 15000, 4800) + ) + + result <- load_annotations_db(db_path, sample_name, roi_dims) + + expect_s3_class(result, "data.frame") + expect_equal(nrow(result), 3) + expect_true(all(c("file_name", "class_name", "score", "width", "height", "roi_area") %in% names(result))) + + # Should be sorted by area descending + expect_equal(result$roi_area, c(15000, 8000, 4800)) + expect_equal(result$class_name, c("Ciliate", "Diatom", "Diatom")) + + unlink(db_dir, recursive = TRUE) +}) + +test_that("load_annotations_db returns NULL for missing sample", { + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + roi_dims <- data.frame( + roi_number = 1:3, width = rep(100, 3), + height = rep(80, 3), area = rep(8000, 3) + ) + + # Non-existent database + result <- load_annotations_db(db_path, "nonexistent_sample", roi_dims) + expect_null(result) + + # Existing database but missing sample + save_annotations_db(db_path, "other_sample", + data.frame(file_name = "other_sample_00001.png", + class_name = "Diatom", + stringsAsFactors = FALSE), + c("Diatom"), "test") + + result2 <- load_annotations_db(db_path, "nonexistent_sample", roi_dims) + expect_null(result2) + + unlink(db_dir, recursive = TRUE) +}) + +test_that("list_annotated_samples_db returns correct sample names", { + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + # Empty / non-existent database + expect_equal(list_annotated_samples_db(db_path), character()) + + # Add two samples + class2use <- c("unclassified", "Diatom") + save_annotations_db(db_path, "sample_A", + data.frame(file_name = "sample_A_00001.png", + class_name = "Diatom", + stringsAsFactors = FALSE), + class2use, "test") + save_annotations_db(db_path, "sample_B", + data.frame(file_name = "sample_B_00001.png", + class_name = "Diatom", + stringsAsFactors = FALSE), + class2use, "test") + + samples <- list_annotated_samples_db(db_path) + expect_equal(sort(samples), c("sample_A", "sample_B")) + + unlink(db_dir, recursive = TRUE) +}) + +test_that("round-trip: save then load returns identical data", { + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + sample_name <- "D20230101T120000_IFCB134" + class2use <- c("unclassified", "Diatom", "Ciliate", "Dinoflagellate") + + classifications <- data.frame( + file_name = sprintf("%s_%05d.png", sample_name, 1:5), + class_name = c("Diatom", "Ciliate", "Dinoflagellate", "Diatom", "unclassified"), + stringsAsFactors = FALSE + ) + + roi_dims <- data.frame( + roi_number = 1:5, + width = c(100, 150, 80, 200, 120), + height = c(80, 100, 60, 150, 90), + area = c(8000, 15000, 4800, 30000, 10800) + ) + + save_annotations_db(db_path, sample_name, classifications, class2use, "RoundTrip") + + loaded <- load_annotations_db(db_path, sample_name, roi_dims) + + # The loaded result is sorted by area descending + expected <- classifications + expected$score <- NA_real_ + expected$width <- roi_dims$width + expected$height <- roi_dims$height + expected$roi_area <- roi_dims$area + expected <- expected[order(-expected$roi_area), ] + rownames(expected) <- NULL + rownames(loaded) <- NULL + + expect_equal(loaded$file_name, expected$file_name) + expect_equal(loaded$class_name, expected$class_name) + expect_equal(loaded$width, expected$width) + expect_equal(loaded$height, expected$height) + expect_equal(loaded$roi_area, expected$roi_area) + + unlink(db_dir, recursive = TRUE) +}) + +test_that("load_from_db delegates to load_annotations_db", { + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + sample_name <- "D20230101T120000_IFCB134" + class2use <- c("unclassified", "Diatom") + + classifications <- data.frame( + file_name = paste0(sample_name, "_00001.png"), + class_name = "Diatom", + stringsAsFactors = FALSE + ) + + roi_dims <- data.frame( + roi_number = 1L, width = 100, height = 80, area = 8000 + ) + + save_annotations_db(db_path, sample_name, classifications, class2use, "test") + + result <- load_from_db(db_path, sample_name, roi_dims) + expect_s3_class(result, "data.frame") + expect_equal(nrow(result), 1) + expect_equal(result$class_name, "Diatom") + + unlink(db_dir, recursive = TRUE) +}) + +test_that("update_annotator changes annotator for a single sample", { + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + sample_name <- "D20230101T120000_IFCB134" + class2use <- c("unclassified", "Diatom") + + classifications <- data.frame( + file_name = sprintf("%s_%05d.png", sample_name, 1:3), + class_name = c("Diatom", "Diatom", "unclassified"), + stringsAsFactors = FALSE + ) + save_annotations_db(db_path, sample_name, classifications, class2use, "OldUser") + + counts <- update_annotator(db_path, sample_name, "NewUser") + expect_equal(counts, c("D20230101T120000_IFCB134" = 3L)) + + # Verify in DB + con <- DBI::dbConnect(RSQLite::SQLite(), db_path) + on.exit(DBI::dbDisconnect(con)) + rows <- DBI::dbGetQuery(con, + "SELECT DISTINCT annotator FROM annotations WHERE sample_name = ?", + params = list(sample_name)) + expect_equal(rows$annotator, "NewUser") + + unlink(db_dir, recursive = TRUE) +}) + +test_that("update_annotator changes multiple samples", { + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + class2use <- c("unclassified", "Diatom") + + save_annotations_db(db_path, "sample_A", + data.frame(file_name = "sample_A_00001.png", + class_name = "Diatom", + stringsAsFactors = FALSE), + class2use, "User1") + save_annotations_db(db_path, "sample_B", + data.frame(file_name = c("sample_B_00001.png", "sample_B_00002.png"), + class_name = c("Diatom", "Diatom"), + stringsAsFactors = FALSE), + class2use, "User2") + + counts <- update_annotator(db_path, c("sample_A", "sample_B"), "SharedUser") + expect_equal(counts, c(sample_A = 1L, sample_B = 2L)) + + # Verify both updated + con <- DBI::dbConnect(RSQLite::SQLite(), db_path) + on.exit(DBI::dbDisconnect(con)) + rows <- DBI::dbGetQuery(con, "SELECT DISTINCT annotator FROM annotations") + expect_equal(rows$annotator, "SharedUser") + + unlink(db_dir, recursive = TRUE) +}) + +test_that("update_annotator returns 0 for non-existent sample", { + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + save_annotations_db(db_path, "existing", + data.frame(file_name = "existing_00001.png", + class_name = "Diatom", + stringsAsFactors = FALSE), + c("Diatom"), "test") + + counts <- update_annotator(db_path, "nonexistent", "NewUser") + expect_equal(counts, c(nonexistent = 0L)) + + # Mix of existing and non-existing + counts2 <- update_annotator(db_path, c("existing", "nonexistent"), "NewUser") + expect_equal(counts2, c(existing = 1L, nonexistent = 0L)) + + unlink(db_dir, recursive = TRUE) +}) + +test_that("update_annotator validates inputs", { + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + save_annotations_db(db_path, "sample", + data.frame(file_name = "sample_00001.png", + class_name = "Diatom", + stringsAsFactors = FALSE), + c("Diatom"), "test") + + # Missing database + expect_error(update_annotator("/nonexistent/db.sqlite", "sample", "X"), + "Database not found") + + # Invalid annotator + expect_error(update_annotator(db_path, "sample", NA_character_), + "annotator must be") + expect_error(update_annotator(db_path, "sample", c("A", "B")), + "annotator must be") + + # Empty sample_names returns empty vector + counts <- update_annotator(db_path, character(0), "X") + expect_length(counts, 0) + + unlink(db_dir, recursive = TRUE) +}) + +test_that("import_mat_to_db migrates data correctly", { + skip_if_not_installed("iRfcb") + skip_if_not(reticulate::py_available(), "Python not available") + skip_if_not(reticulate::py_module_available("scipy"), "scipy not available") + + # Use test data if available + mat_path <- testthat::test_path("test_data", "raw", "2022", "D20220522", + "D20220522T000439_IFCB134.mat") + + # Look for a MAT annotation file in test data + # If no test .mat annotation exists, create one via save first + sample_name <- "D20220522T000439_IFCB134" + class2use_path <- testthat::test_path("test_data", "class2use.mat") + skip_if_not(file.exists(class2use_path), "Test class2use file not found") + + # Check if there's a test annotation mat file + output_test <- testthat::test_path("test_data", "manual") + test_mat <- file.path(output_test, paste0(sample_name, ".mat")) + skip_if_not(file.exists(test_mat), "No test MAT annotation file for migration test") + + class2use <- load_class_list(class2use_path) + + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + result <- import_mat_to_db(test_mat, db_path, sample_name, class2use, "migrated") + expect_true(result) + + # Verify data was imported + samples <- list_annotated_samples_db(db_path) + expect_true(sample_name %in% samples) + + unlink(db_dir, recursive = TRUE) +}) + +test_that("import_mat_to_db returns FALSE for missing file", { + result <- import_mat_to_db( + "/nonexistent/file.mat", + tempfile(fileext = ".sqlite"), + "sample", c("unclassified"), "test" + ) + expect_false(result) +}) + +test_that("export_db_to_mat creates valid .mat file", { + skip_if_not_installed("iRfcb") + skip_if_not(reticulate::py_available(), "Python not available") + skip_if_not(reticulate::py_module_available("scipy"), "scipy not available") + + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + sample_name <- "D20230101T120000_IFCB134" + class2use <- c("unclassified", "Diatom", "Ciliate", "Dinoflagellate") + + classifications <- data.frame( + file_name = sprintf("%s_%05d.png", sample_name, 1:4), + class_name = c("Diatom", "Ciliate", "Diatom", "Dinoflagellate"), + stringsAsFactors = FALSE + ) + save_annotations_db(db_path, sample_name, classifications, class2use, "TestUser") + + mat_dir <- tempfile("mat_") + dir.create(mat_dir) + + result <- export_db_to_mat(db_path, sample_name, mat_dir) + expect_true(result) + + mat_path <- file.path(mat_dir, paste0(sample_name, ".mat")) + expect_true(file.exists(mat_path)) + + # Verify contents via ifcb_get_mat_variable + classlist <- iRfcb::ifcb_get_mat_variable(mat_path, variable_name = "classlist") + expect_equal(nrow(classlist), 4) + # class indices: Diatom=2, Ciliate=3, Diatom=2, Dinoflagellate=4 + expect_equal(classlist[, 2], c(2, 3, 2, 4)) + + unlink(c(db_dir, mat_dir), recursive = TRUE) +}) + +test_that("export_db_to_mat returns FALSE for missing sample", { + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + # Create DB with one sample + save_annotations_db(db_path, "existing_sample", + data.frame(file_name = "existing_sample_00001.png", + class_name = "Diatom", + stringsAsFactors = FALSE), + c("unclassified", "Diatom"), "test") + + result <- export_db_to_mat(db_path, "nonexistent_sample", db_dir) + expect_false(result) + + unlink(db_dir, recursive = TRUE) +}) + +test_that("export_db_to_mat returns FALSE for non-existent database", { + result <- export_db_to_mat("/nonexistent/db.sqlite", "sample", tempdir()) + expect_false(result) +}) + +test_that("import_all_mat_to_db imports multiple files and returns correct counts", { + skip_if_not_installed("iRfcb") + skip_if_not(reticulate::py_available(), "Python not available") + skip_if_not(reticulate::py_module_available("scipy"), "scipy not available") + + mat_dir <- tempfile("mat_") + dir.create(mat_dir) + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + class2use <- c("unclassified", "Diatom", "Ciliate") + + # Create two .mat files using ifcb_create_manual_file + iRfcb::ifcb_create_manual_file( + roi_length = 3, class2use = class2use, + output_file = file.path(mat_dir, "sample_A.mat"), + classlist = c(2, 3, 2) + ) + iRfcb::ifcb_create_manual_file( + roi_length = 2, class2use = class2use, + output_file = file.path(mat_dir, "sample_B.mat"), + classlist = c(1, 3) + ) + # Create a classifier file that should be excluded + iRfcb::ifcb_create_manual_file( + roi_length = 2, class2use = class2use, + output_file = file.path(mat_dir, "sample_C_class_v1.mat"), + classlist = c(1, 2) + ) + + result <- import_all_mat_to_db(mat_dir, db_path, class2use, "test") + + expect_equal(result$success, 2L) + expect_equal(result$failed, 0L) + expect_equal(result$skipped, 0L) + + # Verify both samples in DB + samples <- list_annotated_samples_db(db_path) + expect_true("sample_A" %in% samples) + expect_true("sample_B" %in% samples) + expect_false("sample_C_class_v1" %in% samples) + + # Re-import should skip existing + result2 <- import_all_mat_to_db(mat_dir, db_path, class2use, "test") + expect_equal(result2$success, 0L) + expect_equal(result2$skipped, 2L) + + unlink(c(mat_dir, db_dir), recursive = TRUE) +}) + +test_that("export_all_db_to_mat exports multiple samples", { + skip_if_not_installed("iRfcb") + skip_if_not(reticulate::py_available(), "Python not available") + skip_if_not(reticulate::py_module_available("scipy"), "scipy not available") + + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + class2use <- c("unclassified", "Diatom", "Ciliate") + + save_annotations_db(db_path, "sample_X", + data.frame(file_name = "sample_X_00001.png", + class_name = "Diatom", + stringsAsFactors = FALSE), + class2use, "test") + save_annotations_db(db_path, "sample_Y", + data.frame(file_name = "sample_Y_00001.png", + class_name = "Ciliate", + stringsAsFactors = FALSE), + class2use, "test") + + mat_dir <- tempfile("mat_") + dir.create(mat_dir) + + result <- export_all_db_to_mat(db_path, mat_dir) + + expect_equal(result$success, 2L) + expect_equal(result$failed, 0L) + expect_true(file.exists(file.path(mat_dir, "sample_X.mat"))) + expect_true(file.exists(file.path(mat_dir, "sample_Y.mat"))) + + unlink(c(db_dir, mat_dir), recursive = TRUE) +}) + +test_that("round-trip: DB -> .mat -> DB produces matching data", { + skip_if_not_installed("iRfcb") + skip_if_not(reticulate::py_available(), "Python not available") + skip_if_not(reticulate::py_module_available("scipy"), "scipy not available") + + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + sample_name <- "D20230101T120000_IFCB134" + class2use <- c("unclassified", "Diatom", "Ciliate", "Dinoflagellate") + + original <- data.frame( + file_name = sprintf("%s_%05d.png", sample_name, 1:5), + class_name = c("Diatom", "Ciliate", "Dinoflagellate", "Diatom", "unclassified"), + stringsAsFactors = FALSE + ) + save_annotations_db(db_path, sample_name, original, class2use, "Original") + + # Export to .mat + mat_dir <- tempfile("mat_") + dir.create(mat_dir) + export_db_to_mat(db_path, sample_name, mat_dir) + + # Import back to a fresh DB + db_dir2 <- tempfile("db2_") + dir.create(db_dir2) + db_path2 <- get_db_path(db_dir2) + + mat_path <- file.path(mat_dir, paste0(sample_name, ".mat")) + import_mat_to_db(mat_path, db_path2, sample_name, class2use, "reimported") + + # Compare: read both DBs and check class names match + con1 <- DBI::dbConnect(RSQLite::SQLite(), db_path) + on.exit(DBI::dbDisconnect(con1), add = TRUE) + rows1 <- DBI::dbGetQuery(con1, + "SELECT roi_number, class_name FROM annotations WHERE sample_name = ? ORDER BY roi_number", + params = list(sample_name)) + + con2 <- DBI::dbConnect(RSQLite::SQLite(), db_path2) + on.exit(DBI::dbDisconnect(con2), add = TRUE) + rows2 <- DBI::dbGetQuery(con2, + "SELECT roi_number, class_name FROM annotations WHERE sample_name = ? ORDER BY roi_number", + params = list(sample_name)) + + expect_equal(nrow(rows1), nrow(rows2)) + expect_equal(rows1$class_name, rows2$class_name) + + unlink(c(db_dir, db_dir2, mat_dir), recursive = TRUE) +}) + +test_that("export_db_to_png extracts images into class subfolders", { + roi_path <- testthat::test_path("test_data", "raw", "2022", "D20220522", + "D20220522T000439_IFCB134.roi") + skip_if_not(file.exists(roi_path), "Test ROI file not found") + + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + sample_name <- "D20220522T000439_IFCB134" + class2use <- c("unclassified", "Diatom", "Ciliate") + + # Save annotations for ROIs 2-5 (ROI 1 is empty in test data) + classifications <- data.frame( + file_name = sprintf("%s_%05d.png", sample_name, 2:5), + class_name = c("Diatom", "Ciliate", "Diatom", "Ciliate"), + stringsAsFactors = FALSE + ) + save_annotations_db(db_path, sample_name, classifications, class2use, "test") + + png_dir <- tempfile("png_") + dir.create(png_dir) + + result <- export_db_to_png(db_path, sample_name, roi_path, png_dir) + expect_true(result) + + # Check class subfolders exist + expect_true(dir.exists(file.path(png_dir, "Diatom"))) + expect_true(dir.exists(file.path(png_dir, "Ciliate"))) + + # Check that PNG files were created in the right subfolders + diatom_files <- list.files(file.path(png_dir, "Diatom"), pattern = "\\.png$") + ciliate_files <- list.files(file.path(png_dir, "Ciliate"), pattern = "\\.png$") + expect_equal(length(diatom_files), 2) + expect_equal(length(ciliate_files), 2) + + unlink(c(db_dir, png_dir), recursive = TRUE) +}) + +test_that("export_db_to_png skip_class excludes specified classes", { + roi_path <- testthat::test_path("test_data", "raw", "2022", "D20220522", + "D20220522T000439_IFCB134.roi") + skip_if_not(file.exists(roi_path), "Test ROI file not found") + + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + sample_name <- "D20220522T000439_IFCB134" + class2use <- c("unclassified", "Diatom", "Ciliate") + + # ROIs 2-5: two Diatom, one Ciliate, one unclassified + classifications <- data.frame( + file_name = sprintf("%s_%05d.png", sample_name, 2:5), + class_name = c("Diatom", "Ciliate", "Diatom", "unclassified"), + stringsAsFactors = FALSE + ) + save_annotations_db(db_path, sample_name, classifications, class2use, "test") + + png_dir <- tempfile("png_") + dir.create(png_dir) + + result <- export_db_to_png(db_path, sample_name, roi_path, png_dir, + skip_class = "unclassified") + expect_true(result) + + # unclassified subfolder should NOT exist + expect_false(dir.exists(file.path(png_dir, "unclassified"))) + # Diatom and Ciliate should exist + expect_true(dir.exists(file.path(png_dir, "Diatom"))) + expect_true(dir.exists(file.path(png_dir, "Ciliate"))) + + diatom_files <- list.files(file.path(png_dir, "Diatom"), pattern = "\\.png$") + ciliate_files <- list.files(file.path(png_dir, "Ciliate"), pattern = "\\.png$") + expect_equal(length(diatom_files), 2) + expect_equal(length(ciliate_files), 1) + + unlink(c(db_dir, png_dir), recursive = TRUE) +}) + +test_that("export_db_to_png skip_class with all ROIs skipped returns TRUE", { + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + sample_name <- "D20220522T000439_IFCB134" + class2use <- c("unclassified", "Diatom") + + classifications <- data.frame( + file_name = sprintf("%s_%05d.png", sample_name, 2:3), + class_name = c("unclassified", "unclassified"), + stringsAsFactors = FALSE + ) + save_annotations_db(db_path, sample_name, classifications, class2use, "test") + + roi_path <- testthat::test_path("test_data", "raw", "2022", "D20220522", + "D20220522T000439_IFCB134.roi") + skip_if_not(file.exists(roi_path), "Test ROI file not found") + + png_dir <- tempfile("png_") + dir.create(png_dir) + + result <- export_db_to_png(db_path, sample_name, roi_path, png_dir, + skip_class = "unclassified") + expect_true(result) + + # No class subfolders should be created + expect_equal(length(list.dirs(png_dir, recursive = FALSE)), 0) + + unlink(c(db_dir, png_dir), recursive = TRUE) +}) + +test_that("export_db_to_png returns FALSE for missing sample", { + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + # Create DB with one sample + save_annotations_db(db_path, "existing_sample", + data.frame(file_name = "existing_sample_00001.png", + class_name = "Diatom", + stringsAsFactors = FALSE), + c("unclassified", "Diatom"), "test") + + roi_path <- testthat::test_path("test_data", "raw", "2022", "D20220522", + "D20220522T000439_IFCB134.roi") + skip_if_not(file.exists(roi_path), "Test ROI file not found") + + result <- export_db_to_png(db_path, "nonexistent_sample", roi_path, tempdir()) + expect_false(result) + + unlink(db_dir, recursive = TRUE) +}) + +test_that("export_db_to_png returns FALSE for missing ROI file", { + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + save_annotations_db(db_path, "sample_A", + data.frame(file_name = "sample_A_00001.png", + class_name = "Diatom", + stringsAsFactors = FALSE), + c("unclassified", "Diatom"), "test") + + result <- export_db_to_png(db_path, "sample_A", "/nonexistent/file.roi", tempdir()) + expect_false(result) + + unlink(db_dir, recursive = TRUE) +}) + +test_that("export_all_db_to_png exports multiple samples and skips missing ROIs", { + roi_path <- testthat::test_path("test_data", "raw", "2022", "D20220522", + "D20220522T000439_IFCB134.roi") + skip_if_not(file.exists(roi_path), "Test ROI file not found") + + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + class2use <- c("unclassified", "Diatom") + + # sample_A has a valid ROI path (use ROI 2 since ROI 1 is empty in test data) + save_annotations_db(db_path, "D20220522T000439_IFCB134", + data.frame(file_name = "D20220522T000439_IFCB134_00002.png", + class_name = "Diatom", + stringsAsFactors = FALSE), + class2use, "test") + + # sample_B has no ROI path (will be skipped) + save_annotations_db(db_path, "sample_no_roi", + data.frame(file_name = "sample_no_roi_00001.png", + class_name = "Diatom", + stringsAsFactors = FALSE), + class2use, "test") + + png_dir <- tempfile("png_") + dir.create(png_dir) + + roi_map <- list("D20220522T000439_IFCB134" = roi_path) + + result <- export_all_db_to_png(db_path, png_dir, roi_map) + + expect_equal(result$success, 1L) + expect_equal(result$failed, 0L) + expect_equal(result$skipped, 1L) + expect_true(dir.exists(file.path(png_dir, "Diatom"))) + + unlink(c(db_dir, png_dir), recursive = TRUE) +}) diff --git a/tests/testthat/test-sample_saving.R b/tests/testthat/test-sample_saving.R index 391252e..daa34d0 100644 --- a/tests/testthat/test-sample_saving.R +++ b/tests/testthat/test-sample_saving.R @@ -305,6 +305,8 @@ test_that("save_sample_annotations creates MAT file with real data", { output_folder <- tempfile("output_") png_output_folder <- tempfile("png_output_") + db_folder <- tempfile("db_") + result <- save_sample_annotations( sample_name = sample_name, classifications = current_classifications, @@ -315,7 +317,9 @@ test_that("save_sample_annotations creates MAT file with real data", { png_output_folder = png_output_folder, roi_folder = roi_folder, class2use_path = class2use_path, - annotator = "TestUser" + annotator = "TestUser", + save_format = "mat", + db_folder = db_folder ) expect_true(result) @@ -338,4 +342,150 @@ test_that("save_sample_annotations creates MAT file with real data", { # Cleanup unlink(output_folder, recursive = TRUE) unlink(png_output_folder, recursive = TRUE) + unlink(db_folder, recursive = TRUE) +}) + +test_that("save_sample_annotations with save_format='sqlite' creates database", { + sample_name <- "D20230314T001205_IFCB134" + + # Create class2use file + class2use_file <- tempfile(fileext = ".txt") + writeLines(c("unclassified", "Diatom", "Ciliate"), class2use_file) + + # Create temp source folder with an image + src_folder <- tempfile("png_") + dir.create(file.path(src_folder, sample_name), recursive = TRUE) + file.create(file.path(src_folder, sample_name, paste0(sample_name, "_00001.png"))) + + output_folder <- tempfile("output_") + png_output_folder <- tempfile("png_out_") + db_folder <- tempfile("db_") + + classifications <- data.frame( + file_name = paste0(sample_name, "_00001.png"), + class_name = "Diatom", + score = NA_real_, + stringsAsFactors = FALSE + ) + + changes_log <- data.frame( + image = paste0(sample_name, "_00001.png"), + original_class = "unclassified", + new_class = "Diatom", + stringsAsFactors = FALSE + ) + + result <- save_sample_annotations( + sample_name = sample_name, + classifications = classifications, + original_classifications = classifications, + changes_log = changes_log, + temp_png_folder = src_folder, + output_folder = output_folder, + png_output_folder = png_output_folder, + roi_folder = tempdir(), + class2use_path = class2use_file, + annotator = "TestUser", + save_format = "sqlite", + db_folder = db_folder + ) + + expect_true(result) + + # SQLite database should exist in db_folder, not output_folder + db_path <- get_db_path(db_folder) + expect_true(file.exists(db_path)) + + # Should be able to load the annotations back + samples <- list_annotated_samples_db(db_path) + expect_true(sample_name %in% samples) + + # No .mat file should be created + mat_path <- file.path(output_folder, paste0(sample_name, ".mat")) + expect_false(file.exists(mat_path)) + + # No database in output_folder + expect_false(file.exists(get_db_path(output_folder))) + + # Cleanup + unlink(output_folder, recursive = TRUE) + unlink(png_output_folder, recursive = TRUE) + unlink(src_folder, recursive = TRUE) + unlink(db_folder, recursive = TRUE) + unlink(class2use_file) +}) + +test_that("save_sample_annotations with save_format='both' creates both outputs", { + skip_if_not_installed("iRfcb") + skip_if_not(reticulate::py_available(), "Python not available") + skip_if_not(reticulate::py_module_available("scipy"), "scipy not available") + + sample_name <- "D20220522T000439_IFCB134" + + png_folder <- testthat::test_path("test_data", "png") + roi_folder <- testthat::test_path("test_data", "raw") + class2use_path <- testthat::test_path("test_data", "class2use.mat") + + skip_if_not(dir.exists(file.path(png_folder, sample_name)), "Test PNG folder not found") + skip_if_not(file.exists(class2use_path), "Test class2use file not found") + skip_if_not( + file.exists(file.path(roi_folder, "2022", "D20220522", paste0(sample_name, ".adc"))), + "Test ADC file not found" + ) + + png_files <- list.files(file.path(png_folder, sample_name), pattern = "\\.png$") + skip_if(length(png_files) < 2, "Not enough test PNG files") + + original_classifications <- data.frame( + file_name = png_files, + class_name = rep("unclassified", length(png_files)), + score = rep(NA_real_, length(png_files)), + stringsAsFactors = FALSE + ) + + current_classifications <- data.frame( + file_name = png_files, + class_name = c("Mesodinium_rubrum", rep("Ciliophora", length(png_files) - 1)), + stringsAsFactors = FALSE + ) + + changes_log <- data.frame( + image = png_files[1], + original_class = "unclassified", + new_class = "Mesodinium_rubrum", + stringsAsFactors = FALSE + ) + + output_folder <- tempfile("output_") + png_output_folder <- tempfile("png_output_") + db_folder <- tempfile("db_") + + result <- save_sample_annotations( + sample_name = sample_name, + classifications = current_classifications, + original_classifications = original_classifications, + changes_log = changes_log, + temp_png_folder = png_folder, + output_folder = output_folder, + png_output_folder = png_output_folder, + roi_folder = roi_folder, + class2use_path = class2use_path, + annotator = "TestUser", + save_format = "both", + db_folder = db_folder + ) + + expect_true(result) + + # Both should exist + db_path <- get_db_path(db_folder) + expect_true(file.exists(db_path)) + + mat_file <- file.path(output_folder, paste0(sample_name, ".mat")) + expect_true(file.exists(mat_file)) + + # Cleanup + unlink(output_folder, recursive = TRUE) + unlink(png_output_folder, recursive = TRUE) + unlink(db_folder, recursive = TRUE) }) diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index 9cdc24b..da8f9b8 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -347,6 +347,26 @@ test_that("get_config_dir uses tempdir during R CMD check", { } }) +test_that("get_default_db_dir returns a valid path", { + db_folder <- get_default_db_dir() + expect_true(is.character(db_folder)) + expect_true(nzchar(db_folder)) +}) + +test_that("get_default_db_dir uses tempdir during R CMD check", { + old_val <- Sys.getenv("_R_CHECK_PACKAGE_NAME_", unset = NA) + Sys.setenv("_R_CHECK_PACKAGE_NAME_" = "ClassiPyR") + + db_folder <- get_default_db_dir() + expect_true(grepl(tempdir(), db_folder, fixed = TRUE)) + + if (is.na(old_val)) { + Sys.unsetenv("_R_CHECK_PACKAGE_NAME_") + } else { + Sys.setenv("_R_CHECK_PACKAGE_NAME_" = old_val) + } +}) + # ============================================================================= # File index cache functions # ============================================================================= diff --git a/vignettes/class-management.Rmd b/vignettes/class-management.Rmd index 3cba17f..c111e40 100644 --- a/vignettes/class-management.Rmd +++ b/vignettes/class-management.Rmd @@ -20,7 +20,7 @@ Understanding and managing your class list is important for maintaining consiste ## Why Class Indices Matter (ifcb-analysis Users) -> **Note**: This section is primarily relevant if you use the [ifcb-analysis](https://github.com/hsosik/ifcb-analysis) MATLAB toolbox (Sosik & Olson, 2007). If you only work with CSV exports, class indices are less critical. +> **Note**: This section is primarily relevant if you export `.mat` files for use with the [ifcb-analysis](https://github.com/hsosik/ifcb-analysis) MATLAB toolbox (Sosik & Olson, 2007). If you use the default SQLite storage or work with CSV exports, class indices are less critical because class names are stored directly. IFCB .mat annotations use **numerical indices** to reference classes: diff --git a/vignettes/faq.Rmd b/vignettes/faq.Rmd index f080c00..546de09 100644 --- a/vignettes/faq.Rmd +++ b/vignettes/faq.Rmd @@ -42,13 +42,9 @@ A: No. The app only reads your original files. All output is written to separate **Q: I see "Python not available" warning** -A: This warning affects saving .mat files. Python is required for: +A: This warning only appears when your storage format includes `.mat` files. Python is **not needed** for the default SQLite storage. -- Saving annotations as .mat files for [ifcb-analysis](https://github.com/hsosik/ifcb-analysis) - -Reading .mat files (annotations, classifier output, class lists) does not require Python. If you do not need to save .mat files, you can ignore this warning. - -To enable .mat support: +If you see this warning and don't need `.mat` files, switch to SQLite in Settings > Annotation Storage. Otherwise, to enable `.mat` support: ```{r, eval = FALSE} library(iRfcb) @@ -57,6 +53,10 @@ ifcb_py_install() # Creates venv in current working directory Then restart the app. +**Q: Do I need Python to use ClassiPyR?** + +A: No. The default storage format is SQLite, which works out of the box with no Python dependency. Python is only needed if you want to export `.mat` files for [ifcb-analysis](https://github.com/hsosik/ifcb-analysis) compatibility. + **Q: Where is the Python virtual environment created?** A: By default, `ifcb_py_install()` creates a `venv` folder in your home directory. You can specify a different location: @@ -86,7 +86,7 @@ When you specify `run_app(venv_path = "/path/to/venv")`, that path is used for P A: Make sure you have remotes installed and try: ```{r, eval = FALSE} -install.packages("remotes") +if (!requireNamespace("remotes", quietly = TRUE)) install.packages("remotes") remotes::install_github("EuropeanIFCBGroup/ClassiPyR") ``` @@ -199,7 +199,7 @@ A: The ROI might be empty (no actual image data). These are filtered out automat A: Check that: 1. Output folder is writable -2. Python is available (required for saving .mat files) +2. If using MAT format: Python is available (not needed for default SQLite storage) 3. Click "Save Annotations" before closing --- @@ -254,22 +254,146 @@ Common taxonomic characters like hyphens (`-`), underscores (`_`), periods (`.`) **Q: Where are my annotations saved?** -A: In the Output Folder you configured: +A: Annotations are split across two locations: + +- **SQLite database** (default): stored in the **Database Folder** (a local directory) +- **MAT files and statistics**: stored in the **Output Folder** (can be on a network drive) + +``` +db_folder/ ← local drive (Database Folder) +└── annotations.sqlite ← single database for ALL samples + +output_folder/ ← can be a network drive (Output Folder) +├── D20230101T120000_IFCB134.mat ← only if storage format includes "MAT" +├── D20230202T080000_IFCB134.mat +└── validation_statistics/ + ├── ..._validation_stats.csv + └── ..._validation_detailed.csv +``` + +By default, the database is stored in a persistent local directory (`tools::R_user_dir("ClassiPyR", "data")`). Back up `annotations.sqlite` to preserve your work. + +**Q: Where is the default database location?** + +A: The default Database Folder is a platform-specific local directory: + +- **Linux**: `~/.local/share/R/ClassiPyR/` +- **macOS**: `~/Library/Application Support/org.R-project.R/R/ClassiPyR/` +- **Windows**: `%LOCALAPPDATA%/R/data/R/ClassiPyR/` + +You can find the exact path with: + +```{r, eval = FALSE} +ClassiPyR::get_default_db_dir() +``` + +You can change it in Settings > Database Folder, but it should always be a local drive. + +**Q: Can I put the database on a network drive?** + +A: No. SQLite databases are [not safe on network filesystems](https://www.sqlite.org/useovernet.html) (NFS, SMB/CIFS) because network file locking is unreliable, which can lead to database corruption. Always keep the Database Folder on a local drive. The Output Folder (for MAT files and statistics) can safely be on a network drive. + +**Q: How do I transfer my annotations to another computer?** + +A: Since the SQLite database is stored locally, you cannot simply share it over a network drive. Instead, use `.mat` files as the interchange format: + +1. **Export** from the source computer (requires Python with scipy): + +```{r, eval = FALSE} +library(ClassiPyR) +db_path <- get_db_path(get_default_db_dir()) +# Export all annotations to .mat files in a shared output folder +result <- export_all_db_to_mat(db_path, "/shared/network/manual") +cat(result$success, "exported\n") +``` + +Or use the **Export SQLite → .mat** button in Settings. + +2. **Import** on the target computer: + +```{r, eval = FALSE} +library(ClassiPyR) +class2use <- load_class_list("/shared/network/class2use.mat") +db_path <- get_db_path(get_default_db_dir()) +# Import .mat files from the shared folder into the local database +result <- import_all_mat_to_db("/shared/network/manual", db_path, class2use) +cat(result$success, "imported,", result$skipped, "skipped\n") +``` + +Or use the **Import .mat → SQLite** button in Settings. Already-imported samples are skipped automatically. -- MAT annotation files are saved directly in the output folder (one per sample) -- `validation_statistics/` subfolder contains CSV statistics -- PNGs are in the PNG Output Folder, organized by class name +You can also simply copy the `annotations.sqlite` file directly between machines if you prefer. **Q: Can I import annotations back to MATLAB?** -A: Yes, the MAT files are compatible with the [ifcb-analysis](https://github.com/hsosik/ifcb-analysis) toolbox (Sosik & Olson, 2007). Use the list in `startMC`, or load the list in MATLAB using: +A: Yes, if you save with the "MAT file" or "Both" storage format, the MAT files are compatible with the [ifcb-analysis](https://github.com/hsosik/ifcb-analysis) toolbox (Sosik & Olson, 2007). Use the list in `startMC`, or load the list in MATLAB using: ```matlab load('sample_name.mat'); % classlist contains [roi_number, class_index] ``` -Note: Python with `scipy` must be installed to save .mat files. +Note: Python with `scipy` must be installed to save .mat files. Change the storage format in Settings > Annotation Storage. + +**Q: Can I migrate existing .mat annotations to the SQLite database?** + +A: Yes. The easiest way is the **Import .mat → SQLite** button in Settings > Annotation Storage, which bulk-imports all `.mat` files in your output folder. + +You can also import programmatically — a single file: + +```{r, eval = FALSE} +library(ClassiPyR) +class2use <- load_class_list("/path/to/class2use.mat") +import_mat_to_db( + mat_path = "/data/manual/D20230101T120000_IFCB134.mat", + db_path = get_db_path(get_default_db_dir()), + sample_name = "D20230101T120000_IFCB134", + class2use = class2use +) +``` + +Or bulk-import all `.mat` files in a folder: + +```{r, eval = FALSE} +result <- import_all_mat_to_db("/data/manual", get_db_path(get_default_db_dir()), class2use) +cat(result$success, "imported,", result$failed, "failed,", result$skipped, "skipped\n") +``` + +**Q: Can I export SQLite annotations back to .mat files?** + +A: Yes. Use the **Export SQLite → .mat** button in Settings > Annotation Storage to export all annotated samples at once. This requires Python with scipy. + +You can also export programmatically: + +```{r, eval = FALSE} +# Single sample +export_db_to_mat(get_db_path(get_default_db_dir()), "D20230101T120000_IFCB134", "/data/manual") + +# All samples +result <- export_all_db_to_mat(get_db_path(get_default_db_dir()), "/data/manual") +cat(result$success, "exported,", result$failed, "failed\n") +``` + +**Q: Can I change the annotator name for existing annotations?** + +A: Yes. Use `update_annotator()` from the R console: + +```{r, eval = FALSE} +library(ClassiPyR) +db_path <- get_db_path(get_default_db_dir()) + +# Update a single sample +update_annotator(db_path, "D20230101T120000_IFCB134", "Jane") + +# Update several samples at once +update_annotator(db_path, c("sample_A", "sample_B"), "Jane") + +# Update all annotated samples (e.g. after a bulk import) +all_samples <- list_annotated_samples_db(db_path) +update_annotator(db_path, all_samples, "Jane") +``` + +The function returns a named vector showing how many annotation rows were updated per sample (0 means the sample was not found in the database). **Q: What's in the statistics CSV?** @@ -392,7 +516,7 @@ A: In the same config directory as your settings: |-------|----------| | "ROI file not found" | Check ROI Data Folder path; ensure `.roi` files use IFCB naming and click Sync | | "ADC file not found" | ADC file must be alongside ROI file | -| "Python not available" | Affects saving .mat files. Run `iRfcb::ifcb_py_install()` | +| "Python not available" | Only affects `.mat` export. Switch to SQLite in Settings, or run `iRfcb::ifcb_py_install()` | | "Error loading class list" | Check file format (.mat or .txt) | | "No samples found" | Check ROI Data Folder configuration | | App fails to start | Try `run_app(reset_settings = TRUE)` to clear saved settings | diff --git a/vignettes/getting-started.Rmd b/vignettes/getting-started.Rmd index c4eaee5..15e732c 100644 --- a/vignettes/getting-started.Rmd +++ b/vignettes/getting-started.Rmd @@ -27,9 +27,9 @@ Make sure you have: ### Python Requirements -Python is required for saving annotations as MATLAB .mat files for use with [ifcb-analysis](https://github.com/hsosik/ifcb-analysis). Reading existing .mat files (annotations, classifier output, class lists) does not require Python. +Python is **not required** for the default workflow. ClassiPyR stores annotations in a local SQLite database that works out of the box with no external dependencies. -If you only need to read .mat files or work with CSV classification files, Python is not required. +Python is only needed if you want to export annotations as MATLAB `.mat` files for use with [ifcb-analysis](https://github.com/hsosik/ifcb-analysis). Reading existing `.mat` files (annotations, classifier output, class lists) also does not require Python. ### CSV Classification Format @@ -43,9 +43,9 @@ D20230101T120000_IFCB134_00002.png,Ciliate An optional `score` column (confidence values between 0 and 1) can also be included. See the [User Guide](user-guide.html) for more details. -### Python Setup +### Python Setup (optional) -To set up Python: +Only needed if you plan to export `.mat` files. Skip this step if using the default SQLite storage. ```{r, eval = FALSE} library(iRfcb) @@ -79,8 +79,11 @@ Configure your folders using the built-in folder browser: |---------|-------------|---------| | Classification Folder | Where your CSV/MAT classifications are | `/ifcb/classified/` | | ROI Data Folder | Where your IFCB raw files are | `/ifcb/raw/` | -| Output Folder | Where annotations will be saved | `/ifcb/manual/` | -| PNG Output Folder | Where images will be organized | `/ifcb/png/` | +| Output Folder | Where MAT files and statistics go | `/ifcb/manual/` | +| Database Folder | Where the SQLite database is stored (must be local) | auto-detected | +| PNG Output Folder | Where images will be organized by class | `/ifcb/png/` | + +> **Network drives**: The Output Folder can safely reside on a network share (e.g., for MAT files and statistics). However, the Database Folder must be on a **local** drive because [SQLite is not safe on network filesystems](https://www.sqlite.org/useovernet.html). The default database location is a local user-level directory that works out of the box. Click **Save Settings**. The app will scan your folders and build a file index cache for fast loading. @@ -206,10 +209,16 @@ The images will move to their new class group. Click **Save Annotations** to save: -- MAT file for MATLAB compatibility (requires Python; for use with [ifcb-analysis](https://github.com/hsosik/ifcb-analysis)) +- **SQLite database** (default) - annotations are written to `annotations.sqlite` in your Output Folder. This single file stores annotations for all samples. No Python needed. - Statistics CSV with accuracy metrics - PNGs organized by class +You can change the storage format in **Settings > Annotation Storage**: + +- **SQLite** (recommended) - works out of the box +- **MAT file** - for [ifcb-analysis](https://github.com/hsosik/ifcb-analysis) compatibility (requires Python) +- **Both** - writes to both SQLite and `.mat` + ### Auto-save Work is automatically saved when: diff --git a/vignettes/user-guide.Rmd b/vignettes/user-guide.Rmd index c3676b4..a8d20ec 100644 --- a/vignettes/user-guide.Rmd +++ b/vignettes/user-guide.Rmd @@ -200,7 +200,7 @@ Files matching `*_class*.mat` pattern containing: ### Existing Annotations -Previously saved annotations (in output folder) are automatically detected and can be resumed. +Previously saved annotations (in SQLite database or `.mat` files in the output folder) are automatically detected and can be resumed. When both exist, the SQLite version is loaded (faster). --- @@ -248,9 +248,24 @@ ClassiPyR::rescan_file_index( ## Output Files -When you save, the app creates: +When you save, the app creates files based on your chosen storage format (configurable in Settings). -### Annotation MAT File +### SQLite Database (default) + +`db_folder/annotations.sqlite` + +A single SQLite database file containing annotations for all samples. This is the default storage backend: + +- No Python dependency required +- Fast read/write performance +- Single file for all samples — easy to back up and manage +- Contains `annotations` table (one row per ROI) and `class_lists` table (preserves class indices for `.mat` export) + +The database is stored in a separate **Database Folder** (configurable in Settings), which defaults to a local user-level directory (`tools::R_user_dir("ClassiPyR", "data")`). This separation ensures the SQLite database stays on a local filesystem even when the Output Folder is on a network drive. + +> **Note**: The SQLite database **must** be on a local drive. [SQLite file locking is unreliable on network filesystems](https://www.sqlite.org/useovernet.html) (NFS/SMB), which can lead to database corruption. For multi-user workflows, each annotator should use their own local Database Folder. + +### Annotation MAT File (optional) `output/[sample_name].mat` @@ -259,7 +274,7 @@ MATLAB-compatible format with: - `classlist`: ROI numbers and class indices - Compatible with [ifcb-analysis](https://github.com/hsosik/ifcb-analysis) toolbox -> **Note**: Saving MAT files requires Python with scipy. +> **Note**: Saving MAT files requires Python with scipy. Enable in Settings > Annotation Storage by selecting "MAT file" or "Both". ### Statistics Files @@ -287,11 +302,26 @@ Images organized into class folders for training CNN models or other classifiers |---------|-------------| | Classification Folder | Source of CSV/MAT classifications | | ROI Data Folder | IFCB raw files (ROI/ADC/HDR) | -| Output Folder | Where MAT and CSV output goes | +| Output Folder | Where MAT files and statistics go (can be on a network drive) | +| Database Folder | Where the SQLite database is stored (must be a local drive) | | PNG Output Folder | Where organized images go | Folder paths are configured using a web-based folder browser that works on all platforms (Linux, macOS, Windows). Changing folder paths in Settings automatically invalidates the file index cache, triggering a fresh scan. +### Annotation Storage + +| Format | Description | +|--------|-------------| +| SQLite (recommended) | Default. Stores annotations in `annotations.sqlite` in the Database Folder. No Python needed. | +| MAT file | MATLAB-compatible `.mat` files for [ifcb-analysis](https://github.com/hsosik/ifcb-analysis). Requires Python with scipy. | +| Both | Writes to both SQLite and `.mat` for maximum compatibility. | + +Below the format selector, two buttons allow bulk conversion between formats: + +- **Import .mat → SQLite**: Imports all `.mat` annotation files from the output folder into the SQLite database. Already-imported samples are skipped. +- **Export SQLite → .mat**: Exports all annotated samples from the database to `.mat` files. Requires Python with scipy. +- **Export SQLite → PNG**: Extracts annotated images from ROI files into class-name subfolders in the PNG Output Folder. Useful for building training datasets for CNN classifiers. + ### Auto-Sync | Setting | Description | @@ -374,11 +404,9 @@ Settings are loaded automatically when you start the app, so your folder paths, ## Dependencies -`ClassiPyR` relies on **[`iRfcb`](https://github.com/EuropeanIFCBGroup/iRfcb)** for all IFCB data operations: +`ClassiPyR` relies on: -- Extracting images from ROI files -- Reading ADC metadata (dimensions, timestamps) -- Reading and writing MATLAB .mat files -- Class list handling +- **[`iRfcb`](https://github.com/EuropeanIFCBGroup/iRfcb)** for IFCB data operations (extracting images, reading ADC metadata, reading/writing `.mat` files, class list handling) +- **[`RSQLite`](https://CRAN.R-project.org/package=RSQLite)** and **[`DBI`](https://CRAN.R-project.org/package=DBI)** for the SQLite annotation database -`iRfcb` is installed automatically as a dependency when you install `ClassiPyR`. +All R dependencies are installed automatically when you install `ClassiPyR`. Python is only needed for `.mat` file export. From fb7a7a31de2e12add2a8bd4d3f3dab47900a21f5 Mon Sep 17 00:00:00 2001 From: anderstorstensson Date: Mon, 16 Feb 2026 22:44:02 +0100 Subject: [PATCH 2/2] make sure .mat classlist is preseved during import/export --- R/database.R | 61 +++++-- R/sample_loading.R | 14 +- inst/app/server.R | 9 +- man/import_all_mat_to_db.Rd | 10 +- man/import_mat_to_db.Rd | 17 +- man/save_annotations_db.Rd | 7 +- tests/testthat/test-database.R | 298 +++++++++++++++++++++++++++++++-- 7 files changed, 355 insertions(+), 61 deletions(-) diff --git a/R/database.R b/R/database.R index 448c52b..536a218 100644 --- a/R/database.R +++ b/R/database.R @@ -48,6 +48,7 @@ init_db_schema <- function(con) { class_name TEXT NOT NULL, annotator TEXT, timestamp TEXT DEFAULT (datetime('now')), + is_manual INTEGER NOT NULL DEFAULT 1, PRIMARY KEY (sample_name, roi_number) ) ") @@ -61,6 +62,12 @@ init_db_schema <- function(con) { ) ") + # Migration: add is_manual column to existing databases that lack it + cols <- dbGetQuery(con, "PRAGMA table_info(annotations)") + if (!"is_manual" %in% cols$name) { + dbExecute(con, "ALTER TABLE annotations ADD COLUMN is_manual INTEGER NOT NULL DEFAULT 1") + } + invisible(NULL) } @@ -76,6 +83,9 @@ init_db_schema <- function(con) { #' @param class2use Character vector of class names (preserves index order for #' .mat export) #' @param annotator Annotator name +#' @param is_manual Integer vector of 0/1 flags indicating whether each ROI was +#' manually reviewed (1) or not yet reviewed (0, corresponding to NaN in .mat +#' files). If \code{NULL} (the default), all ROIs are treated as reviewed. #' @return TRUE on success, FALSE on failure #' @export #' @examples @@ -85,7 +95,8 @@ init_db_schema <- function(con) { #' classifications, class2use, "Jane") #' } save_annotations_db <- function(db_path, sample_name, classifications, - class2use, annotator = "Unknown") { + class2use, annotator = "Unknown", + is_manual = NULL) { if (is.null(classifications) || nrow(classifications) == 0) { return(FALSE) } @@ -100,12 +111,17 @@ save_annotations_db <- function(db_path, sample_name, classifications, # Extract ROI numbers from file_name (e.g., "D20230101T120000_IFCB134_00001.png" -> 1) roi_numbers <- as.integer(gsub(".*_(\\d+)\\.png$", "\\1", classifications$file_name)) + if (is.null(is_manual)) { + is_manual <- rep(1L, nrow(classifications)) + } + annotations_df <- data.frame( sample_name = sample_name, roi_number = roi_numbers, class_name = classifications$class_name, annotator = annotator, timestamp = format(Sys.time(), "%Y-%m-%d %H:%M:%S"), + is_manual = as.integer(is_manual), stringsAsFactors = FALSE ) @@ -284,12 +300,13 @@ update_annotator <- function(db_path, sample_names, annotator) { #' Import a .mat annotation file into the SQLite database #' #' Reads an existing .mat annotation file and writes its data into the SQLite -#' database. Useful for migrating existing annotations to the new backend. +#' database. The class list (\code{class2use_manual}) and classlist indices are +#' read directly from the .mat file to ensure a faithful import. ROIs with NaN +#' indices (not yet reviewed) are stored with \code{is_manual = 0}. #' #' @param mat_path Path to the .mat annotation file #' @param db_path Path to the SQLite database file #' @param sample_name Sample name -#' @param class2use Character vector of class names #' @param annotator Annotator name (defaults to \code{"imported"}) #' @return TRUE on success, FALSE on failure #' @export @@ -298,11 +315,10 @@ update_annotator <- function(db_path, sample_names, annotator) { #' import_mat_to_db( #' mat_path = "/data/manual/D20230101T120000_IFCB134.mat", #' db_path = get_db_path("/data/manual"), -#' sample_name = "D20230101T120000_IFCB134", -#' class2use = load_class_list("/data/class2use.mat") +#' sample_name = "D20230101T120000_IFCB134" #' ) #' } -import_mat_to_db <- function(mat_path, db_path, sample_name, class2use, +import_mat_to_db <- function(mat_path, db_path, sample_name, annotator = "imported") { if (!file.exists(mat_path)) { warning("MAT file not found: ", mat_path) @@ -310,12 +326,20 @@ import_mat_to_db <- function(mat_path, db_path, sample_name, class2use, } tryCatch({ + # Read the class list embedded in the .mat file + class2use <- as.character(ifcb_get_mat_variable(mat_path, + variable_name = "class2use_manual")) + classlist <- ifcb_get_mat_variable(mat_path, variable_name = "classlist") roi_numbers <- classlist[, 1] class_indices <- classlist[, 2] + # Detect NaN (not yet reviewed) vs classified ROIs + is_nan <- is.nan(class_indices) + is_manual <- ifelse(is_nan, 0L, 1L) + class_names <- vapply(class_indices, function(idx) { - if (is.na(idx) || idx < 1 || idx > length(class2use)) { + if (is.na(idx) || is.nan(idx) || idx < 1 || idx > length(class2use)) { "unclassified" } else { class2use[idx] @@ -329,7 +353,8 @@ import_mat_to_db <- function(mat_path, db_path, sample_name, class2use, stringsAsFactors = FALSE ) - save_annotations_db(db_path, sample_name, classifications, class2use, annotator) + save_annotations_db(db_path, sample_name, classifications, class2use, + annotator, is_manual = is_manual) }, error = function(e) { warning("Failed to import MAT file: ", e$message) FALSE @@ -361,9 +386,9 @@ export_db_to_mat <- function(db_path, sample_name, output_folder) { con <- dbConnect(SQLite(), db_path) on.exit(dbDisconnect(con), add = TRUE) - # Get annotations for this sample + # Get annotations for this sample (including is_manual flag) rows <- dbGetQuery(con, - "SELECT roi_number, class_name FROM annotations WHERE sample_name = ? ORDER BY roi_number", + "SELECT roi_number, class_name, is_manual FROM annotations WHERE sample_name = ? ORDER BY roi_number", params = list(sample_name) ) @@ -385,10 +410,13 @@ export_db_to_mat <- function(db_path, sample_name, output_folder) { class2use <- class_list$class_name - # Build classlist integer vector: map class names to indices + # Build classlist numeric vector: map class names to indices + # Use NaN for unreviewed ROIs (is_manual == 0) to preserve the distinction classlist_indices <- match(rows$class_name, class2use) # Any unmatched classes default to 1 (typically "unclassified") classlist_indices[is.na(classlist_indices)] <- 1L + classlist_indices <- as.numeric(classlist_indices) + classlist_indices[rows$is_manual == 0L] <- NaN output_file <- file.path(output_folder, paste0(sample_name, ".mat")) @@ -410,22 +438,21 @@ export_db_to_mat <- function(db_path, sample_name, output_folder) { #' Bulk import .mat annotation files into the SQLite database #' #' Scans a folder for \code{.mat} annotation files (excluding classifier output -#' files matching \code{*_class*.mat}) and imports each into the database. +#' files matching \code{*_class*.mat}) and imports each into the database. Each +#' file's embedded \code{class2use_manual} is used for class-name mapping. #' #' @param mat_folder Folder containing .mat annotation files #' @param db_path Path to the SQLite database file -#' @param class2use Character vector of class names #' @param annotator Annotator name (defaults to \code{"imported"}) #' @return Named list with counts: \code{success}, \code{failed}, \code{skipped} #' @export #' @examples #' \dontrun{ #' db_path <- get_db_path("/data/manual") -#' class2use <- load_class_list("/data/class2use.mat") -#' result <- import_all_mat_to_db("/data/manual", db_path, class2use) +#' result <- import_all_mat_to_db("/data/manual", db_path) #' cat(result$success, "imported,", result$failed, "failed,", result$skipped, "skipped\n") #' } -import_all_mat_to_db <- function(mat_folder, db_path, class2use, +import_all_mat_to_db <- function(mat_folder, db_path, annotator = "imported") { mat_files <- list.files(mat_folder, pattern = "\\.mat$", full.names = TRUE) # Exclude classifier output files (*_class*.mat) and class2use files @@ -449,7 +476,7 @@ import_all_mat_to_db <- function(mat_folder, db_path, class2use, next } - ok <- import_mat_to_db(mat_path, db_path, sample_name, class2use, annotator) + ok <- import_mat_to_db(mat_path, db_path, sample_name, annotator) if (isTRUE(ok)) { counts$success <- counts$success + 1L } else { diff --git a/R/sample_loading.R b/R/sample_loading.R index ae94158..0f97e9f 100644 --- a/R/sample_loading.R +++ b/R/sample_loading.R @@ -104,13 +104,23 @@ load_from_mat <- function(mat_path, sample_name, class2use, roi_dimensions) { # Read classlist from MAT file (column 2 contains class indices) classlist <- ifcb_get_mat_variable(mat_path, variable_name = "classlist") + # Prefer the class list embedded in the .mat file for accurate index mapping + mat_class2use <- tryCatch( + as.character(ifcb_get_mat_variable(mat_path, + variable_name = "class2use_manual")), + error = function(e) NULL + ) + if (!is.null(mat_class2use) && length(mat_class2use) > 0) { + class2use <- mat_class2use + } + # Map class indices to class names roi_numbers <- classlist[, 1] class_indices <- classlist[, 2] - # Get class names from indices (handle 0 or NA as "unclassified") + # Get class names from indices (handle NaN, 0, or NA as "unclassified") class_names <- sapply(class_indices, function(idx) { - if (is.na(idx) || idx < 1 || idx > length(class2use)) { + if (is.na(idx) || is.nan(idx) || idx < 1 || idx > length(class2use)) { return("unclassified") } return(class2use[idx]) diff --git a/inst/app/server.R b/inst/app/server.R index 69545ee..4c82bcc 100644 --- a/inst/app/server.R +++ b/inst/app/server.R @@ -725,12 +725,6 @@ server <- function(input, output, session) { type = "error") return() } - if (is.null(rv$class2use) || length(rv$class2use) == 0) { - showNotification("No class list loaded. Load a class list first.", - type = "error") - return() - } - db_path <- get_db_path(config$db_folder) annotator <- if (!is.null(input$annotator_name) && nzchar(input$annotator_name)) { input$annotator_name @@ -739,8 +733,7 @@ server <- function(input, output, session) { } withProgress(message = "Importing .mat files to SQLite...", { - result <- import_all_mat_to_db(config$output_folder, db_path, - rv$class2use, annotator) + result <- import_all_mat_to_db(config$output_folder, db_path, annotator) }) showNotification( diff --git a/man/import_all_mat_to_db.Rd b/man/import_all_mat_to_db.Rd index f7e31c7..3da50dd 100644 --- a/man/import_all_mat_to_db.Rd +++ b/man/import_all_mat_to_db.Rd @@ -4,15 +4,13 @@ \alias{import_all_mat_to_db} \title{Bulk import .mat annotation files into the SQLite database} \usage{ -import_all_mat_to_db(mat_folder, db_path, class2use, annotator = "imported") +import_all_mat_to_db(mat_folder, db_path, annotator = "imported") } \arguments{ \item{mat_folder}{Folder containing .mat annotation files} \item{db_path}{Path to the SQLite database file} -\item{class2use}{Character vector of class names} - \item{annotator}{Annotator name (defaults to \code{"imported"})} } \value{ @@ -20,13 +18,13 @@ Named list with counts: \code{success}, \code{failed}, \code{skipped} } \description{ Scans a folder for \code{.mat} annotation files (excluding classifier output -files matching \code{*_class*.mat}) and imports each into the database. +files matching \code{*_class*.mat}) and imports each into the database. Each +file's embedded \code{class2use_manual} is used for class-name mapping. } \examples{ \dontrun{ db_path <- get_db_path("/data/manual") -class2use <- load_class_list("/data/class2use.mat") -result <- import_all_mat_to_db("/data/manual", db_path, class2use) +result <- import_all_mat_to_db("/data/manual", db_path) cat(result$success, "imported,", result$failed, "failed,", result$skipped, "skipped\n") } } diff --git a/man/import_mat_to_db.Rd b/man/import_mat_to_db.Rd index 8389b49..f9db02d 100644 --- a/man/import_mat_to_db.Rd +++ b/man/import_mat_to_db.Rd @@ -4,13 +4,7 @@ \alias{import_mat_to_db} \title{Import a .mat annotation file into the SQLite database} \usage{ -import_mat_to_db( - mat_path, - db_path, - sample_name, - class2use, - annotator = "imported" -) +import_mat_to_db(mat_path, db_path, sample_name, annotator = "imported") } \arguments{ \item{mat_path}{Path to the .mat annotation file} @@ -19,8 +13,6 @@ import_mat_to_db( \item{sample_name}{Sample name} -\item{class2use}{Character vector of class names} - \item{annotator}{Annotator name (defaults to \code{"imported"})} } \value{ @@ -28,15 +20,16 @@ TRUE on success, FALSE on failure } \description{ Reads an existing .mat annotation file and writes its data into the SQLite -database. Useful for migrating existing annotations to the new backend. +database. The class list (\code{class2use_manual}) and classlist indices are +read directly from the .mat file to ensure a faithful import. ROIs with NaN +indices (not yet reviewed) are stored with \code{is_manual = 0}. } \examples{ \dontrun{ import_mat_to_db( mat_path = "/data/manual/D20230101T120000_IFCB134.mat", db_path = get_db_path("/data/manual"), - sample_name = "D20230101T120000_IFCB134", - class2use = load_class_list("/data/class2use.mat") + sample_name = "D20230101T120000_IFCB134" ) } } diff --git a/man/save_annotations_db.Rd b/man/save_annotations_db.Rd index babdd6c..78688ca 100644 --- a/man/save_annotations_db.Rd +++ b/man/save_annotations_db.Rd @@ -9,7 +9,8 @@ save_annotations_db( sample_name, classifications, class2use, - annotator = "Unknown" + annotator = "Unknown", + is_manual = NULL ) } \arguments{ @@ -24,6 +25,10 @@ save_annotations_db( .mat export)} \item{annotator}{Annotator name} + +\item{is_manual}{Integer vector of 0/1 flags indicating whether each ROI was +manually reviewed (1) or not yet reviewed (0, corresponding to NaN in .mat +files). If \code{NULL} (the default), all ROIs are treated as reviewed.} } \value{ TRUE on success, FALSE on failure diff --git a/tests/testthat/test-database.R b/tests/testthat/test-database.R index 1b11cc7..47bee39 100644 --- a/tests/testthat/test-database.R +++ b/tests/testthat/test-database.R @@ -385,28 +385,18 @@ test_that("import_mat_to_db migrates data correctly", { skip_if_not(reticulate::py_available(), "Python not available") skip_if_not(reticulate::py_module_available("scipy"), "scipy not available") - # Use test data if available - mat_path <- testthat::test_path("test_data", "raw", "2022", "D20220522", - "D20220522T000439_IFCB134.mat") - - # Look for a MAT annotation file in test data - # If no test .mat annotation exists, create one via save first sample_name <- "D20220522T000439_IFCB134" - class2use_path <- testthat::test_path("test_data", "class2use.mat") - skip_if_not(file.exists(class2use_path), "Test class2use file not found") # Check if there's a test annotation mat file output_test <- testthat::test_path("test_data", "manual") test_mat <- file.path(output_test, paste0(sample_name, ".mat")) skip_if_not(file.exists(test_mat), "No test MAT annotation file for migration test") - class2use <- load_class_list(class2use_path) - db_dir <- tempfile("db_") dir.create(db_dir) db_path <- get_db_path(db_dir) - result <- import_mat_to_db(test_mat, db_path, sample_name, class2use, "migrated") + result <- import_mat_to_db(test_mat, db_path, sample_name, "migrated") expect_true(result) # Verify data was imported @@ -420,7 +410,7 @@ test_that("import_mat_to_db returns FALSE for missing file", { result <- import_mat_to_db( "/nonexistent/file.mat", tempfile(fileext = ".sqlite"), - "sample", c("unclassified"), "test" + "sample", "test" ) expect_false(result) }) @@ -516,7 +506,7 @@ test_that("import_all_mat_to_db imports multiple files and returns correct count classlist = c(1, 2) ) - result <- import_all_mat_to_db(mat_dir, db_path, class2use, "test") + result <- import_all_mat_to_db(mat_dir, db_path, "test") expect_equal(result$success, 2L) expect_equal(result$failed, 0L) @@ -529,7 +519,7 @@ test_that("import_all_mat_to_db imports multiple files and returns correct count expect_false("sample_C_class_v1" %in% samples) # Re-import should skip existing - result2 <- import_all_mat_to_db(mat_dir, db_path, class2use, "test") + result2 <- import_all_mat_to_db(mat_dir, db_path, "test") expect_equal(result2$success, 0L) expect_equal(result2$skipped, 2L) @@ -601,7 +591,7 @@ test_that("round-trip: DB -> .mat -> DB produces matching data", { db_path2 <- get_db_path(db_dir2) mat_path <- file.path(mat_dir, paste0(sample_name, ".mat")) - import_mat_to_db(mat_path, db_path2, sample_name, class2use, "reimported") + import_mat_to_db(mat_path, db_path2, sample_name, "reimported") # Compare: read both DBs and check class names match con1 <- DBI::dbConnect(RSQLite::SQLite(), db_path) @@ -812,3 +802,281 @@ test_that("export_all_db_to_png exports multiple samples and skips missing ROIs" unlink(c(db_dir, png_dir), recursive = TRUE) }) + +test_that("save_annotations_db stores is_manual flags", { + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + sample_name <- "D20230101T120000_IFCB134" + class2use <- c("unclassified", "Diatom", "Ciliate") + + classifications <- data.frame( + file_name = sprintf("%s_%05d.png", sample_name, 1:3), + class_name = c("Diatom", "unclassified", "Ciliate"), + stringsAsFactors = FALSE + ) + + result <- save_annotations_db(db_path, sample_name, classifications, + class2use, "TestUser", + is_manual = c(1L, 0L, 1L)) + expect_true(result) + + con <- DBI::dbConnect(RSQLite::SQLite(), db_path) + on.exit(DBI::dbDisconnect(con)) + + rows <- DBI::dbGetQuery(con, + "SELECT roi_number, class_name, is_manual FROM annotations WHERE sample_name = ? ORDER BY roi_number", + params = list(sample_name)) + + expect_equal(rows$is_manual, c(1L, 0L, 1L)) + expect_equal(rows$class_name, c("Diatom", "unclassified", "Ciliate")) + + unlink(db_dir, recursive = TRUE) +}) + +test_that("save_annotations_db defaults is_manual to 1", { + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + sample_name <- "D20230101T120000_IFCB134" + + classifications <- data.frame( + file_name = sprintf("%s_%05d.png", sample_name, 1:2), + class_name = c("Diatom", "Ciliate"), + stringsAsFactors = FALSE + ) + + save_annotations_db(db_path, sample_name, classifications, + c("unclassified", "Diatom", "Ciliate"), "TestUser") + + con <- DBI::dbConnect(RSQLite::SQLite(), db_path) + on.exit(DBI::dbDisconnect(con)) + + rows <- DBI::dbGetQuery(con, + "SELECT is_manual FROM annotations WHERE sample_name = ?", + params = list(sample_name)) + + expect_true(all(rows$is_manual == 1L)) + + unlink(db_dir, recursive = TRUE) +}) + +test_that("schema migration adds is_manual to existing DB", { + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + # Create a database with the OLD schema (no is_manual column) + con <- DBI::dbConnect(RSQLite::SQLite(), db_path) + DBI::dbExecute(con, " + CREATE TABLE annotations ( + sample_name TEXT NOT NULL, + roi_number INTEGER NOT NULL, + class_name TEXT NOT NULL, + annotator TEXT, + timestamp TEXT DEFAULT (datetime('now')), + PRIMARY KEY (sample_name, roi_number) + ) + ") + DBI::dbExecute(con, " + CREATE TABLE class_lists ( + sample_name TEXT NOT NULL, + class_index INTEGER NOT NULL, + class_name TEXT NOT NULL, + PRIMARY KEY (sample_name, class_index) + ) + ") + + # Insert a row without is_manual + DBI::dbExecute(con, + "INSERT INTO annotations (sample_name, roi_number, class_name, annotator) VALUES (?, ?, ?, ?)", + params = list("sample_old", 1L, "Diatom", "test")) + DBI::dbDisconnect(con) + + # Now run init_db_schema which should migrate + con2 <- DBI::dbConnect(RSQLite::SQLite(), db_path) + on.exit(DBI::dbDisconnect(con2)) + init_db_schema(con2) + + cols <- DBI::dbGetQuery(con2, "PRAGMA table_info(annotations)") + expect_true("is_manual" %in% cols$name) + + # Existing row should have default value 1 + row <- DBI::dbGetQuery(con2, + "SELECT is_manual FROM annotations WHERE sample_name = 'sample_old'") + expect_equal(row$is_manual, 1L) + + unlink(db_dir, recursive = TRUE) +}) + +test_that("import_mat_to_db reads class2use_manual from .mat", { + skip_if_not_installed("iRfcb") + skip_if_not(reticulate::py_available(), "Python not available") + skip_if_not(reticulate::py_module_available("scipy"), "scipy not available") + + # Create a .mat file with a known class list + mat_dir <- tempfile("mat_") + dir.create(mat_dir) + mat_path <- file.path(mat_dir, "test_sample.mat") + class2use <- c("unclassified", "Diatom", "Ciliate") + + iRfcb::ifcb_create_manual_file( + roi_length = 3, class2use = class2use, + output_file = mat_path, + classlist = c(2, 3, 1) + ) + + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + result <- import_mat_to_db(mat_path, db_path, "test_sample") + expect_true(result) + + # Verify the class list stored in DB matches the .mat file's embedded list + con <- DBI::dbConnect(RSQLite::SQLite(), db_path) + on.exit(DBI::dbDisconnect(con)) + + cl <- DBI::dbGetQuery(con, + "SELECT class_name FROM class_lists WHERE sample_name = 'test_sample' ORDER BY class_index") + expect_equal(cl$class_name, class2use) + + # Verify class names mapped correctly + ann <- DBI::dbGetQuery(con, + "SELECT roi_number, class_name FROM annotations WHERE sample_name = 'test_sample' ORDER BY roi_number") + expect_equal(ann$class_name, c("Diatom", "Ciliate", "unclassified")) + + unlink(c(mat_dir, db_dir), recursive = TRUE) +}) + +test_that("import_mat_to_db preserves NaN as is_manual=0", { + skip_if_not_installed("iRfcb") + skip_if_not(reticulate::py_available(), "Python not available") + skip_if_not(reticulate::py_module_available("scipy"), "scipy not available") + + mat_dir <- tempfile("mat_") + dir.create(mat_dir) + mat_path <- file.path(mat_dir, "test_nan.mat") + class2use <- c("unclassified", "Diatom", "Ciliate") + + # Create .mat with NaN entries (unreviewed ROIs) + iRfcb::ifcb_create_manual_file( + roi_length = 4, class2use = class2use, + output_file = mat_path, + classlist = c(2, NaN, 3, NaN) + ) + + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + result <- import_mat_to_db(mat_path, db_path, "test_nan") + expect_true(result) + + con <- DBI::dbConnect(RSQLite::SQLite(), db_path) + on.exit(DBI::dbDisconnect(con)) + + rows <- DBI::dbGetQuery(con, + "SELECT roi_number, class_name, is_manual FROM annotations WHERE sample_name = 'test_nan' ORDER BY roi_number") + + expect_equal(rows$is_manual, c(1L, 0L, 1L, 0L)) + expect_equal(rows$class_name, c("Diatom", "unclassified", "Ciliate", "unclassified")) + + unlink(c(mat_dir, db_dir), recursive = TRUE) +}) + +test_that("export_db_to_mat restores NaN for is_manual=0 rows", { + skip_if_not_installed("iRfcb") + skip_if_not(reticulate::py_available(), "Python not available") + skip_if_not(reticulate::py_module_available("scipy"), "scipy not available") + + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + sample_name <- "D20230101T120000_IFCB134" + class2use <- c("unclassified", "Diatom", "Ciliate") + + classifications <- data.frame( + file_name = sprintf("%s_%05d.png", sample_name, 1:4), + class_name = c("Diatom", "unclassified", "Ciliate", "unclassified"), + stringsAsFactors = FALSE + ) + # ROIs 2 and 4 are unreviewed (NaN in .mat) + save_annotations_db(db_path, sample_name, classifications, class2use, + "TestUser", is_manual = c(1L, 0L, 1L, 0L)) + + mat_dir <- tempfile("mat_") + dir.create(mat_dir) + + result <- export_db_to_mat(db_path, sample_name, mat_dir) + expect_true(result) + + mat_path <- file.path(mat_dir, paste0(sample_name, ".mat")) + classlist <- iRfcb::ifcb_get_mat_variable(mat_path, variable_name = "classlist") + + # Reviewed ROIs should have valid indices, unreviewed should be NaN + expect_equal(classlist[1, 2], 2) # Diatom + expect_true(is.nan(classlist[2, 2])) # unreviewed -> NaN + expect_equal(classlist[3, 2], 3) # Ciliate + expect_true(is.nan(classlist[4, 2])) # unreviewed -> NaN + + unlink(c(db_dir, mat_dir), recursive = TRUE) +}) + +test_that("full roundtrip: .mat -> SQLite -> .mat preserves NaN and class list", { + skip_if_not_installed("iRfcb") + skip_if_not(reticulate::py_available(), "Python not available") + skip_if_not(reticulate::py_module_available("scipy"), "scipy not available") + + # Create original .mat with NaN entries + mat_dir <- tempfile("mat_orig_") + dir.create(mat_dir) + original_mat <- file.path(mat_dir, "roundtrip_sample.mat") + class2use <- c("unclassified", "Diatom", "Ciliate", "Dinoflagellate") + + original_classlist <- c(2, NaN, 3, 4, NaN) + iRfcb::ifcb_create_manual_file( + roi_length = 5, class2use = class2use, + output_file = original_mat, + classlist = original_classlist + ) + + # Import into SQLite + db_dir <- tempfile("db_") + dir.create(db_dir) + db_path <- get_db_path(db_dir) + + import_mat_to_db(original_mat, db_path, "roundtrip_sample") + + # Export back to .mat + export_dir <- tempfile("mat_export_") + dir.create(export_dir) + export_db_to_mat(db_path, "roundtrip_sample", export_dir) + + # Read back and compare + exported_mat <- file.path(export_dir, "roundtrip_sample.mat") + exported_classlist <- iRfcb::ifcb_get_mat_variable(exported_mat, + variable_name = "classlist") + exported_class2use <- as.character( + iRfcb::ifcb_get_mat_variable(exported_mat, + variable_name = "class2use_manual")) + + # Class list should match exactly + expect_equal(exported_class2use, class2use) + + # Classlist indices should match: classified ROIs keep their index, NaN stays NaN + for (i in seq_along(original_classlist)) { + if (is.nan(original_classlist[i])) { + expect_true(is.nan(exported_classlist[i, 2]), + info = paste("ROI", i, "should be NaN")) + } else { + expect_equal(exported_classlist[i, 2], original_classlist[i], + info = paste("ROI", i, "index mismatch")) + } + } + + unlink(c(mat_dir, db_dir, export_dir), recursive = TRUE) +})