From f34f55964f36cbfd5dabc9345e1072e6cd45023f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Therese=20Natter=C3=B8y?= <61694854+tnatt@users.noreply.github.com> Date: Sun, 8 Dec 2024 18:44:56 +0100 Subject: [PATCH] ENH: Add data.product to schema --- .../definitions/0.8.0/schema/fmu_results.json | 357 ++++++++++++++++++ src/fmu/dataio/_model/data.py | 5 + src/fmu/dataio/_model/enums.py | 6 + src/fmu/dataio/_model/product.py | 71 ++++ src/fmu/dataio/_model/root.py | 1 + src/fmu/dataio/providers/objectdata/_base.py | 2 +- .../dataio/providers/objectdata/_provider.py | 2 +- 7 files changed, 442 insertions(+), 2 deletions(-) create mode 100644 src/fmu/dataio/_model/product.py diff --git a/schema/definitions/0.8.0/schema/fmu_results.json b/schema/definitions/0.8.0/schema/fmu_results.json index 0717e7d09..a824cd5ed 100644 --- a/schema/definitions/0.8.0/schema/fmu_results.json +++ b/schema/definitions/0.8.0/schema/fmu_results.json @@ -12,6 +12,7 @@ "data.is_prediction", "data.name", "data.offset", + "data.product.name", "data.seismic.attribute", "data.spec.columns", "data.stratigraphic", @@ -210,6 +211,21 @@ ], "title": "AnyData" }, + "AnyProduct": { + "description": "The ``product`` field contains information about which product this data object\nrepresent. Data that is tagged as a product is a standard result from FMU that\nconforms to a specified standard.\n\nThis class, ``AnyProduct``, acts as a container for different data products, with\nthe exact product being identified by the ``product.name`` field.", + "discriminator": { + "mapping": { + "inplace_volumes": "#/$defs/InplaceVolumesProduct" + }, + "propertyName": "name" + }, + "oneOf": [ + { + "$ref": "#/$defs/InplaceVolumesProduct" + } + ], + "title": "AnyProduct" + }, "Asset": { "description": "The ``access.asset`` block contains information about the owner asset of\nthese data.", "properties": { @@ -788,6 +804,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -1378,6 +1405,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -1683,6 +1721,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -1988,6 +2037,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -2390,6 +2450,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -2713,6 +2784,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -2948,6 +3030,27 @@ "title": "File", "type": "object" }, + "FileSchema": { + "description": "The schema identifying the format of a product.", + "properties": { + "url": { + "format": "uri", + "minLength": 1, + "title": "Url", + "type": "string" + }, + "version": { + "title": "Version", + "type": "string" + } + }, + "required": [ + "version", + "url" + ], + "title": "FileSchema", + "type": "object" + }, "FluidContact": { "description": "A block describing a fluid contact. Shall be present if ``data.content``\n== ``fluid_contact``.", "properties": { @@ -3127,6 +3230,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -3328,6 +3442,29 @@ "title": "GridModel", "type": "object" }, + "InplaceVolumesProduct": { + "description": "The ``product`` field contains information about which product this\ndata object represent.\nThis class contains metadata for the 'inplace_volumes' product.", + "properties": { + "file_schema": { + "$ref": "#/$defs/FileSchema", + "default": { + "url": "https://main-fmu-schemas-prod.radix.equinor.com/schemas/file_formats/volumes/0.1.0/inplace_volumes.json", + "version": "0.1.0" + } + }, + "name": { + "const": "inplace_volumes", + "default": "inplace_volumes", + "enum": [ + "inplace_volumes" + ], + "title": "Name", + "type": "string" + } + }, + "title": "InplaceVolumesProduct", + "type": "object" + }, "Iteration": { "description": "The ``fmu.iteration`` block contains information about the iteration this data\nobject belongs to.", "properties": { @@ -3597,6 +3734,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -3942,6 +4090,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -4300,6 +4459,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -4725,6 +4895,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -5051,6 +5232,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -5356,6 +5548,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -5708,6 +5911,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -6013,6 +6227,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -6450,6 +6675,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -6755,6 +6991,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -7148,6 +7395,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "seismic": { "$ref": "#/$defs/Seismic" }, @@ -7457,6 +7715,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -7875,6 +8144,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -8337,6 +8617,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -8671,6 +8962,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -8970,6 +9272,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -9369,6 +9682,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -9692,6 +10016,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -10019,6 +10354,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { @@ -10324,6 +10670,17 @@ "title": "Offset", "type": "number" }, + "product": { + "anyOf": [ + { + "$ref": "#/$defs/AnyProduct" + }, + { + "type": "null" + } + ], + "default": null + }, "spec": { "anyOf": [ { diff --git a/src/fmu/dataio/_model/data.py b/src/fmu/dataio/_model/data.py index 0e444ad77..118333b8a 100644 --- a/src/fmu/dataio/_model/data.py +++ b/src/fmu/dataio/_model/data.py @@ -16,6 +16,7 @@ from typing_extensions import Annotated from . import enums +from .product import AnyProduct from .specification import AnySpecification if TYPE_CHECKING: @@ -202,6 +203,10 @@ class Data(BaseModel): content: enums.Content """The type of content these data represent.""" + product: Optional[AnyProduct] = Field(default=None) + """Information about the product that these data represent. The presence of this + field indicates that these data conforms to a specified standard.""" + name: str = Field(examples=["VIKING GP. Top"]) """This is the identifying name of this data object. For surfaces, this is typically the horizon name or similar. Shall be compliant with the stratigraphic column if diff --git a/src/fmu/dataio/_model/enums.py b/src/fmu/dataio/_model/enums.py index b3a9d9754..1aecd7410 100644 --- a/src/fmu/dataio/_model/enums.py +++ b/src/fmu/dataio/_model/enums.py @@ -4,6 +4,12 @@ from typing import Type +class ProductName(str, Enum): + """The product name of a given data object.""" + + inplace_volumes = "inplace_volumes" + + class Classification(str, Enum): """The security classification for a given data object.""" diff --git a/src/fmu/dataio/_model/product.py b/src/fmu/dataio/_model/product.py new file mode 100644 index 000000000..98026f792 --- /dev/null +++ b/src/fmu/dataio/_model/product.py @@ -0,0 +1,71 @@ +from __future__ import annotations + +from typing import Literal, Optional, Union + +from pydantic import ( + AnyHttpUrl, + BaseModel, + Field, + RootModel, +) +from typing_extensions import Annotated + +from fmu.dataio._products import inplace_volumes + +from . import enums + + +class FileSchema(BaseModel): + """The schema identifying the format of a product.""" + + version: str + """The version of the product schema.""" + + url: AnyHttpUrl + """The url to the product schema.""" + + +class Product(BaseModel): + """ + The ``product`` field contains information about which product this + data object represent. + """ + + name: enums.ProductName + """The identifying product name for this data object.""" + + file_schema: Optional[FileSchema] = None + """The schema identifying the format of the product.""" + + +class InplaceVolumesProduct(Product): + """ + The ``product`` field contains information about which product this + data object represent. + This class contains metadata for the 'inplace_volumes' product. + """ + + name: Literal[enums.ProductName.inplace_volumes] = enums.ProductName.inplace_volumes + """The identifying product name for the 'inplace_volumes' product.""" + + file_schema: FileSchema = FileSchema( + version=inplace_volumes.VERSION, + url=AnyHttpUrl(inplace_volumes.SCHEMA), + ) + """The schema identifying the format of the 'inplace_volumes' product.""" + + +class AnyProduct(RootModel): + """ + The ``product`` field contains information about which product this data object + represent. Data that is tagged as a product is a standard result from FMU that + conforms to a specified standard. + + This class, ``AnyProduct``, acts as a container for different data products, with + the exact product being identified by the ``product.name`` field. + """ + + root: Annotated[ + Union[InplaceVolumesProduct,], + Field(discriminator="name"), + ] diff --git a/src/fmu/dataio/_model/root.py b/src/fmu/dataio/_model/root.py index 8388fda38..8d7d1f8fe 100644 --- a/src/fmu/dataio/_model/root.py +++ b/src/fmu/dataio/_model/root.py @@ -210,6 +210,7 @@ class FmuResultsJsonSchema(GenerateJsonSchema): "data.is_prediction", "data.name", "data.offset", + "data.product.name", "data.seismic.attribute", "data.spec.columns", "data.stratigraphic", diff --git a/src/fmu/dataio/providers/objectdata/_base.py b/src/fmu/dataio/providers/objectdata/_base.py index c14501ae4..418287d4e 100644 --- a/src/fmu/dataio/providers/objectdata/_base.py +++ b/src/fmu/dataio/providers/objectdata/_base.py @@ -83,7 +83,7 @@ def __post_init__(self) -> None: metadata[usecontent] = getattr( content_model.content_incl_specific, usecontent, None ) - + metadata["product"] = None metadata["tagname"] = self.dataio.tagname metadata["format"] = self.fmt metadata["layout"] = self.layout diff --git a/src/fmu/dataio/providers/objectdata/_provider.py b/src/fmu/dataio/providers/objectdata/_provider.py index 804aeefc7..35e76aa71 100644 --- a/src/fmu/dataio/providers/objectdata/_provider.py +++ b/src/fmu/dataio/providers/objectdata/_provider.py @@ -119,7 +119,7 @@ def objectdata_provider_factory( - obj: Inferrable, dataio: ExportData, meta_existing: dict | None = None + obj: Inferrable, dataio: ExportData ) -> ObjectDataProvider: """Factory function that generates metadata for a particular data object. This function will return an instance of an object-independent (i.e., typeable) class