-
Notifications
You must be signed in to change notification settings - Fork 1
feat: consume the shared metric-value contract (kind, reference_id, presentation attrs) #32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
4ada83e
2f7bc5e
5b1c212
af9f6fe
43e8db3
b7faa0a
445b45c
a78fd73
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -540,6 +540,12 @@ class SeriesValue(BaseModel): | |
| attributes: dict[str, Union[str, float]] | None = None | ||
| execution_group_id: int | ||
| execution_id: int | ||
| kind: Literal["model", "reference"] = "model" | ||
| reference_id: str | None = None | ||
| value_units: str | None = None | ||
| value_long_name: str | None = None | ||
| index_units: str | None = None | ||
| calendar: str | None = None | ||
|
|
||
|
|
||
| class Facet(BaseModel): | ||
|
|
@@ -554,6 +560,48 @@ class AnnotatedScalarValue: | |
| verification_status: Literal["verified", "unverified"] | None = None | ||
|
|
||
|
|
||
| def _normalize_kind(dimensions: dict[str, str]) -> Literal["model", "reference"]: | ||
| """ | ||
| Normalise the ``kind`` CV dimension to the model/reference role. | ||
|
|
||
| ``kind`` is absent from ``dimensions`` for model rows (the committed | ||
| default is omitted at serialisation), so a missing or empty value is | ||
| treated as ``"model"``. | ||
| """ | ||
| kind = dimensions.get("kind") | ||
| return "reference" if kind == "reference" else "model" | ||
|
Comment on lines
+563
to
+572
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Normalise The new helper maps missing/empty Also applies to: 639-639, 678-680 |
||
|
|
||
|
|
||
| _PRESENTATION_ATTRIBUTE_FALLBACKS: dict[str, tuple[str, ...]] = { | ||
| "value_units": ("value_units", "units"), | ||
| "value_long_name": ("value_long_name", "long_name"), | ||
| "index_units": ("index_units",), | ||
| "calendar": ("calendar",), | ||
| } | ||
|
|
||
|
|
||
| def _normalize_presentation_attributes( | ||
| attributes: dict[str, Union[str, float]] | None, | ||
| ) -> dict[str, str | None]: | ||
| """ | ||
| Normalise provider-specific presentation attribute keys to the shared names. | ||
|
|
||
| Providers disagree on attribute keys (ESMValTool already uses the target | ||
| names, ILAMB uses ``units``/``long_name``), so the first present key in | ||
| each fallback chain wins; a series with no matching key surfaces ``None``. | ||
| """ | ||
| attributes = attributes or {} | ||
| normalized: dict[str, str | None] = {} | ||
| for target, fallback_keys in _PRESENTATION_ATTRIBUTE_FALLBACKS.items(): | ||
| value: str | None = None | ||
| for key in fallback_keys: | ||
| if key in attributes and attributes[key] is not None: | ||
| value = str(attributes[key]) | ||
| break | ||
| normalized[target] = value | ||
| return normalized | ||
|
|
||
|
|
||
| class MetricValueCollection(BaseModel): | ||
| data: Sequence[ScalarValue | SeriesValue] | ||
| count: int | ||
|
|
@@ -588,6 +636,7 @@ def build_scalar( | |
| execution_id=v.execution_id, | ||
| is_outlier=item.is_outlier, | ||
| verification_status=item.verification_status, | ||
| kind=_normalize_kind(v.dimensions), | ||
| ) | ||
| ) | ||
|
|
||
|
|
@@ -615,6 +664,7 @@ def build_series( | |
| all_data: list[ScalarValue | SeriesValue] = [] | ||
|
|
||
| for series in series_values: | ||
| presentation = _normalize_presentation_attributes(series.attributes) | ||
| all_data.append( | ||
| SeriesValue( | ||
| id=series.id, | ||
|
|
@@ -625,6 +675,9 @@ def build_series( | |
| index_name=series.index_name, | ||
| execution_group_id=series.execution.execution_group_id, | ||
| execution_id=series.execution_id, | ||
| kind=_normalize_kind(series.dimensions), | ||
| reference_id=series.reference_id, | ||
| **presentation, | ||
| ) | ||
| ) | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: Climate-REF/ref-app
Length of output: 2336
🏁 Script executed:
sed -n '1,120p' backend/pyproject.tomlRepository: Climate-REF/ref-app
Length of output: 2739
Pin
climate-refin the published dependency metadata too.[tool.uv.sources]only affectsuvresolution;project.dependenciesstill advertisesclimate-ref[aft-providers,postgres]>=0.13.1, sopip/wheel/sdist installs can resolve a PyPI release that may not include the new contract this backend expects.