From 9230cd540164054ef346da7278e108252e118474 Mon Sep 17 00:00:00 2001 From: Michael Barlow Date: Thu, 5 Feb 2026 12:41:53 -0700 Subject: [PATCH 1/6] add initial FieldResource model --- deployments/api/src/stitch/api/sources.py | 124 ++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 deployments/api/src/stitch/api/sources.py diff --git a/deployments/api/src/stitch/api/sources.py b/deployments/api/src/stitch/api/sources.py new file mode 100644 index 0000000..35acdc0 --- /dev/null +++ b/deployments/api/src/stitch/api/sources.py @@ -0,0 +1,124 @@ +from __future__ import annotations +from pydantic import AwareDatetime, BaseModel, Field, confloat, conint, constr +from enum import Enum +from typing import Any, Literal + + +class Owner(BaseModel): + name: str = Field(..., description="Name of the company") + stake: float = Field( + ..., description="Ownership percentage (0-100)", ge=0.0, le=100 + ) + + +class Operator(BaseModel): + name: str = Field(..., description="Name of the operating company") + stake: float = Field( + ..., description="Operating stake percentage (0-100)", ge=0.0, le=100 + ) + + +class LocationType(Enum): + Onshore = "Onshore" + Offshore = "Offshore" + Unknown = "Unknown" + + +LocationType_ = Literal["Onshore", "Offshore", "Unknown"] + + +class ProductionConventionality(Enum): + Conventional = "Conventional" + Unconventional = "Unconventional" + Mixed = "Mixed" + Unknown = "Unknown" + + +class PrimaryHydrocarbonGroup(Enum): + Ultra_Light_Oil = "Ultra-Light Oil" + Light_Oil = "Light Oil" + Medium_Oil = "Medium Oil" + Heavy_Oil = "Heavy Oil" + Extra_Heavy_Oil = "Extra-Heavy Oil" + Dry_Gas = "Dry Gas" + Wet_Gas = "Wet Gas" + Acid_Gas = "Acid Gas" + Condensate = "Condensate" + Mixed = "Mixed" + Unknown = "Unknown" + + +class FieldStatus(Enum): + Producing = "Producing" + Non_Producing = "Non-Producing" + Abandoned = "Abandoned" + Planned = "Planned" + + +class RawLineage(BaseModel): + gem: dict[str, dict[str, Any]] | None = None + woodmac: dict[str, dict[str, Any]] | None = None + + +class Schema(BaseModel): + id: int = Field(..., description="Stable, unique identifier for the resource") + repointed_to: str | None = Field( + None, + description="If this resource has been repointed, the `id` of the new resource", + ) + name: str | None = Field( + ..., description="Primary name of the resource", min_length=1 + ) + country: str | None = Field( + ..., description="ISO 3166-1 alpha-3 country code", pattern=r"^[A-Z]{3}$" + ) + latitude: float | None = Field( + None, description="Latitude in WGS84 coordinate system", ge=-90.0, le=90 + ) + longitude: float | None = Field( + None, description="Longitude in WGS84 coordinate system", ge=-180.0, le=180.0 + ) + last_updated: AwareDatetime | None = Field( + None, description="ISO 8601 timestamp of most recent source data update" + ) + + +class OilAndGasFieldSource(Schema): + name_local: str | None = Field( + None, description="Name in local script if different from primary name" + ) + state_province: str | None = Field( + None, description="State or province where the resource is located" + ) + region: str | None = Field(None, description="Geographic or administrative region") + basin: str | None = Field(None, description="Geological basin name") + owners: list[Owner] | None = Field( + None, description="List of owners and their ownership stakes" + ) + operators: list[Operator] | None = Field( + None, description="List of operators and their operating stakes" + ) + location_type: LocationType | None = Field( + None, description="Whether the resource is onshore or offshore" + ) + production_conventionality: ProductionConventionality | None = Field( + None, description="Production conventionality classification" + ) + primary_hydrocarbon_group: PrimaryHydrocarbonGroup | None = Field( + None, description="Primary hydrocarbon type aligned with OGSI nomenclature" + ) + reservoir_formation: str | None = Field( + None, description="Name or description of the reservoir formation" + ) + discovery_year: int | None = Field( + None, description="Year of discovery", ge=1800, le=2100 + ) + production_start_year: int | None = Field( + None, description="Actual or planned year of first production", ge=1800, le=2100 + ) + fid_year: int | None = Field( + None, description="Year of final investment decision (FID)", ge=1800, le=2100 + ) + field_status: FieldStatus | None = Field( + None, description="Current status of the field" + ) From 28dc1cfc3c65a5d2b923720bdf90b730dc341798 Mon Sep 17 00:00:00 2001 From: Michael Barlow Date: Thu, 5 Feb 2026 19:25:26 -0700 Subject: [PATCH 2/6] initial entity extraction --- deployments/api/pyproject.toml | 3 + .../api/src/stitch/api/db/model/sources.py | 6 + deployments/api/src/stitch/api/sources.py | 124 +----------------- packages/stitch-resources/.python-version | 1 + packages/stitch-resources/README.md | 0 packages/stitch-resources/pyproject.toml | 17 +++ .../src/stitch/resources/__init__.py | 2 + .../src/stitch/resources/ogsi/__init__.py | 19 +++ .../src/stitch/resources/ogsi/models.py | 98 ++++++++++++++ .../src/stitch/resources/py.typed | 0 10 files changed, 149 insertions(+), 121 deletions(-) create mode 100644 packages/stitch-resources/.python-version create mode 100644 packages/stitch-resources/README.md create mode 100644 packages/stitch-resources/pyproject.toml create mode 100644 packages/stitch-resources/src/stitch/resources/__init__.py create mode 100644 packages/stitch-resources/src/stitch/resources/ogsi/__init__.py create mode 100644 packages/stitch-resources/src/stitch/resources/ogsi/models.py create mode 100644 packages/stitch-resources/src/stitch/resources/py.typed diff --git a/deployments/api/pyproject.toml b/deployments/api/pyproject.toml index bbf2243..89445ad 100644 --- a/deployments/api/pyproject.toml +++ b/deployments/api/pyproject.toml @@ -8,9 +8,11 @@ requires-python = ">=3.12.12" dependencies = [ "fastapi[standard]>=0.124.0", "greenlet>=3.3.0", + "pydantic>=2.12.5", "pydantic-settings>=2.12.0", "sqlalchemy>=2.0.44", "stitch-core", + "stitch-resources", ] [project.scripts] @@ -40,3 +42,4 @@ addopts = ["-v", "--strict-markers", "--tb=short"] [tool.uv.sources] stitch-core = { workspace = true } +stitch-resources = { workspace = true } diff --git a/deployments/api/src/stitch/api/db/model/sources.py b/deployments/api/src/stitch/api/db/model/sources.py index f05fc2d..083b197 100644 --- a/deployments/api/src/stitch/api/db/model/sources.py +++ b/deployments/api/src/stitch/api/db/model/sources.py @@ -19,6 +19,8 @@ CCReservoirsData, WMSource, ) +from stitch.api.sources import OilAndGasFieldSource +from stitch.resources.ogsi import OilAndGasFieldSourceData def float_constraint( @@ -74,6 +76,10 @@ def from_entity(cls, entity: TModelIn) -> "SourceBase": return cls(**filtered) +class GemSourceModel_(SourceBase[OilAndGasFieldSourceData, OilAndGasFieldSource]): + __tablename__ = "gem_field_sources" + + class GemSourceModel(SourceBase[GemData, GemSource]): __tablename__ = "gem_sources" diff --git a/deployments/api/src/stitch/api/sources.py b/deployments/api/src/stitch/api/sources.py index 35acdc0..562b2da 100644 --- a/deployments/api/src/stitch/api/sources.py +++ b/deployments/api/src/stitch/api/sources.py @@ -1,124 +1,6 @@ -from __future__ import annotations -from pydantic import AwareDatetime, BaseModel, Field, confloat, conint, constr -from enum import Enum -from typing import Any, Literal +from pydantic import Field +from stitch.resources.ogsi.models import OilAndGasFieldSourceData -class Owner(BaseModel): - name: str = Field(..., description="Name of the company") - stake: float = Field( - ..., description="Ownership percentage (0-100)", ge=0.0, le=100 - ) - - -class Operator(BaseModel): - name: str = Field(..., description="Name of the operating company") - stake: float = Field( - ..., description="Operating stake percentage (0-100)", ge=0.0, le=100 - ) - - -class LocationType(Enum): - Onshore = "Onshore" - Offshore = "Offshore" - Unknown = "Unknown" - - -LocationType_ = Literal["Onshore", "Offshore", "Unknown"] - - -class ProductionConventionality(Enum): - Conventional = "Conventional" - Unconventional = "Unconventional" - Mixed = "Mixed" - Unknown = "Unknown" - - -class PrimaryHydrocarbonGroup(Enum): - Ultra_Light_Oil = "Ultra-Light Oil" - Light_Oil = "Light Oil" - Medium_Oil = "Medium Oil" - Heavy_Oil = "Heavy Oil" - Extra_Heavy_Oil = "Extra-Heavy Oil" - Dry_Gas = "Dry Gas" - Wet_Gas = "Wet Gas" - Acid_Gas = "Acid Gas" - Condensate = "Condensate" - Mixed = "Mixed" - Unknown = "Unknown" - - -class FieldStatus(Enum): - Producing = "Producing" - Non_Producing = "Non-Producing" - Abandoned = "Abandoned" - Planned = "Planned" - - -class RawLineage(BaseModel): - gem: dict[str, dict[str, Any]] | None = None - woodmac: dict[str, dict[str, Any]] | None = None - - -class Schema(BaseModel): +class OilAndGasFieldSource(OilAndGasFieldSourceData): id: int = Field(..., description="Stable, unique identifier for the resource") - repointed_to: str | None = Field( - None, - description="If this resource has been repointed, the `id` of the new resource", - ) - name: str | None = Field( - ..., description="Primary name of the resource", min_length=1 - ) - country: str | None = Field( - ..., description="ISO 3166-1 alpha-3 country code", pattern=r"^[A-Z]{3}$" - ) - latitude: float | None = Field( - None, description="Latitude in WGS84 coordinate system", ge=-90.0, le=90 - ) - longitude: float | None = Field( - None, description="Longitude in WGS84 coordinate system", ge=-180.0, le=180.0 - ) - last_updated: AwareDatetime | None = Field( - None, description="ISO 8601 timestamp of most recent source data update" - ) - - -class OilAndGasFieldSource(Schema): - name_local: str | None = Field( - None, description="Name in local script if different from primary name" - ) - state_province: str | None = Field( - None, description="State or province where the resource is located" - ) - region: str | None = Field(None, description="Geographic or administrative region") - basin: str | None = Field(None, description="Geological basin name") - owners: list[Owner] | None = Field( - None, description="List of owners and their ownership stakes" - ) - operators: list[Operator] | None = Field( - None, description="List of operators and their operating stakes" - ) - location_type: LocationType | None = Field( - None, description="Whether the resource is onshore or offshore" - ) - production_conventionality: ProductionConventionality | None = Field( - None, description="Production conventionality classification" - ) - primary_hydrocarbon_group: PrimaryHydrocarbonGroup | None = Field( - None, description="Primary hydrocarbon type aligned with OGSI nomenclature" - ) - reservoir_formation: str | None = Field( - None, description="Name or description of the reservoir formation" - ) - discovery_year: int | None = Field( - None, description="Year of discovery", ge=1800, le=2100 - ) - production_start_year: int | None = Field( - None, description="Actual or planned year of first production", ge=1800, le=2100 - ) - fid_year: int | None = Field( - None, description="Year of final investment decision (FID)", ge=1800, le=2100 - ) - field_status: FieldStatus | None = Field( - None, description="Current status of the field" - ) diff --git a/packages/stitch-resources/.python-version b/packages/stitch-resources/.python-version new file mode 100644 index 0000000..763b626 --- /dev/null +++ b/packages/stitch-resources/.python-version @@ -0,0 +1 @@ +3.12.12 diff --git a/packages/stitch-resources/README.md b/packages/stitch-resources/README.md new file mode 100644 index 0000000..e69de29 diff --git a/packages/stitch-resources/pyproject.toml b/packages/stitch-resources/pyproject.toml new file mode 100644 index 0000000..99f305f --- /dev/null +++ b/packages/stitch-resources/pyproject.toml @@ -0,0 +1,17 @@ +[project] +name = "stitch-resources" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +authors = [{ name = "Michael Barlow", email = "mbarlow@rmi.org" }] +requires-python = ">=3.12.12" +dependencies = [ + "pydantic>=2.12.5", +] + +[build-system] +requires = ["uv_build>=0.9.30,<0.10.0"] +build-backend = "uv_build" + +[tool.uv.build-backend] +module-name = "stitch.resources" diff --git a/packages/stitch-resources/src/stitch/resources/__init__.py b/packages/stitch-resources/src/stitch/resources/__init__.py new file mode 100644 index 0000000..d843159 --- /dev/null +++ b/packages/stitch-resources/src/stitch/resources/__init__.py @@ -0,0 +1,2 @@ +def hello() -> str: + return "Hello from stitch-resources!" diff --git a/packages/stitch-resources/src/stitch/resources/ogsi/__init__.py b/packages/stitch-resources/src/stitch/resources/ogsi/__init__.py new file mode 100644 index 0000000..879ed45 --- /dev/null +++ b/packages/stitch-resources/src/stitch/resources/ogsi/__init__.py @@ -0,0 +1,19 @@ +from .models import ( + LocationType, + ProductionConventionality, + PrimaryHydrocarbonGroup, + FieldStatus, + Owner, + Operator, + OilAndGasFieldSourceData, +) + +__all__ = [ + "FieldStatus", + "LocationType", + "OilAndGasFieldSourceData", + "Operator", + "Owner", + "PrimaryHydrocarbonGroup", + "ProductionConventionality", +] diff --git a/packages/stitch-resources/src/stitch/resources/ogsi/models.py b/packages/stitch-resources/src/stitch/resources/ogsi/models.py new file mode 100644 index 0000000..00c5963 --- /dev/null +++ b/packages/stitch-resources/src/stitch/resources/ogsi/models.py @@ -0,0 +1,98 @@ +from __future__ import annotations +from pydantic import AwareDatetime, BaseModel, Field +from typing import Literal + + +LocationType = Literal["Onshore", "Offshore", "Unknown"] + + +ProductionConventionality = Literal[ + "Conventional", "Unconventional", "Mixed", "Unknown" +] + + +PrimaryHydrocarbonGroup = Literal[ + "Ultra-Light Oil", + "Light Oil", + "Medium Oil", + "Heavy Oil", + "Extra-Heavy Oil", + "Dry Gas", + "Wet Gas", + "Acid Gas", + "Condensate", + "Mixed", + "Unknown", +] + +FieldStatus = Literal["Producing", "Non-Producing", "Abandoned", "Planned"] + + +class Owner(BaseModel): + name: str = Field(..., description="Name of the company") + stake: float = Field( + ..., description="Ownership percentage (0-100)", ge=0.0, le=100 + ) + + +class Operator(BaseModel): + name: str = Field(..., description="Name of the operating company") + stake: float = Field( + ..., description="Operating stake percentage (0-100)", ge=0.0, le=100 + ) + + +class OilAndGasFieldSourceData(BaseModel): + name: str | None = Field( + ..., description="Primary name of the resource", min_length=1 + ) + country: str | None = Field( + ..., description="ISO 3166-1 alpha-3 country code", pattern=r"^[A-Z]{3}$" + ) + latitude: float | None = Field( + None, description="Latitude in WGS84 coordinate system", ge=-90.0, le=90 + ) + longitude: float | None = Field( + None, description="Longitude in WGS84 coordinate system", ge=-180.0, le=180.0 + ) + last_updated: AwareDatetime | None = Field( + None, description="ISO 8601 timestamp of most recent source data update" + ) + name_local: str | None = Field( + None, description="Name in local script if different from primary name" + ) + state_province: str | None = Field( + None, description="State or province where the resource is located" + ) + region: str | None = Field(None, description="Geographic or administrative region") + basin: str | None = Field(None, description="Geological basin name") + owners: list[Owner] | None = Field( + None, description="List of owners and their ownership stakes" + ) + operators: list[Operator] | None = Field( + None, description="List of operators and their operating stakes" + ) + location_type: LocationType | None = Field( + None, description="Whether the resource is onshore or offshore" + ) + production_conventionality: ProductionConventionality | None = Field( + None, description="Production conventionality classification" + ) + primary_hydrocarbon_group: PrimaryHydrocarbonGroup | None = Field( + None, description="Primary hydrocarbon type aligned with OGSI nomenclature" + ) + reservoir_formation: str | None = Field( + None, description="Name or description of the reservoir formation" + ) + discovery_year: int | None = Field( + None, description="Year of discovery", ge=1800, le=2100 + ) + production_start_year: int | None = Field( + None, description="Actual or planned year of first production", ge=1800, le=2100 + ) + fid_year: int | None = Field( + None, description="Year of final investment decision (FID)", ge=1800, le=2100 + ) + field_status: FieldStatus | None = Field( + None, description="Current status of the field" + ) diff --git a/packages/stitch-resources/src/stitch/resources/py.typed b/packages/stitch-resources/src/stitch/resources/py.typed new file mode 100644 index 0000000..e69de29 From 4d5f2bc856fef3868715b6dacc7f7d492d685c9f Mon Sep 17 00:00:00 2001 From: Michael Barlow Date: Thu, 5 Feb 2026 19:49:51 -0700 Subject: [PATCH 3/6] add stitch-resources as member --- pyproject.toml | 6 +++++- uv.lock | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a4d8390..83a316b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,11 @@ requires-python = ">=3.12" dependencies = ["stitch-core"] [tool.uv.workspace] -members = ["deployments/api", "packages/stitch-core"] +members = [ + "deployments/api", + "packages/stitch-core", + "packages/stitch-resources", +] [tool.uv.sources] stitch-core = { workspace = true } diff --git a/uv.lock b/uv.lock index 9c97c2b..419cc63 100644 --- a/uv.lock +++ b/uv.lock @@ -7,6 +7,7 @@ members = [ "stitch", "stitch-api", "stitch-core", + "stitch-resources", ] [[package]] @@ -971,9 +972,11 @@ source = { editable = "deployments/api" } dependencies = [ { name = "fastapi", extra = ["standard"] }, { name = "greenlet" }, + { name = "pydantic" }, { name = "pydantic-settings" }, { name = "sqlalchemy" }, { name = "stitch-core" }, + { name = "stitch-resources" }, ] [package.dev-dependencies] @@ -988,9 +991,11 @@ dev = [ requires-dist = [ { name = "fastapi", extras = ["standard"], specifier = ">=0.124.0" }, { name = "greenlet", specifier = ">=3.3.0" }, + { name = "pydantic", specifier = ">=2.12.5" }, { name = "pydantic-settings", specifier = ">=2.12.0" }, { name = "sqlalchemy", specifier = ">=2.0.44" }, { name = "stitch-core", editable = "packages/stitch-core" }, + { name = "stitch-resources", editable = "packages/stitch-resources" }, ] [package.metadata.requires-dev] @@ -1020,6 +1025,17 @@ requires-dist = [ { name = "sqlalchemy", specifier = ">=2.0.44" }, ] +[[package]] +name = "stitch-resources" +version = "0.1.0" +source = { editable = "packages/stitch-resources" } +dependencies = [ + { name = "pydantic" }, +] + +[package.metadata] +requires-dist = [{ name = "pydantic", specifier = ">=2.12.5" }] + [[package]] name = "typer" version = "0.21.1" From 15a2a8b1b61949ed2cbdd8cc186936032b782569 Mon Sep 17 00:00:00 2001 From: Michael Barlow Date: Thu, 5 Feb 2026 19:53:09 -0700 Subject: [PATCH 4/6] chore: update python version --- packages/stitch-resources/pyproject.toml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/stitch-resources/pyproject.toml b/packages/stitch-resources/pyproject.toml index 99f305f..4cf9bca 100644 --- a/packages/stitch-resources/pyproject.toml +++ b/packages/stitch-resources/pyproject.toml @@ -4,10 +4,8 @@ version = "0.1.0" description = "Add your description here" readme = "README.md" authors = [{ name = "Michael Barlow", email = "mbarlow@rmi.org" }] -requires-python = ">=3.12.12" -dependencies = [ - "pydantic>=2.12.5", -] +requires-python = ">=3.11" +dependencies = ["pydantic>=2.12.5"] [build-system] requires = ["uv_build>=0.9.30,<0.10.0"] From 324f25d4c0443e62e1f325dbd350fdf4f77109bc Mon Sep 17 00:00:00 2001 From: Michael Barlow Date: Thu, 5 Feb 2026 19:56:54 -0700 Subject: [PATCH 5/6] feat(api): unify source models to OilAndGasFieldSourceData, remove CC --- deployments/api/src/stitch/api/db/init_job.py | 36 +++---- .../api/src/stitch/api/db/model/__init__.py | 2 - .../api/src/stitch/api/db/model/resource.py | 7 -- .../api/src/stitch/api/db/model/sources.py | 100 ++++++++---------- .../api/src/stitch/api/db/resource_actions.py | 5 +- deployments/api/src/stitch/api/entities.py | 81 +++----------- deployments/api/src/stitch/api/sources.py | 3 +- deployments/api/tests/conftest.py | 56 ++-------- .../api/tests/db/test_resource_actions.py | 24 +++-- .../routers/test_resources_integration.py | 10 +- .../api/tests/routers/test_resources_unit.py | 6 +- deployments/api/tests/utils.py | 84 ++++----------- 12 files changed, 129 insertions(+), 285 deletions(-) diff --git a/deployments/api/src/stitch/api/db/init_job.py b/deployments/api/src/stitch/api/db/init_job.py index 72b9d93..40de927 100644 --- a/deployments/api/src/stitch/api/db/init_job.py +++ b/deployments/api/src/stitch/api/db/init_job.py @@ -12,7 +12,6 @@ from sqlalchemy.orm import Session from stitch.api.db.model import ( - CCReservoirsSourceModel, GemSourceModel, MembershipModel, RMIManualSourceModel, @@ -267,24 +266,25 @@ def create_seed_user() -> UserModel: def create_seed_sources(): gem_sources = [ GemSourceModel.from_entity( - GemData(name="Permian Basin Field", country="USA", lat=31.8, lon=-102.3) + GemData( + name="Permian Basin Field", + country="USA", + latitude=31.8, + longitude=-102.3, + ) ), GemSourceModel.from_entity( - GemData(name="North Sea Platform", country="GBR", lat=57.5, lon=1.5) + GemData( + name="North Sea Platform", country="GBR", latitude=57.5, longitude=1.5 + ) ), ] for i, src in enumerate(gem_sources, start=1): src.id = i wm_sources = [ - WMSourceModel.from_entity( - WMData( - field_name="Eagle Ford Shale", field_country="USA", production=125000.5 - ) - ), - WMSourceModel.from_entity( - WMData(field_name="Ghawar Field", field_country="SAU", production=500000.0) - ), + WMSourceModel.from_entity(WMData(name="Eagle Ford Shale", country="USA")), + WMSourceModel.from_entity(WMData(name="Ghawar Field", country="SAU")), ] for i, src in enumerate(wm_sources, start=1): src.id = i @@ -292,9 +292,7 @@ def create_seed_sources(): rmi_sources = [ RMIManualSourceModel.from_entity( RMIManualData( - name_override="Custom Override Name", - gwp=25.5, - gor=0.45, + name="Custom Override Name", country="CAN", latitude=56.7, longitude=-111.4, @@ -304,11 +302,7 @@ def create_seed_sources(): for i, src in enumerate(rmi_sources, start=1): src.id = i - # CC Reservoir sources are intentionally omitted from the dev seed profile; - # the CCReservoirsSourceModel table is still created from SQLAlchemy metadata. - cc_sources: list[CCReservoirsSourceModel] = [] - - return gem_sources, wm_sources, rmi_sources, cc_sources + return gem_sources, wm_sources, rmi_sources def create_seed_resources(user: UserEntity) -> list[ResourceModel]: @@ -362,8 +356,8 @@ def seed_dev(engine) -> None: name=f"{user_model.first_name} {user_model.last_name}", ) - gem_sources, wm_sources, rmi_sources, cc_sources = create_seed_sources() - session.add_all(gem_sources + wm_sources + rmi_sources + cc_sources) + gem_sources, wm_sources, rmi_sources = create_seed_sources() + session.add_all(gem_sources + wm_sources + rmi_sources) resources = create_seed_resources(user_entity) session.add_all(resources) diff --git a/deployments/api/src/stitch/api/db/model/__init__.py b/deployments/api/src/stitch/api/db/model/__init__.py index d3f74ab..31710e9 100644 --- a/deployments/api/src/stitch/api/db/model/__init__.py +++ b/deployments/api/src/stitch/api/db/model/__init__.py @@ -2,14 +2,12 @@ from .sources import ( GemSourceModel, RMIManualSourceModel, - CCReservoirsSourceModel, WMSourceModel, ) from .resource import MembershipStatus, MembershipModel, ResourceModel from .user import User as UserModel __all__ = [ - "CCReservoirsSourceModel", "GemSourceModel", "MembershipModel", "MembershipStatus", diff --git a/deployments/api/src/stitch/api/db/model/resource.py b/deployments/api/src/stitch/api/db/model/resource.py index 0b6f596..ffa7b10 100644 --- a/deployments/api/src/stitch/api/db/model/resource.py +++ b/deployments/api/src/stitch/api/db/model/resource.py @@ -119,10 +119,6 @@ def copy(self): # def rmi(self) -> list[RMIManualSourceModel]: # return self._owner._rmi_sources # -# @property -# def cc(self) -> list[CCReservoirsSourceModel]: -# return self._owner._cc_sources -# # # class SourcesDescriptor: # def __get__(self, obj: "ResourceModel | None", objtype: Any = None) -> SourceModels: @@ -158,9 +154,6 @@ class ResourceModel(TimestampMixin, UserAuditMixin, Base): # _rmi_sources: Mapped[list[RMIManualSourceModel]] = src_relationship( # model=RMIManualSourceModel, source="rmi" # ) - # _cc_sources: Mapped[list[CCReservoirsSourceModel]] = src_relationship( - # model=CCReservoirsSourceModel, source="cc" - # ) # # sources: SourceModels = SourcesDescriptor() diff --git a/deployments/api/src/stitch/api/db/model/sources.py b/deployments/api/src/stitch/api/db/model/sources.py index 083b197..c878fe6 100644 --- a/deployments/api/src/stitch/api/db/model/sources.py +++ b/deployments/api/src/stitch/api/db/model/sources.py @@ -3,21 +3,13 @@ from collections.abc import Mapping, MutableMapping from typing import Final, Generic, TypeVar, TypedDict, get_args, get_origin from pydantic import BaseModel -from sqlalchemy import CheckConstraint, inspect +from sqlalchemy import CheckConstraint, DateTime, Float, Integer, String, inspect from sqlalchemy.orm import Mapped, mapped_column from .common import Base -from .types import PORTABLE_BIGINT, StitchJson +from .types import PORTABLE_BIGINT, PORTABLE_JSON from stitch.api.entities import ( - CCReservoirsSource, - GemSource, IdType, - RMIManualSource, SourceKey, - WMData, - GemData, - RMIManualData, - CCReservoirsData, - WMSource, ) from stitch.api.sources import OilAndGasFieldSource from stitch.resources.ogsi import OilAndGasFieldSourceData @@ -40,6 +32,10 @@ def lon_constraints(colname: str): return float_constraint(colname, -180, 180) +def year_constraints(colname: str): + return float_constraint(colname, 1800, 2100) + + TModelIn = TypeVar("TModelIn", bound=BaseModel) TModelOut = TypeVar("TModelOut", bound=BaseModel) @@ -53,6 +49,41 @@ class SourceBase(Base, Generic[TModelIn, TModelOut]): PORTABLE_BIGINT, primary_key=True, autoincrement=True ) + # All OilAndGasFieldSourceData columns + name: Mapped[str | None] = mapped_column(String, nullable=True) + country: Mapped[str | None] = mapped_column(String(3), nullable=True) + latitude: Mapped[float | None] = mapped_column( + Float, lat_constraints("latitude"), nullable=True + ) + longitude: Mapped[float | None] = mapped_column( + Float, lon_constraints("longitude"), nullable=True + ) + last_updated: Mapped[str | None] = mapped_column( + DateTime(timezone=True), nullable=True + ) + name_local: Mapped[str | None] = mapped_column(String, nullable=True) + state_province: Mapped[str | None] = mapped_column(String, nullable=True) + region: Mapped[str | None] = mapped_column(String, nullable=True) + basin: Mapped[str | None] = mapped_column(String, nullable=True) + owners: Mapped[list | None] = mapped_column(PORTABLE_JSON, nullable=True) + operators: Mapped[list | None] = mapped_column(PORTABLE_JSON, nullable=True) + location_type: Mapped[str | None] = mapped_column(String, nullable=True) + production_conventionality: Mapped[str | None] = mapped_column( + String, nullable=True + ) + primary_hydrocarbon_group: Mapped[str | None] = mapped_column(String, nullable=True) + reservoir_formation: Mapped[str | None] = mapped_column(String, nullable=True) + discovery_year: Mapped[int | None] = mapped_column( + Integer, year_constraints("discovery_year"), nullable=True + ) + production_start_year: Mapped[int | None] = mapped_column( + Integer, year_constraints("production_start_year"), nullable=True + ) + fid_year: Mapped[int | None] = mapped_column( + Integer, year_constraints("fid_year"), nullable=True + ) + field_status: Mapped[str | None] = mapped_column(String, nullable=True) + def __init_subclass__(cls, **kwargs) -> None: super().__init_subclass__(**kwargs) for base in getattr(cls, "__orig_bases__", ()): @@ -76,72 +107,33 @@ def from_entity(cls, entity: TModelIn) -> "SourceBase": return cls(**filtered) -class GemSourceModel_(SourceBase[OilAndGasFieldSourceData, OilAndGasFieldSource]): - __tablename__ = "gem_field_sources" - - -class GemSourceModel(SourceBase[GemData, GemSource]): +class GemSourceModel(SourceBase[OilAndGasFieldSourceData, OilAndGasFieldSource]): __tablename__ = "gem_sources" - name: Mapped[str] - country: Mapped[str] - lat: Mapped[float] = mapped_column(lat_constraints("lat")) - lon: Mapped[float] = mapped_column(lon_constraints("lon")) - -class WMSourceModel(SourceBase[WMData, WMSource]): +class WMSourceModel(SourceBase[OilAndGasFieldSourceData, OilAndGasFieldSource]): __tablename__ = "wm_sources" - field_name: Mapped[str] - field_country: Mapped[str] - production: Mapped[float] - -class RMIManualSourceModel(SourceBase[RMIManualData, RMIManualSource]): +class RMIManualSourceModel(SourceBase[OilAndGasFieldSourceData, OilAndGasFieldSource]): __tablename__ = "rmi_manual_sources" - name_override: Mapped[str | None] - gwp: Mapped[float | None] - gor: Mapped[float | None | None] = mapped_column( - float_constraint("gor", 0, 1), nullable=True - ) - country: Mapped[str | None] - latitude: Mapped[float | None] = mapped_column( - lat_constraints("latitude"), nullable=True - ) - longitude: Mapped[float | None] = mapped_column( - lon_constraints("longitude"), nullable=True - ) - - -class CCReservoirsSourceModel(SourceBase[CCReservoirsData, CCReservoirsSource]): - __tablename__ = "cc_reservoirs_sources" - name: Mapped[str] - basin: Mapped[str] - depth: Mapped[float] - geofence: Mapped[list[tuple[float, float]]] = mapped_column(StitchJson()) - - -SourceModel = ( - GemSourceModel | WMSourceModel | RMIManualSourceModel | CCReservoirsSourceModel -) +SourceModel = GemSourceModel | WMSourceModel | RMIManualSourceModel SourceModelCls = type[SourceModel] SOURCE_TABLES: Final[Mapping[SourceKey, SourceModelCls]] = { "gem": GemSourceModel, "wm": WMSourceModel, "rmi": RMIManualSourceModel, - "cc": CCReservoirsSourceModel, } class SourceModelData(TypedDict, total=False): gem: MutableMapping[IdType, GemSourceModel] wm: MutableMapping[IdType, WMSourceModel] - cc: MutableMapping[IdType, CCReservoirsSourceModel] rmi: MutableMapping[IdType, RMIManualSourceModel] def empty_source_model_data(): - return SourceModelData(gem={}, wm={}, cc={}, rmi={}) + return SourceModelData(gem={}, wm={}, rmi={}) diff --git a/deployments/api/src/stitch/api/db/resource_actions.py b/deployments/api/src/stitch/api/db/resource_actions.py index 4e407ce..eff8d54 100644 --- a/deployments/api/src/stitch/api/db/resource_actions.py +++ b/deployments/api/src/stitch/api/db/resource_actions.py @@ -20,7 +20,6 @@ ) from .model import ( - CCReservoirsSourceModel, GemSourceModel, MembershipModel, RMIManualSourceModel, @@ -144,13 +143,11 @@ async def create_source_data(session: AsyncSession, data: CreateSourceData): gems = tuple(GemSourceModel.from_entity(gem) for gem in data.gem) wms = tuple(WMSourceModel.from_entity(wm) for wm in data.wm) rmis = tuple(RMIManualSourceModel.from_entity(rmi) for rmi in data.rmi) - ccs = tuple(CCReservoirsSourceModel.from_entity(cc) for cc in data.cc) - session.add_all(gems + wms + rmis + ccs) + session.add_all(gems + wms + rmis) await session.flush() return SourceData( gem={g.id: g.as_entity() for g in gems}, wm={wm.id: wm.as_entity() for wm in wms}, rmi={rmi.id: rmi.as_entity() for rmi in rmis}, - cc={cc.id: cc.as_entity() for cc in ccs}, ) diff --git a/deployments/api/src/stitch/api/entities.py b/deployments/api/src/stitch/api/entities.py index 4401800..9691151 100644 --- a/deployments/api/src/stitch/api/entities.py +++ b/deployments/api/src/stitch/api/entities.py @@ -1,7 +1,6 @@ from collections.abc import Sequence from datetime import datetime from typing import ( - Annotated, Generic, Literal, Mapping, @@ -10,7 +9,10 @@ runtime_checkable, ) from uuid import UUID -from pydantic import BaseModel, ConfigDict, EmailStr, Field +from pydantic import BaseModel, EmailStr, Field + +from stitch.resources.ogsi.models import OilAndGasFieldSourceData +from stitch.api.sources import OilAndGasFieldSource IdType = int | str | UUID @@ -24,9 +26,8 @@ def id(self) -> IdType: ... GEM_SRC = Literal["gem"] WM_SRC = Literal["wm"] RMI_SRC = Literal["rmi"] -CC_SRC = Literal["cc"] -SourceKey = GEM_SRC | WM_SRC | RMI_SRC | CC_SRC +SourceKey = GEM_SRC | WM_SRC | RMI_SRC TSourceKey = TypeVar("TSourceKey", bound=SourceKey) @@ -36,10 +37,6 @@ class Timestamped(BaseModel): updated: datetime = Field(default_factory=datetime.now) -class Identified(BaseModel): - id: IdType - - class SourceBase(BaseModel, Generic[TSourceKey]): source: TSourceKey id: IdType @@ -50,76 +47,25 @@ class SourceRef(BaseModel): id: int -# The sources will come in and be initially stored in a raw table. -# That raw table will be an append-only table. -# We'll translate that data into one of the below structures, so each source will have a `UUID` or similar that -# references their id in the "raw" table. -# When pulling into the internal "sources" table, each will get a new unique id which is what the memberships will reference - - -class GemData(BaseModel): - name: str - lat: float = Field(ge=-90, le=90) - lon: float = Field(ge=-180, le=180) - country: str - - -class GemSource(Identified, GemData): - model_config = ConfigDict(from_attributes=True) - - -class WMData(BaseModel): - field_name: str - field_country: str - production: float - - -class WMSource(Identified, WMData): - model_config = ConfigDict(from_attributes=True) - - -class RMIManualData(BaseModel): - name_override: str - gwp: float - gor: float = Field(gt=0, lt=1) - country: str - latitude: float = Field(ge=-90, le=90) - longitude: float = Field(ge=-180, le=180) - - -class RMIManualSource(Identified, RMIManualData): - model_config = ConfigDict(from_attributes=True) - - -class CCReservoirsData(BaseModel): - name: str - basin: str - depth: float - geofence: Sequence[tuple[float, float]] - - -class CCReservoirsSource(Identified, CCReservoirsData): - model_config = ConfigDict(from_attributes=True) - - -OGSISourcePayload = Annotated[ - GemSource | WMSource | RMIManualSource | CCReservoirsSource, - Field(discriminator="source"), -] +# Entity aliases — all sources share the same schema +GemData = OilAndGasFieldSourceData +WMData = OilAndGasFieldSourceData +RMIManualData = OilAndGasFieldSourceData +GemSource = OilAndGasFieldSource +WMSource = OilAndGasFieldSource +RMIManualSource = OilAndGasFieldSource class SourceData(BaseModel): gem: Mapping[IdType, GemSource] = Field(default_factory=dict) wm: Mapping[IdType, WMSource] = Field(default_factory=dict) rmi: Mapping[IdType, RMIManualSource] = Field(default_factory=dict) - cc: Mapping[IdType, CCReservoirsSource] = Field(default_factory=dict) class CreateSourceData(BaseModel): gem: Sequence[GemData] = Field(default_factory=list) wm: Sequence[WMData] = Field(default_factory=list) rmi: Sequence[RMIManualData] = Field(default_factory=list) - cc: Sequence[CCReservoirsData] = Field(default_factory=list) class CreateResourceSourceData(BaseModel): @@ -132,7 +78,6 @@ class CreateResourceSourceData(BaseModel): gem: Sequence[GemData | int] = Field(default_factory=list) wm: Sequence[WMData | int] = Field(default_factory=list) rmi: Sequence[RMIManualData | int] = Field(default_factory=list) - cc: Sequence[CCReservoirsData | int] = Field(default_factory=list) def get(self, key: SourceKey): if key == "gem": @@ -141,8 +86,6 @@ def get(self, key: SourceKey): return self.wm elif key == "rmi": return self.rmi - elif key == "cc": - return self.cc raise ValueError(f"Unknown source key: {key}") diff --git a/deployments/api/src/stitch/api/sources.py b/deployments/api/src/stitch/api/sources.py index 562b2da..8c8cc05 100644 --- a/deployments/api/src/stitch/api/sources.py +++ b/deployments/api/src/stitch/api/sources.py @@ -1,6 +1,7 @@ -from pydantic import Field +from pydantic import ConfigDict, Field from stitch.resources.ogsi.models import OilAndGasFieldSourceData class OilAndGasFieldSource(OilAndGasFieldSourceData): + model_config = ConfigDict(from_attributes=True) id: int = Field(..., description="Stable, unique identifier for the resource") diff --git a/deployments/api/tests/conftest.py b/deployments/api/tests/conftest.py index 2551a91..f3ee09d 100644 --- a/deployments/api/tests/conftest.py +++ b/deployments/api/tests/conftest.py @@ -9,7 +9,6 @@ from stitch.api.db.config import UnitOfWork, get_uow from stitch.api.db.model import ( - CCReservoirsSourceModel, GemSourceModel, RMIManualSourceModel, StitchBase, @@ -21,7 +20,6 @@ from stitch.api.main import app from .utils import ( - CC_DEFAULTS, GEM_DEFAULTS, RMI_DEFAULTS, WM_DEFAULTS, @@ -185,8 +183,8 @@ async def existing_gem_source( """Pre-create a GEM source in DB, return model with ID.""" model = GemSourceModel( name=GEM_DEFAULTS["name"], - lat=GEM_DEFAULTS["lat"], - lon=GEM_DEFAULTS["lon"], + latitude=GEM_DEFAULTS["latitude"], + longitude=GEM_DEFAULTS["longitude"], country=GEM_DEFAULTS["country"], ) seeded_integration_session.add(model) @@ -200,9 +198,8 @@ async def existing_wm_source( ) -> WMSourceModel: """Pre-create a WM source in DB, return model with ID.""" model = WMSourceModel( - field_name=WM_DEFAULTS["field_name"], - field_country=WM_DEFAULTS["field_country"], - production=WM_DEFAULTS["production"], + name=WM_DEFAULTS["name"], + country=WM_DEFAULTS["country"], ) seeded_integration_session.add(model) await seeded_integration_session.flush() @@ -215,9 +212,7 @@ async def existing_rmi_source( ) -> RMIManualSourceModel: """Pre-create an RMI source in DB, return model with ID.""" model = RMIManualSourceModel( - name_override=RMI_DEFAULTS["name_override"], - gwp=RMI_DEFAULTS["gwp"], - gor=RMI_DEFAULTS["gor"], + name=RMI_DEFAULTS["name"], country=RMI_DEFAULTS["country"], latitude=RMI_DEFAULTS["latitude"], longitude=RMI_DEFAULTS["longitude"], @@ -227,22 +222,6 @@ async def existing_rmi_source( return model -@pytest.fixture -async def existing_cc_source( - seeded_integration_session: AsyncSession, -) -> CCReservoirsSourceModel: - """Pre-create a CC source in DB, return model with ID.""" - model = CCReservoirsSourceModel( - name=CC_DEFAULTS["name"], - basin=CC_DEFAULTS["basin"], - depth=CC_DEFAULTS["depth"], - geofence=list(CC_DEFAULTS["geofence"]), - ) - seeded_integration_session.add(model) - await seeded_integration_session.flush() - return model - - @pytest.fixture async def existing_sources( seeded_integration_session: AsyncSession, @@ -251,42 +230,27 @@ async def existing_sources( session = seeded_integration_session gems = [ - GemSourceModel(name=f"GEM {i}", lat=45.0 + i, lon=-120.0 + i, country="USA") - for i in range(2) - ] - wms = [ - WMSourceModel( - field_name=f"WM Field {i}", field_country="USA", production=1000.0 * (i + 1) + GemSourceModel( + name=f"GEM {i}", latitude=45.0 + i, longitude=-120.0 + i, country="USA" ) for i in range(2) ] + wms = [WMSourceModel(name=f"WM Field {i}", country="USA") for i in range(2)] rmis = [ RMIManualSourceModel( - name_override=f"RMI {i}", - gwp=25.0, - gor=0.5, + name=f"RMI {i}", country="USA", latitude=40.0 + i, longitude=-100.0 + i, ) for i in range(2) ] - ccs = [ - CCReservoirsSourceModel( - name=f"CC Reservoir {i}", - basin="Permian", - depth=3000.0, - geofence=[(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)], - ) - for i in range(2) - ] - session.add_all(gems + wms + rmis + ccs) + session.add_all(gems + wms + rmis) await session.flush() return { "gem": [g.id for g in gems], "wm": [w.id for w in wms], "rmi": [r.id for r in rmis], - "cc": [c.id for c in ccs], } diff --git a/deployments/api/tests/db/test_resource_actions.py b/deployments/api/tests/db/test_resource_actions.py index 39538a2..85d56f0 100644 --- a/deployments/api/tests/db/test_resource_actions.py +++ b/deployments/api/tests/db/test_resource_actions.py @@ -14,7 +14,6 @@ from stitch.api.entities import CreateSourceData, GemData, User, WMData from tests.utils import ( - make_cc_data, make_create_resource, make_empty_resource, make_gem_data, @@ -77,7 +76,9 @@ async def test_creates_resource_with_new_gem_source( ): """New GEM source creates resource, source, and membership.""" resource_in = make_resource_with_new_sources( - gem=make_gem_data(name="Test GEM Field", lat=40.0, lon=-100.0).model, + gem=make_gem_data( + name="Test GEM Field", latitude=40.0, longitude=-100.0 + ).model, name="With GEM", ) @@ -123,12 +124,11 @@ async def test_creates_resource_with_new_sources_all_types( seeded_integration_session: AsyncSession, test_user: User, ): - """Resource with all four source types creates correct memberships.""" + """Resource with all three source types creates correct memberships.""" source_data = make_source_data( gem=[make_gem_data(name="All Types GEM").model], - wm=[make_wm_data(field_name="All Types WM").model], - rmi=[make_rmi_data(name_override="All Types RMI").model], - cc=[make_cc_data(name="All Types CC").model], + wm=[make_wm_data(name="All Types WM").model], + rmi=[make_rmi_data(name="All Types RMI").model], ) resource_in = make_create_resource( name="All Sources Resource", @@ -154,7 +154,7 @@ async def test_creates_resource_with_new_sources_all_types( ) sources = {m.source for m in memberships} - assert sources == {"gem", "wm", "rmi", "cc"} + assert sources == {"gem", "wm", "rmi"} @pytest.mark.anyio async def test_creates_resource_with_existing_gem_id( @@ -393,11 +393,15 @@ async def test_bulk_creates_sources_returns_source_data_with_ids( """Bulk create sources returns SourceData with assigned IDs.""" source_data = CreateSourceData( gem=[ - GemData(name="Bulk GEM 1", lat=40.0, lon=-100.0, country="USA"), - GemData(name="Bulk GEM 2", lat=41.0, lon=-101.0, country="CAN"), + GemData( + name="Bulk GEM 1", latitude=40.0, longitude=-100.0, country="USA" + ), + GemData( + name="Bulk GEM 2", latitude=41.0, longitude=-101.0, country="CAN" + ), ], wm=[ - WMData(field_name="Bulk WM", field_country="USA", production=5000.0), + WMData(name="Bulk WM", country="USA"), ], ) diff --git a/deployments/api/tests/routers/test_resources_integration.py b/deployments/api/tests/routers/test_resources_integration.py index 58f2d13..9d86ade 100644 --- a/deployments/api/tests/routers/test_resources_integration.py +++ b/deployments/api/tests/routers/test_resources_integration.py @@ -28,7 +28,9 @@ async def test_get_nonexistent_returns_404(self, integration_client): async def test_create_resource_returns_resource(self, integration_client): """POST /resources/ returns the created resource.""" resource_in = make_resource_with_new_sources( - gem=make_gem_data(name="GEM Integration Field", lat=40.0, lon=-100.0).model, + gem=make_gem_data( + name="GEM Integration Field", latitude=40.0, longitude=-100.0 + ).model, name="Integration Test Resource", country="USA", ) @@ -46,9 +48,7 @@ async def test_create_resource_returns_resource(self, integration_client): async def test_create_and_get_resource(self, integration_client): """POST creates resource, GET retrieves it.""" resource_in = make_resource_with_new_sources( - wm=make_wm_data( - field_name="WM Roundtrip Field", field_country="CAN", production=5000.0 - ).model, + wm=make_wm_data(name="WM Roundtrip Field", country="CAN").model, name="Roundtrip Resource", country="CAN", ) @@ -75,7 +75,7 @@ async def test_create_persists_to_database( """POST resource is persisted and queryable directly.""" resource_in = make_resource_with_new_sources( gem=make_gem_data( - name="GEM Persist Field", lat=25.0, lon=-105.0, country="MEX" + name="GEM Persist Field", latitude=25.0, longitude=-105.0, country="MEX" ).model, name="Persisted Resource", country="MEX", diff --git a/deployments/api/tests/routers/test_resources_unit.py b/deployments/api/tests/routers/test_resources_unit.py index b8841ce..2982c23 100644 --- a/deployments/api/tests/routers/test_resources_unit.py +++ b/deployments/api/tests/routers/test_resources_unit.py @@ -91,7 +91,7 @@ async def test_creates_resource_with_user(self, async_client, mock_uow, test_use expected = make_resource(id=123, name="New Resource", country="CAN") resource_in = make_resource_with_new_sources( gem=make_gem_data( - name="GEM Field", lat=45.0, lon=-120.0, country="CAN" + name="GEM Field", latitude=45.0, longitude=-120.0, country="CAN" ).model, name="New Resource", country="CAN", @@ -117,9 +117,7 @@ async def test_returns_created_resource(self, async_client, mock_uow): """POST /resources/ returns the created resource entity.""" expected = make_resource(id=456, name="Created Resource") resource_in = make_resource_with_new_sources( - wm=make_wm_data( - field_name="WM Field", field_country="USA", production=1000.0 - ).model, + wm=make_wm_data(name="WM Field", country="USA").model, name="Created Resource", ) diff --git a/deployments/api/tests/utils.py b/deployments/api/tests/utils.py index 490383c..f927b0c 100644 --- a/deployments/api/tests/utils.py +++ b/deployments/api/tests/utils.py @@ -13,7 +13,6 @@ from pydantic import BaseModel from stitch.api.entities import ( - CCReservoirsData, CreateResource, CreateResourceSourceData, GemData, @@ -39,88 +38,63 @@ def data(self) -> dict[str, Any]: # Static defaults for each source type (no id - these are for creation) GEM_DEFAULTS: dict[str, Any] = { "name": "Default GEM Field", - "lat": 45.0, - "lon": -120.0, + "latitude": 45.0, + "longitude": -120.0, "country": "USA", } WM_DEFAULTS: dict[str, Any] = { - "field_name": "Default WM Field", - "field_country": "USA", - "production": 1000.0, + "name": "Default WM Field", + "country": "USA", } RMI_DEFAULTS: dict[str, Any] = { - "name_override": "Default RMI", - "gwp": 25.0, - "gor": 0.5, + "name": "Default RMI", "country": "USA", "latitude": 40.0, "longitude": -100.0, } -CC_DEFAULTS: dict[str, Any] = { - "name": "Default CC Reservoir", - "basin": "Permian", - "depth": 3000.0, - "geofence": [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)], -} - def make_gem_data( name: str = GEM_DEFAULTS["name"], - lat: float = GEM_DEFAULTS["lat"], - lon: float = GEM_DEFAULTS["lon"], + latitude: float = GEM_DEFAULTS["latitude"], + longitude: float = GEM_DEFAULTS["longitude"], country: str = GEM_DEFAULTS["country"], + **kwargs: Any, ) -> FactoryResult[GemData]: """Create a GemData with both model and dict representations.""" - return FactoryResult(model=GemData(name=name, lat=lat, lon=lon, country=country)) + return FactoryResult( + model=GemData( + name=name, latitude=latitude, longitude=longitude, country=country, **kwargs + ) + ) def make_wm_data( - field_name: str = WM_DEFAULTS["field_name"], - field_country: str = WM_DEFAULTS["field_country"], - production: float = WM_DEFAULTS["production"], + name: str = WM_DEFAULTS["name"], + country: str = WM_DEFAULTS["country"], + **kwargs: Any, ) -> FactoryResult[WMData]: """Create a WMData with both model and dict representations.""" - return FactoryResult( - model=WMData( - field_name=field_name, field_country=field_country, production=production - ) - ) + return FactoryResult(model=WMData(name=name, country=country, **kwargs)) def make_rmi_data( - name_override: str = RMI_DEFAULTS["name_override"], - gwp: float = RMI_DEFAULTS["gwp"], - gor: float = RMI_DEFAULTS["gor"], + name: str = RMI_DEFAULTS["name"], country: str = RMI_DEFAULTS["country"], latitude: float = RMI_DEFAULTS["latitude"], longitude: float = RMI_DEFAULTS["longitude"], + **kwargs: Any, ) -> FactoryResult[RMIManualData]: """Create an RMIManualData with both model and dict representations.""" return FactoryResult( model=RMIManualData( - name_override=name_override, - gwp=gwp, - gor=gor, + name=name, country=country, latitude=latitude, longitude=longitude, - ) - ) - - -def make_cc_data( - name: str = CC_DEFAULTS["name"], - basin: str = CC_DEFAULTS["basin"], - depth: float = CC_DEFAULTS["depth"], - geofence: Sequence[tuple[float, float]] = CC_DEFAULTS["geofence"], -) -> FactoryResult[CCReservoirsData]: - """Create a CCReservoirsData with both model and dict representations.""" - return FactoryResult( - model=CCReservoirsData( - name=name, basin=basin, depth=depth, geofence=list(geofence) + **kwargs, ) ) @@ -129,7 +103,6 @@ def make_source_data( gem: Sequence[GemData | int] | None = None, wm: Sequence[WMData | int] | None = None, rmi: Sequence[RMIManualData | int] | None = None, - cc: Sequence[CCReservoirsData | int] | None = None, ) -> FactoryResult[CreateResourceSourceData]: """Create CreateResourceSourceData with both model and dict representations. @@ -137,7 +110,6 @@ def make_source_data( gem: List of GemData models or existing source IDs wm: List of WMData models or existing source IDs rmi: List of RMIManualData models or existing source IDs - cc: List of CCReservoirsData models or existing source IDs Returns: FactoryResult with model and data (dict) attributes @@ -147,7 +119,6 @@ def make_source_data( gem=list(gem or []), wm=list(wm or []), rmi=list(rmi or []), - cc=list(cc or []), ) ) @@ -197,7 +168,6 @@ def make_resource_with_new_sources( gem: GemData | Sequence[GemData] | None = None, wm: WMData | Sequence[WMData] | None = None, rmi: RMIManualData | Sequence[RMIManualData] | None = None, - cc: CCReservoirsData | Sequence[CCReservoirsData] | None = None, name: str | None = "Resource with Sources", country: str | None = "USA", ) -> FactoryResult[CreateResource]: @@ -214,7 +184,6 @@ def to_list(item: Any | Sequence[Any] | None) -> list[Any]: gem=to_list(gem), wm=to_list(wm), rmi=to_list(rmi), - cc=to_list(cc), ) return make_create_resource(name=name, country=country, source_data=source_data) @@ -223,7 +192,6 @@ def make_resource_with_existing_ids( gem_ids: Sequence[int] | None = None, wm_ids: Sequence[int] | None = None, rmi_ids: Sequence[int] | None = None, - cc_ids: Sequence[int] | None = None, name: str | None = "Resource with Existing Sources", country: str | None = "USA", ) -> FactoryResult[CreateResource]: @@ -232,7 +200,6 @@ def make_resource_with_existing_ids( gem=list(gem_ids or []), wm=list(wm_ids or []), rmi=list(rmi_ids or []), - cc=list(cc_ids or []), ) return make_create_resource(name=name, country=country, source_data=source_data) @@ -244,8 +211,6 @@ def make_resource_with_mixed_sources( existing_wm_ids: Sequence[int] | None = None, new_rmi: RMIManualData | Sequence[RMIManualData] | None = None, existing_rmi_ids: Sequence[int] | None = None, - new_cc: CCReservoirsData | Sequence[CCReservoirsData] | None = None, - existing_cc_ids: Sequence[int] | None = None, name: str | None = "Resource with Mixed Sources", country: str | None = "USA", ) -> FactoryResult[CreateResource]: @@ -263,11 +228,6 @@ def to_list(item: Any | Sequence[Any] | None) -> list[Any]: rmi_items: list[RMIManualData | int] = to_list(new_rmi) + list( existing_rmi_ids or [] ) - cc_items: list[CCReservoirsData | int] = to_list(new_cc) + list( - existing_cc_ids or [] - ) - source_data = make_source_data( - gem=gem_items, wm=wm_items, rmi=rmi_items, cc=cc_items - ) + source_data = make_source_data(gem=gem_items, wm=wm_items, rmi=rmi_items) return make_create_resource(name=name, country=country, source_data=source_data) From 7dc683e85de9aa46fbdd3bd046c558ac8debea62 Mon Sep 17 00:00:00 2001 From: Michael Barlow Date: Thu, 12 Feb 2026 16:26:55 -0700 Subject: [PATCH 6/6] chore(stitch-resources): remove default init function --- packages/stitch-resources/src/stitch/resources/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/stitch-resources/src/stitch/resources/__init__.py b/packages/stitch-resources/src/stitch/resources/__init__.py index d843159..e69de29 100644 --- a/packages/stitch-resources/src/stitch/resources/__init__.py +++ b/packages/stitch-resources/src/stitch/resources/__init__.py @@ -1,2 +0,0 @@ -def hello() -> str: - return "Hello from stitch-resources!"