Source code for rs_metadata.profile.recommendations
"""The recommended-field check.
Separate from the mandatory-field check because the two exist for different
reasons and behave differently: a missing mandatory field is an error about
one field, while missing recommendations are a single piece of advice about
the record as a whole.
"""
from __future__ import annotations
from ..diagnostics import Severity
from ..report import Diagnostic
from .context import ProfileContext
__all__ = ["check_recommendations", "missing_recommendations"]
[docs]
def missing_recommendations(ctx: ProfileContext) -> list[str]:
"""List the recommended fields the record does not have.
Parameters
----------
ctx : ProfileContext
The record under validation.
Returns
-------
list of str
Absent recommended fields, in the profile's own order.
Examples
--------
>>> ctx = ProfileContext.for_document({"keywords": ["genomics"]})
>>> "keywords" in missing_recommendations(ctx)
False
>>> "datePublished" in missing_recommendations(ctx)
True
"""
return [name for name in ctx.policy["recommended"] if name not in ctx.document]
[docs]
def check_recommendations(ctx: ProfileContext) -> list[Diagnostic]:
"""Report absent recommended fields as one finding, not fifteen.
Parameters
----------
ctx : ProfileContext
The record under validation.
Returns
-------
list of Diagnostic
At most one warning, naming every absent field.
Notes
-----
Aggregation is deliberate. The profile recommends fifteen fields, and a
minimal-but-valid record is missing most of them; emitting one warning
each would bury the errors that actually need attention and train people
to ignore warnings altogether.
Recommendations never fail a build unless the run is configured with
``--fail-on warning``.
Examples
--------
A record missing recommendations gets exactly one finding:
>>> ctx = ProfileContext.for_document({"name": "MyTool"})
>>> findings = check_recommendations(ctx)
>>> len(findings)
1
>>> findings[0].code, findings[0].severity.value
('recommendation.missing', 'warning')
The message names them all, so the advice is actionable:
>>> "datePublished" in findings[0].message and "keywords" in findings[0].message
True
A record with every recommendation produces nothing:
>>> complete = {name: "x" for name in ctx.policy["recommended"]}
>>> check_recommendations(ProfileContext.for_document(complete))
[]
"""
missing = missing_recommendations(ctx)
if not missing:
return []
return [
ctx.diagnostic(
"recommendation.missing",
Severity.WARNING,
f"{ctx.file} omits {len(missing)} recommended "
f"{'field' if len(missing) == 1 else 'fields'}: "
f"{', '.join(missing)}.",
suggestion=(
"These are not required by the LUMC profile, but several are "
"required by registries such as the Research Software "
"Directory and bio.tools. See docs/using/profile.md for what "
"each one is for."
),
)
]