Before You Start
Complete the one-time Setup and Credentials guide first if you want to make sure GitHub installs work cleanly and any optional LLM provider is configured before you begin.
Installation
install.packages("remotes")
remotes::install_github("salmon-data-mobilization/metasalmon")One-shot Workflow
Load the built-in Fraser Coho 2023-2024 example (173 rows) and create a review-ready Salmon Data Package in one call.
library(metasalmon)
data_path <- system.file("extdata", "nuseds-fraser-coho-2023-2024.csv", package = "metasalmon")
fraser_coho <- readr::read_csv(data_path, show_col_types = FALSE)
pkg_path <- create_sdp(
fraser_coho,
path = "fraser-coho-2023-2024-sdp",
dataset_id = "fraser-coho-2023-2024",
table_id = "escapement",
check_updates = FALSE,
overwrite = TRUE
)
pkg_path
list.files(pkg_path, recursive = TRUE)If path is omitted, create_sdp() writes to
your working directory using a default folder name like
fraser-coho-2023-2024-sdp. In interactive use it can also
mention when a newer metasalmon release is available; set
check_updates = FALSE to skip that check.
This quickstart uses the bundled
nuseds-fraser-coho-2023-2024.csv example, a 173-row Fraser
coho slice derived from the official Open Government Canada Fraser and
BC Interior workbook.
Open Government Canada record: https://open.canada.ca/data/en/dataset/c48669a3-045b-400d-b730-48aafe8c5ee6
See example-data-README.md in the bundled
extdata folder for provenance and licensing. The package
also ships a matching starter dictionary at
nuseds-fraser-coho-2023-2024-column_dictionary.csv if you
want a ready-made context file for the package-native LLM review
path.
If you also need the DFO Enterprise Data Hub / GeoNetwork XML, use the one-shot path:
pkg_path <- create_sdp(
fraser_coho,
path = "fraser-coho-2023-2024-sdp",
dataset_id = "fraser-coho-2023-2024",
table_id = "escapement",
include_edh_xml = TRUE,
check_updates = FALSE,
overwrite = TRUE
)That writes the HNAP-aware EDH XML to
metadata/metadata-edh-hnap.xml. Because
create_sdp() produces review-ready metadata, this first XML
is a draft when REVIEW: or
MISSING markers remain, and the package warns accordingly.
Finalize the metadata CSVs and rebuild the submission-ready XML with
write_edh_xml_from_sdp(pkg_path). If you are working from
an already finalized dataset.csv row instead of a one-shot
package build, call edh_build_hnap_xml() directly.
Optional LLM Review Later
The base quickstart does not require an API key. If
you want llm_assess = TRUE, finish the one-time Setup
and Credentials guide first, then rerun the
create_sdp() call with your chosen provider.
When you want the LLM to use supporting README notes, CSV dictionaries, Excel workbooks, or PDF reports as context, continue with:
Review In R
The Semantic Review in R walkthrough shows the queue, decisions and all four metadata setters in more detail.
create_sdp() gives you a package that is
review-ready, not finished. Two calls take it the rest
of the way, and both print the exact call for the next step — you paste
it, and the script you end up with is the record of what you
decided.
1. Decide the semantic IRIs.
review_semantics() queues every unfilled slot with its
ranked candidates and their definitions:
review <- review_semantics(pkg_path)
review── escapement · spawner_count · variable ──────────────────────────
field: column_dictionary.csv · term_iri
current: REVIEW: https://w3id.org/smn/SpawnerAbundance
[1] Spawner Abundance smn score 4.9
The number of mature salmon returning to spawn in a stream.
https://w3id.org/smn/SpawnerAbundance
review <- accept_suggestion(review, "spawner_count", "variable", rank = 1)
Paste the line for the candidate you want, or the printed
reject_suggestion(..., reason = "…") when none fits — the
reason is written into the package, so the next reader can see why. Then
write the decisions back:
review <- accept_suggestion(review, "spawner_count", "variable", rank = 1)
apply_sdp_semantics(pkg_path, review)apply_sdp_semantics() is surgical and safe to re-run: it
strips the REVIEW: prefix from the fields you decided,
leaves the ones you did not alone, and does not touch a single byte of
your data CSVs. Decisions persist, so you can stop and pick the review
up tomorrow without being asked the same questions again.
2. Fill in the free text and anything with no
candidates. review_metadata() reads the package
against the rules that decide strict validation rather than against the
shortlist, so it sees something the semantic review structurally cannot:
a required field that no candidate was ever found
for.
review_metadata(pkg_path)── dataset.csv ────────────────────────────────────────────────────
creator: placeholder text, refused by strict validation
MISSING METADATA: add creator, team, or originating program.
set_sdp_dataset(pkg_path,
creator = "<add creator, team, or originating program>"
)
Replace each <…> with the real value and paste.
(Pasting one unedited is refused — a package whose creator
reads <add creator…> would pass validation while
saying nothing.) The four setters are set_sdp_dataset(),
set_sdp_table(), set_sdp_column() and
set_sdp_code(); each keeps datapackage.json in
step in the same write.
When review_metadata() reports nothing, strict
validation passes:
validate_salmon_datapackage(pkg_path, require_iris = TRUE)Editing metadata/*.csv in a spreadsheet still works and
is still supported. It is the fallback rather than the recommended path,
for one reason: a spreadsheet edit leaves no record of
why a term was chosen, and that record is the most
valuable thing the review produces. README-review.txt in
the package folder carries the same checklist.
The path to metadata/column_dictionary.csv is also a
reasonable llm_context_files input when you want the LLM
review step to reason from the package itself instead of a separate
methods note. Pass the path string, not a tibble created by reading the
CSV, and set llm_assess = TRUE; context alone does not
enable an LLM call.
create_sdp() seeds semantic suggestions by default and
auto-fills top-ranked compatible drafts directly into blank semantic
fields in the metadata CSVs. That includes variable, property, entity,
and unit IRIs in metadata/column_dictionary.csv plus strong
table-level observation-unit drafts in metadata/tables.csv,
using observation_unit/description when
available and otherwise falling back to
table_label/table_id. Constraint and
statistical-modifier candidates are held back unless the column’s own
name, label, or description carries the evidence for them, and under LLM
review (llm_assess = TRUE) they are never auto-filled at
all. Any auto-applied semantic IRI draft is written back as
REVIEW: <iri> so you still confirm it manually. It
does not overwrite existing non-empty semantic values. Code-level
suggestions default to factor and low-cardinality character source
columns; use semantic_code_scope = "all" if you want
broader code-level seeding.
When LLM review is enabled, the six measurement roles — variable,
property, entity, unit, constraint, and statistical modifier — are
judged together and then checked by deterministic validators. A
validator can downgrade an unsupported acceptance to manual review, but
it never substitutes or invents a term. Omit
semantic_sources for role-aware defaults; an explicitly
supplied vector is a strict allowlist through the single retry
round.
The inferred metadata includes MISSING DESCRIPTION: and
MISSING METADATA: placeholders for required fields, so the
package tells you what it is still missing.
review_metadata() lists exactly those fields and prints the
call that fills each one. Expect
validate_salmon_datapackage() to warn about every
placeholder it finds (since 0.2.6), including on a package you
created seconds ago. That warning on a fresh package is normal: it is
the to-do list, not a defect. The metadata/*.csv files are
the canonical package metadata; datapackage.json is a
derived export for interoperability.
How To Decide If term_iri Is Correct
Use plain-language checks for each measurement column:
- Does the suggested label describe exactly what the column measures?
- Does the definition match your intent (not just a similar word)?
- Is the scope right (for example species-level vs population-level)?
- Is the unit consistent with your values and
unit_iri?
Keep the IRI only when all checks pass.
Replace it when the term is close but not exact.
Remove it (leave blank) when no candidate is reliable yet.
When the top auto-applied suggestion is wrong,
review_semantics() prints the whole shortlist with each
candidate’s definition; accept a different one with the
rank = it shows. When the right term exists but retrieval
never surfaced it, pass it directly:
accept_suggestion(review, "spawner_count", "variable", iri = "…"),
or
set_sdp_column(pkg_path, "spawner_count", term_iri = "…")
for a slot with no shortlist at all.
If no candidate fits, request a new term instead of forcing a bad match:
- shared cross-organization/domain terms -> https://github.com/salmon-data-mobilization/salmon-domain-ontology/issues/new/choose
- DFO-specific policy/operations terms -> https://github.com/dfo-pacific-science/dfo-salmon-ontology/issues/new/choose
Finalize
apply_sdp_semantics() and the set_sdp_*()
setters write straight into the package, so there is nothing to save or
reload. If you did edit the CSVs in a spreadsheet, save them back as CSV
and reload:
pkg <- read_salmon_datapackage(pkg_path)
validate_salmon_datapackage(pkg_path, require_iris = FALSE)That review-state validation is the first follow-up check. For the full post-review workflow — reloading the package, detecting unresolved semantic gaps, deciding shared salmon-domain vs DFO-specific routing, drafting term requests, rebuilding EDH XML if needed, and only then running strict final validation — continue with:
For a staged, fully explicit workflow where you assemble metadata
tables manually instead of continuing from a reviewed
create_sdp() package, use:
Two more guides are worth knowing about before you package real data:
- Tidy Data for Salmon Data Packages — what the primary-key and untidy-column-name checks mean, and how to reshape a year-per-column table.
- Migrating to SDP 0.3.0 — required reading if you have a package built with metasalmon 0.2.x, since methods left the column dictionary.
