
Write the reviewed semantic closure for a Salmon Data Package
Source:R/semantic-closure.R
write_sdp_semantic_closure.RdProduces the two files write_eml_from_sdp() and publish_sdp_to_knb()
require and neither writes: metadata/semantic_vocabulary.csv, one evidence
row per canonical measurement IRI, and reviewed_semantic_selections.csv,
exactly one accepted row per canonical review target. Both row sets are
derived from the package on disk, each vocabulary row's
reviewed_snapshot_sha256 is computed, and the two file digests in
metadata/eml-mapping.yml are rewritten when that sidecar exists – so no
digest is ever hand-written.
Usage
write_sdp_semantic_closure(
path,
evidence = NULL,
search_fn = find_terms,
sources = c("smn", "gcdfo"),
quiet = FALSE
)Arguments
- path
Path to an existing Salmon Data Package directory.
- evidence
Optional data frame of hand-supplied closure rows, one per IRI, with an
iricolumn and any oflabel,definition,source,ontology,resource_kind,type_iris,native_type,source_url,source_artifact_sha256,confidence,review_rationale. Supplied non-empty values win over resolved ones field by field, so a row may correct one field and leave the rest to the search. An optionaltarget_sdp_fieldcolumn narrows a row to one slot, for an IRI selected in more than one. When a row supplies every required vocabulary field, no search runs for that IRI at all.- search_fn
Function used to search terms. Defaults to
find_terms(). A test hook; the signature isfunction(query, role, sources).- sources
Vocabulary sources to search. Defaults to
c("smn", "gcdfo"), the two this package resolves deterministically.- quiet
Suppress the progress and summary messages. Warnings about gaps and placeholder rationales are not suppressed.
Value
Invisibly, a list with vocabulary and review (the two tibbles as
written), gaps (IRIs absent from every searched source, in term-gap
shape), incomplete (IRIs that were found but whose evidence is short of a
required field, which is not a gap), measurement_iris and review_targets
(the two canonical sets), placeholders (ledger rows that got a
REVIEW REQUIRED: rationale), and files (the paths written).
The two canonical sets are not one set
The vocabulary describes IRIs the EML measurement and method paths emit: the
six semantic fields of every measurement dictionary row, a table-level
method_iri, and any sosa:usedProcedure reached through a code value. The
ledger describes slots a reviewer decided: the same six dictionary fields
plus a table's observation_unit_iri and method_iri. So an
observation_unit_iri is a review target and not a vocabulary term, and a
code-resolved procedure is a vocabulary term and not a review target.
Neither set can be reasoned from the other, which is why both are derived
here.
Unresolvable IRIs become gaps, not errors
Evidence is resolved by re-running the package's own deterministic search
(find_terms()) for each IRI and keeping the hit whose IRI matches. An IRI
every searched source answered about and none of them has is reported in
gaps – the shape detect_semantic_term_gaps() returns, plus an
unresolved_iri column – and both files are still written without it. A
code-resolved procedure has one gap row per codes.csv row carrying its IRI,
with that row's keys and term_iri address. Rendering these gaps produces
one candidate term request per carrying code row; review them before filing
so several uses of one IRI do not become duplicate ontology issues. That
is deliberate: an IRI absent from every searched vocabulary is an ontology
gap to file through render_ontology_term_request() and
submit_term_request_issues(), not a reason to leave the user with no files
at all. The omission is not silent: attempting EML export then names the same
IRI as missing from the vocabulary.
What is not a gap
Two other outcomes leave a vocabulary row unwritten and neither is an
ontology gap, because in neither case is the term known to be absent.
Reporting them as gaps would ask render_ontology_term_request() to mint a
term that exists.
A lookup that did not answer. If a search throws, or
find_terms()returns empty while its"diagnostics"attribute records a source that failed, nothing has been learned about that IRI. The call aborts and writes nothing, naming each IRI and the sources that did not answer; the remedy is to re-run, so leaving a half-derived closure on disk would make the retry start from worse state.A term found with incomplete evidence. An exact IRI hit can still carry a blank required field – a class with no definition is the ordinary case. The row is reported in
incomplete, naming the missing field and the slot, with a warning; the other rows are still written. Supply the field throughevidence, or annotate the term upstream.
Hand-supplied evidence
find_terms() fills label, definition, source, ontology,
resource_kind and type_iris for smn and gcdfo. It cannot fill
native_type or source_url, which describe the ontology artifact rather
than the term – both are derived here from the resolved source and can be
overridden – and it cannot search QUDT at all, so a QUDT row is entirely
hand-authored. confidence and review_rationale are human judgements: they
are read from semantic_suggestions.csv where apply_sdp_semantics()
recorded them, and otherwise written as a REVIEW REQUIRED: placeholder with
a warning naming every target that got one. A placeholder rationale satisfies
the ledger's non-empty check, so replace it before publication; the warning
and the marker are the only signals, deliberately, because judging a
rationale is not a validator's job.
Examples
if (FALSE) { # \dontrun{
# Everything the deterministic search can resolve, resolved.
closure <- write_sdp_semantic_closure("path/to/sdp")
# QUDT is not a searchable source, so a unit row is hand-authored.
closure <- write_sdp_semantic_closure(
"path/to/sdp",
evidence = tibble::tibble(
iri = "https://qudt.org/vocab/unit/INDIV",
label = "Individual",
definition = "A counting unit denoting one organism.",
source = "qudt",
ontology = "qudt",
resource_kind = "Unit",
type_iris = "http://qudt.org/schema/qudt/Unit",
native_type = "qudt:Unit",
source_url = "https://qudt.org/vocab/unit/",
confidence = "high",
review_rationale = "Values are whole counts of organisms."
)
)
# Anything the search could not resolve is a term request, not a failure.
if (nrow(closure$gaps) > 0) {
requests <- render_ontology_term_request(closure$gaps, ask = FALSE)
}
} # }