From aaa3f8cfa384748e9b5331e16a3ed7a70fd45898 Mon Sep 17 00:00:00 2001 From: David Huard Date: Wed, 15 Nov 2023 11:04:44 -0500 Subject: [PATCH 1/6] Fix bugs in datacube extension. Add test. Add schema for local validation. --- .../implementations/CMIP6_UofT/extensions.py | 34 +- tests/schemas/datacube.json | 516 ++++++++++++++++++ tests/test_cmip6_datacube.py | 33 ++ 3 files changed, 571 insertions(+), 12 deletions(-) create mode 100644 tests/schemas/datacube.json create mode 100644 tests/test_cmip6_datacube.py diff --git a/STACpopulator/implementations/CMIP6_UofT/extensions.py b/STACpopulator/implementations/CMIP6_UofT/extensions.py index 31450a6..cc49f0e 100644 --- a/STACpopulator/implementations/CMIP6_UofT/extensions.py +++ b/STACpopulator/implementations/CMIP6_UofT/extensions.py @@ -8,7 +8,7 @@ class DataCubeHelper: """Return STAC Item from CF JSON metadata, as provided by `xncml.Dataset.to_cf_dict`.""" - axis = {"X": "x", "Y": "y", "Z": "z", "T": "t", "longitude": "x", "latitude": "y", "vertical": "z", "time": "t"} + axis = {"X": "x", "Y": "y", "Z": "z", "T": None, "longitude": "x", "latitude": "y", "vertical": "z", "time": "t"} def __init__(self, attrs: dict): """ @@ -145,6 +145,7 @@ def __init__(self, attrs: dict): def dimensions(self) -> dict: """Return Dimension objects required for Datacube extension.""" + dims = {} for name, length in self.attrs["dimensions"].items(): v = self.attrs["variables"].get(name) @@ -163,17 +164,20 @@ def dimensions(self) -> dict: extent = bbox[0], bbox[2] elif key == "Y": extent = bbox[1], bbox[3] + elif key in ["T", "time"]: + extent = self.temporal_extent() else: - extent = None - - dims[name] = Dimension( - properties=dict( - axis=axis, - type=type_, - extent=extent, - description=v.get("description", v.get("long_name", criteria["standard_name"])), - ) + extent = ["", ""] + + properties = dict( + type=type_, + extent=extent, + description=v.get("description", v.get("long_name", criteria["standard_name"][0])) or "", ) + if type_ == DimensionType.SPATIAL: + properties["axis"] = axis + + dims[name] = Dimension(properties=properties) return dims @@ -192,8 +196,8 @@ def variables(self) -> dict: properties=dict( dimensions=meta["shape"], type=VariableType.AUXILIARY.value if self.is_coordinate(attrs) else VariableType.DATA.value, - description=attrs.get("description", attrs.get("long_name")), - unit=attrs.get("units", None), + description=attrs.get("description", attrs.get("long_name", "")), + unit=attrs.get("units", ""), ) ) return variables @@ -207,3 +211,9 @@ def is_coordinate(self, attrs: dict) -> bool: if attrs.get(criterion, None) in expected: return True return False + + def temporal_extent(self): + cfmeta = self.attrs["groups"]["CFMetadata"]["attributes"] + start_datetime = cfmeta["time_coverage_start"] + end_datetime = cfmeta["time_coverage_end"] + return [start_datetime, end_datetime] diff --git a/tests/schemas/datacube.json b/tests/schemas/datacube.json new file mode 100644 index 0000000..1558378 --- /dev/null +++ b/tests/schemas/datacube.json @@ -0,0 +1,516 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://stac-extensions.github.io/datacube/v2.0.0/schema.json", + "title": "Datacube Extension", + "description": "Datacube Extension for STAC Items and STAC Collections.", + "oneOf": [ + { + "$comment": "This is the schema for STAC Items. Remove this object if this extension only applies to Collections.", + "allOf": [ + { + "$ref": "#/definitions/stac_extensions" + }, + { + "type": "object", + "required": [ + "type", + "properties", + "assets" + ], + "properties": { + "type": { + "const": "Feature" + }, + "properties": { + "allOf": [ + { + "$comment": "Require fields here for Item Properties.", + "required": [ + "cube:dimensions" + ] + }, + { + "$ref": "#/definitions/fields" + } + ] + }, + "assets": { + "$comment": "This validates the fields in Item Assets, but does not require them.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/fields" + } + } + } + } + ] + }, + { + "$comment": "This is the schema for STAC Collections.", + "type": "object", + "allOf": [ + { + "required": [ + "type" + ], + "properties": { + "type": { + "const": "Collection" + } + } + }, + { + "$ref": "#/definitions/stac_extensions" + } + ], + "anyOf": [ + { + "$comment": "This is the schema for the top-level fields in a Collection. Remove this if this extension does not define top-level fields for Collections.", + "allOf": [ + { + "$comment": "Require fields here for Collections (top-level).", + "required": [ + "cube:dimensions" + ] + }, + { + "$ref": "#/definitions/fields" + } + ] + }, + { + "$comment": "This validates the fields in Collection Assets, but does not require them.", + "required": [ + "assets" + ], + "properties": { + "assets": { + "type": "object", + "not": { + "additionalProperties": { + "not": { + "allOf": [ + { + "$ref": "#/definitions/require_any_field" + }, + { + "$ref": "#/definitions/fields" + } + ] + } + } + } + } + } + }, + { + "$comment": "This is the schema for the fields in Item Asset Definitions. It doesn't require any fields.", + "required": [ + "item_assets" + ], + "properties": { + "item_assets": { + "type": "object", + "not": { + "additionalProperties": { + "not": { + "allOf": [ + { + "$ref": "#/definitions/require_any_field" + }, + { + "$ref": "#/definitions/fields" + } + ] + } + } + } + } + } + }, + { + "$comment": "This is the schema for the fields in Summaries. By default, only checks the existance of the properties, but not the schema of the summaries.", + "required": [ + "summaries" + ], + "properties": { + "summaries": { + "$ref": "#/definitions/require_any_field" + } + } + } + ] + } + ], + "definitions": { + "stac_extensions": { + "type": "object", + "required": [ + "stac_extensions" + ], + "properties": { + "stac_extensions": { + "type": "array", + "contains": { + "const": "https://stac-extensions.github.io/datacube/v2.0.0/schema.json" + } + } + } + }, + "require_any_field": { + "$comment": "Please list all fields here so that we can force the existance of one of them in other parts of the schemas.", + "anyOf": [ + {"required": ["cube:dimensions"]}, + {"required": ["cube:variables"]} + ] + }, + "fields": { + "$comment": "Add your new fields here. Don't require them here, do that above in the corresponding schema.", + "type": "object", + "properties": { + "cube:dimensions": { + "$ref": "#/definitions/cube:dimensions" + }, + "cube:variables": { + "$ref": "#/definitions/cube:variables" + } + }, + "patternProperties": { + "^(?!cube:)": {} + }, + "additionalProperties": false + }, + "cube:dimensions": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "$ref": "#/definitions/additional_dimension" + }, + { + "$ref": "#/definitions/horizontal_spatial_dimension" + }, + { + "$ref": "#/definitions/vertical_spatial_dimension" + }, + { + "$ref": "#/definitions/temporal_dimension" + } + ] + } + }, + "cube:variables": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/variable" + } + }, + "additional_dimension": { + "title": "Additional Dimension Object", + "type": "object", + "anyOf": [ + { + "required": [ + "type", + "extent" + ] + }, + { + "required": [ + "type", + "values" + ] + } + ], + "not": { + "required": [ + "axis" + ] + }, + "properties": { + "type": { + "allOf": [ + { + "type": "string" + }, + { + "not": { + "type": "string", + "const": "spatial" + } + } + ] + }, + "description": { + "$ref": "#/definitions/description" + }, + "extent": { + "$ref": "#/definitions/extent_open" + }, + "values": { + "$ref": "#/definitions/values" + }, + "step": { + "$ref": "#/definitions/step" + }, + "unit": { + "$ref": "#/definitions/unit" + }, + "reference_system": { + "type": "string" + }, + "dimensions": { + "type": "array", + "items": { + "type": [ + "string" + ] + } + } + } + }, + "horizontal_spatial_dimension": { + "title": "Horizontal Spatial Dimension Object", + "type": "object", + "required": [ + "type", + "axis", + "extent" + ], + "properties": { + "type": { + "$ref": "#/definitions/type_spatial" + }, + "axis": { + "$ref": "#/definitions/axis_xy" + }, + "description": { + "$ref": "#/definitions/description" + }, + "extent": { + "$ref": "#/definitions/extent_closed" + }, + "values": { + "$ref": "#/definitions/values_numeric" + }, + "step": { + "$ref": "#/definitions/step" + }, + "reference_system": { + "$ref": "#/definitions/reference_system_spatial" + } + } + }, + "vertical_spatial_dimension": { + "title": "Vertical Spatial Dimension Object", + "type": "object", + "anyOf": [ + { + "required": [ + "type", + "axis", + "extent" + ] + }, + { + "required": [ + "type", + "axis", + "values" + ] + } + ], + "properties": { + "type": { + "$ref": "#/definitions/type_spatial" + }, + "axis": { + "$ref": "#/definitions/axis_z" + }, + "description": { + "$ref": "#/definitions/description" + }, + "extent": { + "$ref": "#/definitions/extent_open" + }, + "values": { + "$ref": "#/definitions/values" + }, + "step": { + "$ref": "#/definitions/step" + }, + "unit": { + "$ref": "#/definitions/unit" + }, + "reference_system": { + "$ref": "#/definitions/reference_system_spatial" + } + } + }, + "temporal_dimension": { + "title": "Temporal Dimension Object", + "type": "object", + "required": [ + "type", + "extent" + ], + "not": { + "required": [ + "axis" + ] + }, + "properties": { + "type": { + "type": "string", + "const": "temporal" + }, + "description": { + "$ref": "#/definitions/description" + }, + "values": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "extent": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": [ + "string", + "null" + ] + } + }, + "step": { + "type": [ + "string", + "null" + ] + } + } + }, + "variable": { + "title": "Variable Object", + "type": "object", + "required": [ + "dimensions" + ], + "properties": { + "variable_type": { + "type": "string", + "enum": [ + "data", + "auxiliary" + ] + }, + "description": { + "$ref": "#/definitions/description" + }, + "dimensions": { + "type": "array", + "items": { + "type": "string" + } + }, + "values": { + "type": "array", + "minItems": 1 + }, + "extent": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": [ + "string", + "number", + "null" + ] + } + }, + "unit": { + "$ref": "#/definitions/unit" + } + } + }, + "type_spatial": { + "type": "string", + "const": "spatial" + }, + "axis_xy": { + "type": "string", + "enum": [ + "x", + "y" + ] + }, + "axis_z": { + "type": "string", + "const": "z" + }, + "extent_closed": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "number" + } + }, + "extent_open": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": [ + "number", + "null" + ] + } + }, + "values_numeric": { + "type": "array", + "minItems": 1, + "items": { + "type": "number" + } + }, + "values": { + "type": "array", + "minItems": 1, + "items": { + "oneOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + } + }, + "step": { + "type": [ + "number", + "null" + ] + }, + "unit": { + "type": "string" + }, + "reference_system_spatial": { + "type": [ + "string", + "number", + "object" + ], + "default": 4326 + }, + "description": { + "type": "string" + } + } +} diff --git a/tests/test_cmip6_datacube.py b/tests/test_cmip6_datacube.py new file mode 100644 index 0000000..8b7b298 --- /dev/null +++ b/tests/test_cmip6_datacube.py @@ -0,0 +1,33 @@ +import xncml +from pathlib import Path +from STACpopulator.implementations.CMIP6_UofT.extensions import DataCubeHelper +from pystac.extensions.datacube import DatacubeExtension +from STACpopulator.stac_utils import STAC_item_from_metadata +from STACpopulator.implementations.CMIP6_UofT.add_CMIP6 import CMIP6ItemProperties +from STACpopulator.models import GeoJSONPolygon +import json +from pystac import Item +from pystac.validation.stac_validator import JsonSchemaSTACValidator + +DIR = Path(__file__).parent + + +def test_datacube_helper(): + # Create item + ds = xncml.Dataset(filepath=DIR / "data" / "o3_Amon_GFDL-ESM4_historical_r1i1p1f1_gr1_185001-194912.xml") + attrs = ds.to_cf_dict() + attrs["access_urls"] = {"HTTPServer": "http://example.com"} + item = STAC_item_from_metadata("test", attrs, CMIP6ItemProperties, GeoJSONPolygon) + + # Add extension + dc = DataCubeHelper(attrs) + dc_ext = DatacubeExtension.ext(item, add_if_missing=True) + dc_ext.apply(dimensions=dc.dimensions, variables=dc.variables) + + # Validate + schema_uri = DIR / "schemas" / "datacube.json" + val = JsonSchemaSTACValidator() + val.validate_extension(stac_dict=item.to_dict(), + stac_object_type=Item, + stac_version="1.0", + extension_id=schema_uri) From fefdcab5cc90d2ddf07f7ffc59a8abade1f3be9a Mon Sep 17 00:00:00 2001 From: David Huard Date: Wed, 15 Nov 2023 11:07:02 -0500 Subject: [PATCH 2/6] Update CHANGES --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 0b6b22d..0464ee5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ ## [Unreleased](https://github.com/crim-ca/stac-populator) (latest) +* Fix datacube extension creation to match schema. ## [0.2.0](https://github.com/crim-ca/stac-populator/tree/0.2.0) (2023-11-10) From a56dbefe2c9eb7c0cfe77ce7ca5e5d015c4a7a90 Mon Sep 17 00:00:00 2001 From: David Huard Date: Wed, 15 Nov 2023 11:17:54 -0500 Subject: [PATCH 3/6] add jsonschema to dev requirements --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index ac0db2e..9250ecc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,6 +78,7 @@ dev = [ "pytest-cov", "coverage", "bump-my-version", + "jsonschema", ] [tool.pytest.ini_options] From 93af1ac111e56070d87c7a241488724b8dfbaf68 Mon Sep 17 00:00:00 2001 From: David Huard Date: Wed, 15 Nov 2023 13:44:01 -0500 Subject: [PATCH 4/6] Updated datacube to schema 2.2 --- .../implementations/CMIP6_UofT/extensions.py | 2 +- tests/schemas/datacube.json | 194 ++++++++++++++---- tests/test_cmip6_datacube.py | 3 +- 3 files changed, 153 insertions(+), 46 deletions(-) diff --git a/STACpopulator/implementations/CMIP6_UofT/extensions.py b/STACpopulator/implementations/CMIP6_UofT/extensions.py index cc49f0e..8027cf9 100644 --- a/STACpopulator/implementations/CMIP6_UofT/extensions.py +++ b/STACpopulator/implementations/CMIP6_UofT/extensions.py @@ -170,7 +170,7 @@ def dimensions(self) -> dict: extent = ["", ""] properties = dict( - type=type_, + type=type_.value, extent=extent, description=v.get("description", v.get("long_name", criteria["standard_name"][0])) or "", ) diff --git a/tests/schemas/datacube.json b/tests/schemas/datacube.json index 1558378..c1bdc89 100644 --- a/tests/schemas/datacube.json +++ b/tests/schemas/datacube.json @@ -1,44 +1,67 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://stac-extensions.github.io/datacube/v2.0.0/schema.json", + "$id": "https://stac-extensions.github.io/datacube/v2.2.0/schema.json", "title": "Datacube Extension", - "description": "Datacube Extension for STAC Items and STAC Collections.", + "description": "STAC Datacube Extension for STAC Items and STAC Collections.", "oneOf": [ { - "$comment": "This is the schema for STAC Items. Remove this object if this extension only applies to Collections.", + "$comment": "This is the schema for STAC Items.", "allOf": [ - { - "$ref": "#/definitions/stac_extensions" - }, { "type": "object", "required": [ - "type", - "properties", - "assets" + "type" ], "properties": { "type": { "const": "Feature" - }, + } + } + }, + { + "$ref": "#/definitions/stac_extensions" + } + ], + "anyOf": [ + { + "type": "object", + "required": [ + "properties" + ], + "properties": { "properties": { "allOf": [ { - "$comment": "Require fields here for Item Properties.", - "required": [ - "cube:dimensions" - ] + "$ref": "#/definitions/require_field" }, { "$ref": "#/definitions/fields" } ] - }, + } + } + }, + { + "$comment": "This validates the fields in Item Assets.", + "required": [ + "assets" + ], + "properties": { "assets": { - "$comment": "This validates the fields in Item Assets, but does not require them.", "type": "object", - "additionalProperties": { - "$ref": "#/definitions/fields" + "not": { + "additionalProperties": { + "not": { + "allOf": [ + { + "$ref": "#/definitions/require_field" + }, + { + "$ref": "#/definitions/fields" + } + ] + } + } } } } @@ -65,13 +88,10 @@ ], "anyOf": [ { - "$comment": "This is the schema for the top-level fields in a Collection. Remove this if this extension does not define top-level fields for Collections.", + "$comment": "This is the schema for the top-level fields in a Collection.", "allOf": [ { - "$comment": "Require fields here for Collections (top-level).", - "required": [ - "cube:dimensions" - ] + "$ref": "#/definitions/require_field" }, { "$ref": "#/definitions/fields" @@ -79,7 +99,7 @@ ] }, { - "$comment": "This validates the fields in Collection Assets, but does not require them.", + "$comment": "This validates the fields in Collection Assets.", "required": [ "assets" ], @@ -91,7 +111,7 @@ "not": { "allOf": [ { - "$ref": "#/definitions/require_any_field" + "$ref": "#/definitions/require_field" }, { "$ref": "#/definitions/fields" @@ -104,7 +124,7 @@ } }, { - "$comment": "This is the schema for the fields in Item Asset Definitions. It doesn't require any fields.", + "$comment": "This is the schema for the fields in Item Asset Definitions.", "required": [ "item_assets" ], @@ -152,7 +172,7 @@ "stac_extensions": { "type": "array", "contains": { - "const": "https://stac-extensions.github.io/datacube/v2.0.0/schema.json" + "const": "https://stac-extensions.github.io/datacube/v2.2.0/schema.json" } } } @@ -164,6 +184,11 @@ {"required": ["cube:variables"]} ] }, + "require_field": { + "required": [ + "cube:dimensions" + ] + }, "fields": { "$comment": "Add your new fields here. Don't require them here, do that above in the corresponding schema.", "type": "object", @@ -185,7 +210,7 @@ "additionalProperties": { "anyOf": [ { - "$ref": "#/definitions/additional_dimension" + "$ref": "#/definitions/vector_dimension" }, { "$ref": "#/definitions/horizontal_spatial_dimension" @@ -195,6 +220,9 @@ }, { "$ref": "#/definitions/temporal_dimension" + }, + { + "$ref": "#/definitions/additional_dimension" } ] } @@ -229,17 +257,13 @@ }, "properties": { "type": { - "allOf": [ - { - "type": "string" - }, - { - "not": { - "type": "string", - "const": "spatial" - } - } - ] + "type": "string", + "not": { + "enum": [ + "spatial", + "geometry" + ] + } }, "description": { "$ref": "#/definitions/description" @@ -270,7 +294,7 @@ } }, "horizontal_spatial_dimension": { - "title": "Horizontal Spatial Dimension Object", + "title": "Horizontal Spatial Raster Dimension Object", "type": "object", "required": [ "type", @@ -347,6 +371,79 @@ } } }, + "vector_dimension": { + "title": "Spatial Vector Dimension Object", + "type": "object", + "required": [ + "type", + "bbox" + ], + "properties": { + "type": { + "type": "string", + "const": "geometry" + }, + "axes": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "x", + "y", + "z" + ] + } + }, + "description": { + "$ref": "#/definitions/description" + }, + "bbox": { + "title": "Spatial extent", + "type": "array", + "oneOf": [ + { + "minItems":4, + "maxItems":4 + }, + { + "minItems":6, + "maxItems":6 + } + ], + "items": { + "type": "number" + } + }, + "values": { + "type": "array", + "minItems": 1, + "items": { + "description": "WKT or Identifier", + "type": "string" + } + }, + "geometry_types": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "Point", + "MultiPoint", + "LineString", + "MultiLineString", + "Polygon", + "MultiPolygon", + "GeometryCollection" + ] + } + }, + "reference_system": { + "$ref": "#/definitions/reference_system_spatial" + } + } + }, "temporal_dimension": { "title": "Temporal Dimension Object", "type": "object", @@ -502,10 +599,19 @@ "type": "string" }, "reference_system_spatial": { - "type": [ - "string", - "number", - "object" + "oneOf": [ + { + "description": "WKT2", + "type": "string" + }, + { + "description": "EPSG code", + "type": "integer", + "minimum": 0 + }, + { + "$ref": "https://proj.org/schemas/v0.4/projjson.schema.json" + } ], "default": 4326 }, diff --git a/tests/test_cmip6_datacube.py b/tests/test_cmip6_datacube.py index 8b7b298..4b8dede 100644 --- a/tests/test_cmip6_datacube.py +++ b/tests/test_cmip6_datacube.py @@ -5,7 +5,6 @@ from STACpopulator.stac_utils import STAC_item_from_metadata from STACpopulator.implementations.CMIP6_UofT.add_CMIP6 import CMIP6ItemProperties from STACpopulator.models import GeoJSONPolygon -import json from pystac import Item from pystac.validation.stac_validator import JsonSchemaSTACValidator @@ -27,7 +26,9 @@ def test_datacube_helper(): # Validate schema_uri = DIR / "schemas" / "datacube.json" val = JsonSchemaSTACValidator() + val.validate_extension(stac_dict=item.to_dict(), stac_object_type=Item, stac_version="1.0", extension_id=schema_uri) + From ec638005dbef49cd1c67f1cd21ef00920c56bc03 Mon Sep 17 00:00:00 2001 From: David Huard Date: Thu, 16 Nov 2023 09:28:28 -0500 Subject: [PATCH 5/6] Specify extension version used in validation. --- tests/schemas/{datacube.json => datacube/v2.2.0.json} | 0 tests/test_cmip6_datacube.py | 10 ++++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) rename tests/schemas/{datacube.json => datacube/v2.2.0.json} (100%) diff --git a/tests/schemas/datacube.json b/tests/schemas/datacube/v2.2.0.json similarity index 100% rename from tests/schemas/datacube.json rename to tests/schemas/datacube/v2.2.0.json diff --git a/tests/test_cmip6_datacube.py b/tests/test_cmip6_datacube.py index 4b8dede..48fbe14 100644 --- a/tests/test_cmip6_datacube.py +++ b/tests/test_cmip6_datacube.py @@ -23,10 +23,16 @@ def test_datacube_helper(): dc_ext = DatacubeExtension.ext(item, add_if_missing=True) dc_ext.apply(dimensions=dc.dimensions, variables=dc.variables) + # Get schema for version of datacube extension used + ext_version = item.stac_extensions[0].split("/")[-2] + + # Try to find the schema locally, otherwise it will be fetched from the net. + schema_uri = DIR / "schemas" / "datacube" / f"{ext_version}.json" + if not schema_uri.exists(): + schema_uri = item.stac_extensions[0] + # Validate - schema_uri = DIR / "schemas" / "datacube.json" val = JsonSchemaSTACValidator() - val.validate_extension(stac_dict=item.to_dict(), stac_object_type=Item, stac_version="1.0", From c8669c197ee29cc9a6c9b0739feea4c788b448fe Mon Sep 17 00:00:00 2001 From: David Huard Date: Thu, 16 Nov 2023 14:42:18 -0500 Subject: [PATCH 6/6] use validate method to validate both item and datacube extension --- tests/test_cmip6_datacube.py | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/tests/test_cmip6_datacube.py b/tests/test_cmip6_datacube.py index 48fbe14..0e7dd91 100644 --- a/tests/test_cmip6_datacube.py +++ b/tests/test_cmip6_datacube.py @@ -5,8 +5,6 @@ from STACpopulator.stac_utils import STAC_item_from_metadata from STACpopulator.implementations.CMIP6_UofT.add_CMIP6 import CMIP6ItemProperties from STACpopulator.models import GeoJSONPolygon -from pystac import Item -from pystac.validation.stac_validator import JsonSchemaSTACValidator DIR = Path(__file__).parent @@ -23,18 +21,7 @@ def test_datacube_helper(): dc_ext = DatacubeExtension.ext(item, add_if_missing=True) dc_ext.apply(dimensions=dc.dimensions, variables=dc.variables) - # Get schema for version of datacube extension used - ext_version = item.stac_extensions[0].split("/")[-2] - - # Try to find the schema locally, otherwise it will be fetched from the net. - schema_uri = DIR / "schemas" / "datacube" / f"{ext_version}.json" - if not schema_uri.exists(): - schema_uri = item.stac_extensions[0] - - # Validate - val = JsonSchemaSTACValidator() - val.validate_extension(stac_dict=item.to_dict(), - stac_object_type=Item, - stac_version="1.0", - extension_id=schema_uri) - + schemas = item.validate() + assert len(schemas) >= 2 + assert "item.json" in schemas[0] + assert "datacube" in schemas[1]