---
title: "Auditing and scaling biofetchR workflows"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Auditing and scaling biofetchR workflows}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  message = TRUE,
  warning = TRUE
)

# Helper for calculating retention rates safely.
safe_rate <- function(numerator, denominator) {
  ifelse(
    !is.na(denominator) & denominator > 0,
    numerator / denominator,
    NA_real_
  )
}

# Helper for compact display of selected columns.
select_existing <- function(x, columns) {
  x[
    ,
    intersect(columns, names(x)),
    drop = FALSE
  ]
}

# Vignette-safe example paths. These keep all example outputs and caches under
# tempdir(), which is safe for CRAN examples/vignettes/tests.
example_output_dir <- file.path(tempdir(), "biofetchR-vignette-outputs")
example_cache_dir <- file.path(tempdir(), "biofetchR-vignette-cache")
```

```{r load-package}
library(biofetchR)
```

# Purpose

Larger occurrence workflows are useful only when their outputs can be checked,
understood and reused. A completed run should show which rows were attempted,
which rows completed, how many records were retained after each processing step,
where exported files were written, and which settings were used.

This vignette focuses on audit and scaling steps after a `biofetchR` workflow has
run. The workflow-specific vignettes cover terrestrial, freshwater, marine,
spatial-overlay and origin-evidence workflows. Here, the emphasis is on reading
the outputs, identifying rows that need review, rerunning selected rows, and
keeping enough information to reproduce the run later.

The main principle is to inspect before analysis. Rows with clear summaries,
sensible retention, expected output files and interpretable evidence fields can
be carried forward. Rows with missing files, unexpected record loss, unresolved
evidence or incomplete spatial assignment should be reviewed before they are used.

# Recommended output structure

For small tests, outputs can be written to a temporary directory. For project
work, use a stable folder structure that separates inputs, exported outputs,
provider caches, scripts and reports.

A clear folder structure might look like this:

```text
project/
  data_raw/
    species_country_input.csv
    marine_species_input.csv

  outputs/
    terrestrial_gadm_batch/
      gbif_summary.csv
      occurrence_files/
      origin_audit/
      taxonomy/

    marine_eez_batch/
      gbif_summary.csv
      occurrence_files/
      origin_audit/
      taxonomy/

    spatial_overlay_checks/
      gbif_summary.csv
      occurrence_files/

  cache/
    gadm/
    marine_overlays/
    griis/
    native_web/
    raster/
    wdpa/
    ramsar/

  scripts/
    01_run_smoke_test.R
    02_run_batch.R
    03_check_outputs.R
    04_rerun_failed_rows.R

  reports/
    sessionInfo.txt
    provider_citations.txt
```

The exact folder names can be changed. The important point is to keep final
occurrence outputs separate from reusable provider caches, while keeping summary
and audit files close to the processed records they describe.

# The main summary file

The main file to inspect after most high-level workflows is `gbif_summary.csv`.
This table usually contains one row per attempted species-region,
species-country or species-overlay combination.

The exact columns depend on the pipeline and settings used, but common fields
include:

```{r summary-column-reference, echo = FALSE}
summary_columns <- data.frame(
  column_group = c(
    "Input identifiers",
    "Spatial identifiers",
    "Record counts",
    "Status fields",
    "Output fields",
    "Evidence fields"
  ),
  example_columns = c(
    "species, iso2c, region_id, overlay",
    "region_source, gadm_unit, overlay, marine_region_name",
    "n_total, n_cleaned, n_thinned, n_with_overlay, n_without_overlay",
    "status, fail_stage, fail_reason",
    "output_file, output_dir",
    "native_filter_mode, griis_filter_mode, evidence_action"
  ),
  why_check = c(
    "Confirm which row was attempted.",
    "Confirm which spatial framework was used.",
    "Check how many records survived each processing step.",
    "Identify rows that succeeded, failed, or returned no usable records.",
    "Confirm where exported occurrence files were written.",
    "Confirm how native-range or invasive-status evidence was handled."
  ),
  stringsAsFactors = FALSE
)

summary_columns
```

Use this file as the first checkpoint. It gives a compact view of row status,
record counts, output paths and any evidence or spatial settings recorded by the
workflow.

# Example summary table

The example below is offline and does not use live GBIF data. It mimics the kind
of summary table produced by a mixed terrestrial and marine batch.

```{r example-summary}
summary_example <- data.frame(
  species = c(
    "Xenopus laevis",
    "Trachemys scripta",
    "Rattus rattus",
    "Carcinus maenas",
    "Mnemiopsis leidyi",
    "Undaria pinnatifida"
  ),
  workflow = c(
    "terrestrial_gadm",
    "terrestrial_gadm",
    "terrestrial_gadm",
    "marine_eez",
    "marine_eez",
    "marine_lme"
  ),
  region_id = c(
    "FR",
    "GB",
    "GB",
    NA,
    NA,
    NA
  ),
  overlay = c(
    NA,
    NA,
    NA,
    "eez",
    "eez",
    "lme"
  ),
  n_total = c(
    1340,
    220,
    0,
    980,
    640,
    410
  ),
  n_cleaned = c(
    1309,
    198,
    0,
    944,
    612,
    389
  ),
  n_thinned = c(
    861,
    174,
    0,
    702,
    455,
    301
  ),
  n_with_overlay = c(
    861,
    174,
    0,
    690,
    430,
    292
  ),
  n_without_overlay = c(
    0,
    0,
    0,
    12,
    25,
    9
  ),
  status = c(
    "success",
    "success",
    "no_records",
    "success",
    "success",
    "success"
  ),
  fail_stage = c(
    NA,
    NA,
    "import",
    NA,
    NA,
    NA
  ),
  fail_reason = c(
    NA,
    NA,
    "No records returned",
    NA,
    NA,
    NA
  ),
  output_file = c(
    file.path(
      example_output_dir,
      "terrestrial_gadm_batch",
      "occurrence_files",
      "Xenopus_laevis_FR.csv"
    ),
    file.path(
      example_output_dir,
      "terrestrial_gadm_batch",
      "occurrence_files",
      "Trachemys_scripta_GB.csv"
    ),
    NA,
    file.path(
      example_output_dir,
      "marine_eez_batch",
      "occurrence_files",
      "Carcinus_maenas_eez.csv"
    ),
    file.path(
      example_output_dir,
      "marine_eez_batch",
      "occurrence_files",
      "Mnemiopsis_leidyi_eez.csv"
    ),
    file.path(
      example_output_dir,
      "marine_lme_batch",
      "occurrence_files",
      "Undaria_pinnatifida_lme.csv"
    )
  ),
  stringsAsFactors = FALSE
)

summary_example
```

This table is illustrative. In a real workflow, read `gbif_summary.csv` from the
pipeline output directory.

```{r read-summary-template, eval = FALSE}
summary_tbl <- read.csv(
  file.path(example_output_dir, "terrestrial_gadm_batch", "gbif_summary.csv"),
  stringsAsFactors = FALSE
)
```

# Status checks

Start by checking the status distribution. A batch can complete overall while
still containing rows that returned no records, stopped during import, stopped
during spatial attribution, or need manual review.

```{r status-check}
table(summary_example$status, useNA = "ifany")
```

Rows that did not complete successfully should be inspected before analysis.

```{r failed-rows}
failed_rows <- summary_example[
  !summary_example$status %in% "success",
  ,
  drop = FALSE
]

failed_rows
```

When a row is skipped, stops early or returns no records, use the summary fields
to identify the likely cause. The issue may be a missing GBIF result, a
taxon-name problem, coordinate filtering, provider access, output writing, or a
spatial framework that does not match the retained records.

The most useful diagnostic fields are usually `status`, `fail_stage` and
`fail_reason`.

```{r failure-diagnostic-fields}
select_existing(
  failed_rows,
  c(
    "species",
    "workflow",
    "region_id",
    "overlay",
    "status",
    "fail_stage",
    "fail_reason"
  )
)
```

# Record-retention checks

Record counts show how much data remains after each processing step. Comparing
`n_total`, `n_cleaned`, `n_thinned` and overlay-assignment counts helps identify
rows that need closer inspection.

```{r retention-checks}
summary_example$retained_after_cleaning <- safe_rate(
  summary_example$n_cleaned,
  summary_example$n_total
)

summary_example$retained_after_thinning <- safe_rate(
  summary_example$n_thinned,
  summary_example$n_cleaned
)

summary_example$overlay_assignment_rate <- safe_rate(
  summary_example$n_with_overlay,
  summary_example$n_thinned
)

select_existing(
  summary_example,
  c(
    "species",
    "workflow",
    "n_total",
    "n_cleaned",
    "n_thinned",
    "n_with_overlay",
    "n_without_overlay",
    "retained_after_cleaning",
    "retained_after_thinning",
    "overlay_assignment_rate"
  )
)
```

Low retention after cleaning can indicate many invalid coordinates, missing
coordinates, exact zero-zero records, institution coordinates, centroid records
or records removed by coordinate-uncertainty filters.

Low retention after thinning usually indicates strong spatial clustering. This
may be appropriate for the analysis, but it should be recorded when thinning is
used in the final workflow.

Low overlay assignment rates should also be reviewed. For example, unmatched
marine records may fall outside the selected overlay, sit near boundaries, occur
in provider coverage gaps, or retain coordinate problems that were not removed
during cleaning.

# Rows needing review

Not every row needing attention has a non-success status. Some rows complete but
still deserve review because they have low retention, many unmatched records, or
evidence fields that affect interpretation.

```{r review-flags}
summary_example$needs_review <- with(
  summary_example,
  status != "success" |
    retained_after_cleaning < 0.5 |
    retained_after_thinning < 0.5 |
    overlay_assignment_rate < 0.9
)

select_existing(
  summary_example[summary_example$needs_review, , drop = FALSE],
  c(
    "species",
    "workflow",
    "status",
    "n_total",
    "n_cleaned",
    "n_thinned",
    "n_with_overlay",
    "n_without_overlay",
    "retained_after_cleaning",
    "retained_after_thinning",
    "overlay_assignment_rate",
    "fail_stage",
    "fail_reason"
  )
)
```

The thresholds above are examples only. Choose review thresholds that match the
taxa, spatial scale and purpose of the analysis.

# Output-file checks

Successful rows should usually point to exported occurrence files. Before
combining outputs, check that listed files exist and can be opened.

The example paths below are illustrative, so they are expected not to exist when
this vignette is rendered.

```{r output-path-checks}
summary_example$output_file_listed <- !is.na(summary_example$output_file) &
  nzchar(summary_example$output_file)

summary_example$output_file_exists <- ifelse(
  summary_example$output_file_listed,
  file.exists(summary_example$output_file),
  FALSE
)

select_existing(
  summary_example,
  c(
    "species",
    "status",
    "output_file_listed",
    "output_file_exists",
    "output_file"
  )
)
```

In a real run, a successful row with a missing output file should be checked
before the exported dataset is used. The file may have been moved, the path may
need to be resolved relative to the output directory, or the export may not have
completed as expected.

# Rerunning problem rows

In larger batches, rerun only the rows that need another pass. This avoids
repeating completed rows and makes the new output easier to compare with the
original run.

```{r rows-to-rerun}
rows_to_rerun <- summary_example[
  !summary_example$status %in% "success",
  ,
  drop = FALSE
]

terrestrial_rows_to_rerun <- rows_to_rerun[
  grepl("^terrestrial|^freshwater", rows_to_rerun$workflow),
  ,
  drop = FALSE
]

marine_rows_to_rerun <- rows_to_rerun[
  grepl("^marine", rows_to_rerun$workflow),
  ,
  drop = FALSE
]

select_existing(
  rows_to_rerun,
  c(
    "species",
    "workflow",
    "region_id",
    "overlay",
    "fail_stage",
    "fail_reason"
  )
)
```

For terrestrial and freshwater workflows, rerun the selected rows with the same
spatial settings as the original run.

```{r rerun-failed-terrestrial-template, eval = FALSE}
failed_terrestrial_input <- data.frame(
  species = terrestrial_rows_to_rerun$species,
  iso2c = terrestrial_rows_to_rerun$region_id,
  stringsAsFactors = FALSE
)

process_gbif_terrestrial_freshwater_pipeline(
  df = failed_terrestrial_input,

  output_dir = file.path(example_output_dir, "terrestrial_gadm_rerun"),

  user = Sys.getenv("GBIF_USER"),
  pwd = Sys.getenv("GBIF_PWD"),
  email = Sys.getenv("GBIF_EMAIL"),

  region_source = "gadm",
  gadm_unit = 1,

  batch_size = 1,
  apply_cleaning = TRUE,
  apply_thinning = TRUE,
  dist_km = 5,

  export_summary = TRUE,
  store_in_memory = FALSE,
  return_all_results = FALSE
)
```

For marine workflows, rerun the selected species with the same overlay and
evidence settings as the original run.

```{r rerun-failed-marine-template, eval = FALSE}
failed_marine_input <- data.frame(
  species = marine_rows_to_rerun$species,
  stringsAsFactors = FALSE
)

process_gbif_marine_pipeline(
  df = failed_marine_input,

  output_dir = file.path(example_output_dir, "marine_eez_rerun"),

  user = Sys.getenv("GBIF_USER"),
  pwd = Sys.getenv("GBIF_PWD"),
  email = Sys.getenv("GBIF_EMAIL"),

  overlay = "eez",
  overlay_cache_dir = file.path(example_cache_dir, "marine_overlays"),

  batch_size = 1,
  apply_cleaning = TRUE,
  apply_thinning = TRUE,
  dist_km = 5,

  export_summary = TRUE,
  store_in_memory = FALSE,
  return_all_results = FALSE
)
```

Keep rerun outputs separate from the original batch. This makes it clear which
rows were rerun and which settings were used.

# Preserving evidence and spatial audit outputs

When taxonomy preparation, origin evidence, invasive-status evidence or spatial
attribution are enabled, keep their supporting outputs with the processed
occurrence files. These tables explain how rows were interpreted before they were
filtered, exported or carried into later analyses.

A useful origin-evidence audit might look like this:

```{r origin-audit-example}
origin_audit_example <- data.frame(
  species = c(
    "Xenopus laevis",
    "Trachemys scripta",
    "Carcinus maenas",
    "Undaria pinnatifida"
  ),
  recipient_region = c(
    "FR",
    "GB",
    "GB",
    "NZ"
  ),
  evidence_source = c(
    "native_web_and_griis",
    "native_web_and_griis",
    "griis_country_and_native_web",
    "griis_country_and_native_web"
  ),
  native_range_decision = c(
    "outside_native_range",
    "outside_native_range",
    "outside_native_range",
    "outside_native_range"
  ),
  griis_decision = c(
    "listed_invasive",
    "listed_invasive",
    "listed_alien_or_invasive",
    "listed_alien_or_invasive"
  ),
  evidence_action = c(
    "retain",
    "retain",
    "retain",
    "review"
  ),
  stringsAsFactors = FALSE
)

origin_audit_example
```

Rows marked for review should remain visible in an audit table, even when they
are excluded from a final analysis dataset.

Spatial attribution can be checked in the same way. The example below records the
spatial framework used and the number of records with and without an assignment.

```{r spatial-attribution-audit-example}
spatial_audit_example <- data.frame(
  species = c(
    "Xenopus laevis",
    "Carcinus maenas",
    "Carcinus maenas",
    "Undaria pinnatifida"
  ),
  workflow = c(
    "terrestrial_gadm",
    "marine_eez",
    "marine_lme",
    "marine_ecs"
  ),
  spatial_framework = c(
    "GADM level 1",
    "Exclusive Economic Zones",
    "Large Marine Ecosystems",
    "Extended continental shelves"
  ),
  records_with_assignment = c(
    861,
    690,
    675,
    288
  ),
  records_without_assignment = c(
    0,
    12,
    27,
    13
  ),
  stringsAsFactors = FALSE
)

spatial_audit_example
```

The aim is not to duplicate every occurrence record in an audit table. It is to
keep enough information to explain which evidence and spatial settings shaped the
exported outputs.

# Scaling, memory and runtime

Scale gradually. Start with a small run, inspect the summary, open the exported
file, then increase the number of rows only after the settings behave as
expected.

A practical sequence is:

1. run one species-region or one species-overlay row;
2. inspect `gbif_summary.csv`;
3. open the exported occurrence file;
4. run a short batch with the same settings;
5. check retention rates and rows needing review;
6. add optional overlays or evidence sources only when needed;
7. split very large projects by taxon group, region, realm or overlay;
8. keep all summary and audit outputs.

The example below shows a modest second-stage terrestrial input table.

```{r second-stage-terrestrial-input}
terrestrial_test_batch <- data.frame(
  species = c(
    "Xenopus laevis",
    "Trachemys scripta",
    "Sturnus vulgaris",
    "Rattus rattus",
    "Myocastor coypus"
  ),
  iso2c = c(
    "FR",
    "GB",
    "DE",
    "GB",
    "IT"
  ),
  stringsAsFactors = FALSE
)

terrestrial_test_batch
```

For marine workflows, a second-stage test might use a short species list and one
overlay.

```{r second-stage-marine-input}
marine_test_batch <- data.frame(
  species = c(
    "Carcinus maenas",
    "Mnemiopsis leidyi",
    "Undaria pinnatifida",
    "Didemnum vexillum"
  ),
  stringsAsFactors = FALSE
)

marine_test_batch
```

For larger workflows, `store_in_memory = FALSE` is usually the safest setting.
This writes processed occurrence files to disk rather than retaining all records
in the R session. Keep `batch_size` conservative while testing, then adjust it
only after the workflow is stable.

The example below shows a project-style batch call. It is not evaluated because
paths and input tables are project-specific.

```{r project-scale-template, eval = FALSE}
project_output <- file.path(example_output_dir, "terrestrial_gadm_batch")
project_cache <- example_cache_dir

batch_result <- process_gbif_terrestrial_freshwater_pipeline(
  df = terrestrial_test_batch,

  output_dir = project_output,

  user = Sys.getenv("GBIF_USER"),
  pwd = Sys.getenv("GBIF_PWD"),
  email = Sys.getenv("GBIF_EMAIL"),

  region_source = "gadm",
  gadm_unit = 1,
  cache_dir_gadm = file.path(project_cache, "gadm"),

  batch_size = 1,

  apply_cleaning = TRUE,
  apply_thinning = TRUE,
  dist_km = 5,

  export_summary = TRUE,
  store_in_memory = FALSE,
  return_all_results = FALSE,
  quiet = FALSE
)
```

# Provider metadata and session information

`biofetchR` workflows may use GBIF occurrence data, administrative boundaries,
marine overlays, ecoregions, protected-area layers, raster layers, native-range
evidence, invasive-status evidence, WoRMS evidence, SInAS evidence and other
provider resources.

For project outputs, keep a simple metadata table recording which sources were
used and what should be saved with the results.

```{r provider-metadata-template}
provider_metadata_template <- data.frame(
  provider = c(
    "GBIF",
    "GADM",
    "Marine Regions",
    "GRIIS",
    "SInAS",
    "WoRMS"
  ),
  used_for = c(
    "Occurrence records",
    "Administrative attribution",
    "Marine overlay attribution",
    "Species-country alien or invasive status evidence",
    "Native and alien distribution evidence",
    "Marine taxonomy or distribution evidence"
  ),
  record_with_outputs = c(
    "Download DOI or download key",
    "Version or access date",
    "Product name, version or access date, and overlay name",
    "Version or access date",
    "Dataset version and access date",
    "Dataset DOI, version, or access date"
  ),
  stringsAsFactors = FALSE
)

provider_metadata_template
```

Provider metadata should be stored alongside `gbif_summary.csv`, exported
occurrence files, and any origin or spatial audit tables.

For reproducibility, record the R session information alongside final outputs.
This helps identify the R version, package versions and optional dependencies
used for the run.

```{r session-info}
sessionInfo()
```

In a project workflow, save this to a text file at the end of the run.

```{r save-session-info-template, eval = FALSE}
dir.create(example_output_dir, recursive = TRUE, showWarnings = FALSE)

writeLines(
  capture.output(sessionInfo()),
  file.path(example_output_dir, "sessionInfo.txt")
)
```

# Final checklist

Before carrying outputs forward, confirm that the run can be traced from input to
exported records.

A complete run folder should include:

- the input table used for the run;
- `gbif_summary.csv`;
- exported occurrence files for successful rows;
- any taxonomy, origin-evidence or spatial-attribution outputs produced by the
  selected settings;
- provider-cache notes or paths;
- provider citations or access details where needed;
- saved R session information.

Rows should be held back for review if they stopped early, returned no records,
lack an expected output file, have unexpectedly low retention, contain many
unmatched spatial assignments, or include unresolved evidence that affects the
analysis.

Once these checks are complete, the retained occurrence files can be combined,
summarised or passed into the next stage of the project with a clear record of
how they were produced.
