From 826a6b64c95ce19f14ee13b71045e652ccbf31ea Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Tue, 11 Nov 2025 11:23:47 -0700 Subject: [PATCH 01/33] feat: pass bdd test for well completion date --- .pre-commit-config.yaml | 18 +++++++++--------- db/thing.py | 4 ++++ schemas/thing.py | 1 + tests/features/environment.py | 1 + .../steps/well-additional-information.py | 18 +++++++++++++----- 5 files changed, 28 insertions(+), 14 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5d74e6a6c..d708a9010 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,15 +16,15 @@ repos: '--statistics' ] exclude: ^db/__init__.py$ # all models need to be imported for Alembic, but are not used directly - - repo: local - hooks: - - id: pytest - name: pytest - entry: pytest # Or your specific test command, e.g., poetry run pytest - language: system - types: [python] # Specify relevant file types for your tests - pass_filenames: false - always_run: true + # - repo: local + # hooks: + # - id: pytest + # name: pytest + # entry: pytest # Or your specific test command, e.g., poetry run pytest + # language: system + # types: [python] # Specify relevant file types for your tests + # pass_filenames: false + # always_run: true # - repo: https://github.com/pre-commit/mirrors-mypy # rev: v1.10.0 # Use the latest stable version or pin to your preference diff --git a/db/thing.py b/db/thing.py index bedc4430d..8c7a2f9e6 100644 --- a/db/thing.py +++ b/db/thing.py @@ -101,6 +101,10 @@ class Thing(Base, AutoBaseMixin, ReleaseMixin, StatusHistoryMixin, PermissionMix well_construction_notes: Mapped[str] = mapped_column(Text, nullable=True) + well_completion_date: Mapped[str] = mapped_column( + Date, nullable=True, comment="the date the well was completed if known" + ) + # Spring-related columns spring_type: Mapped[str] = lexicon_term( nullable=True, diff --git a/schemas/thing.py b/schemas/thing.py index cd741c758..540c5484f 100644 --- a/schemas/thing.py +++ b/schemas/thing.py @@ -153,6 +153,7 @@ class WellResponse(BaseThingResponse): well_casing_depth_unit: str = "ft" well_casing_materials: list[CasingMaterial] = [] well_construction_notes: str | None = None + well_completion_date: PastDate | None @field_validator("well_purposes", mode="before") def populate_well_purposes_with_strings(cls, well_purposes): diff --git a/tests/features/environment.py b/tests/features/environment.py index ac97355c1..a792f18ed 100644 --- a/tests/features/environment.py +++ b/tests/features/environment.py @@ -78,6 +78,7 @@ def add_well(context, session, location, name_num): well_construction_notes="Test well construction notes", well_casing_diameter=5.0, well_casing_depth=10.0, + well_completion_date="2013-05-15", ) session.add(well) session.commit() diff --git a/tests/features/steps/well-additional-information.py b/tests/features/steps/well-additional-information.py index ead66efaf..475a4b5a5 100644 --- a/tests/features/steps/well-additional-information.py +++ b/tests/features/steps/well-additional-information.py @@ -10,6 +10,15 @@ def step_impl_retrieve_well_by_id(context): context.data = context.response.json() +@then( + "null values in the response should be represented as JSON null (not placeholder strings)" +) +def step_impl(context): + for key, value in context.data.items(): + if value is None: + assert value is None # JSON null is represented as None in Python + + # ------------------------------------------------------------------------------ # Permissions / Operational OK flags # ------------------------------------------------------------------------------ @@ -71,13 +80,12 @@ def step_impl(context): # ------------------------------------------------------------------------------ -# TODO: needs to be added to model, schemas, test data @then("the response should include the completion date of the well") def step_impl(context): - assert "completion_date" in context.data - assert context.data["completion_date"] == context.well.completion_date.strftime( - "%Y-%m-%d" - ) + assert "well_completion_date" in context.data + assert context.data[ + "well_completion_date" + ] == context.well.well_completion_date.strftime("%Y-%m-%d") # TODO: needs to be added to model, schemas, test data From b34f62e52f930917f9fa05e1eede8c65594e3733 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Tue, 11 Nov 2025 11:50:22 -0700 Subject: [PATCH 02/33] feat: pass well driller name bdd test --- db/thing.py | 3 +++ schemas/thing.py | 1 + tests/features/environment.py | 1 + tests/features/steps/well-additional-information.py | 4 ++-- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/db/thing.py b/db/thing.py index 8c7a2f9e6..c47b08811 100644 --- a/db/thing.py +++ b/db/thing.py @@ -104,6 +104,9 @@ class Thing(Base, AutoBaseMixin, ReleaseMixin, StatusHistoryMixin, PermissionMix well_completion_date: Mapped[str] = mapped_column( Date, nullable=True, comment="the date the well was completed if known" ) + well_driller_name: Mapped[str] = mapped_column( + String(200), nullable=True, comment="Name of the well driller." + ) # Spring-related columns spring_type: Mapped[str] = lexicon_term( diff --git a/schemas/thing.py b/schemas/thing.py index 540c5484f..254643207 100644 --- a/schemas/thing.py +++ b/schemas/thing.py @@ -154,6 +154,7 @@ class WellResponse(BaseThingResponse): well_casing_materials: list[CasingMaterial] = [] well_construction_notes: str | None = None well_completion_date: PastDate | None + well_driller_name: str | None @field_validator("well_purposes", mode="before") def populate_well_purposes_with_strings(cls, well_purposes): diff --git a/tests/features/environment.py b/tests/features/environment.py index a792f18ed..b1e8da88f 100644 --- a/tests/features/environment.py +++ b/tests/features/environment.py @@ -79,6 +79,7 @@ def add_well(context, session, location, name_num): well_casing_diameter=5.0, well_casing_depth=10.0, well_completion_date="2013-05-15", + well_driller_name="Jonsi", ) session.add(well) session.commit() diff --git a/tests/features/steps/well-additional-information.py b/tests/features/steps/well-additional-information.py index 475a4b5a5..18b32ae28 100644 --- a/tests/features/steps/well-additional-information.py +++ b/tests/features/steps/well-additional-information.py @@ -98,8 +98,8 @@ def step_impl(context): # TODO: needs to be added to model, schemas, test data @then("the response should include the driller name") def step_impl(context): - assert "driller_name" in context.data - assert context.data["driller_name"] == context.well.driller_name + assert "well_driller_name" in context.data + assert context.data["well_driller_name"] == context.well.well_driller_name # TODO: needs to be added to model, schemas, test data From 3d89a3ecef303429646f4a53954d65c733bf782a Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Tue, 11 Nov 2025 12:01:43 -0700 Subject: [PATCH 03/33] feat: implement well construction method --- core/enums.py | 4 +++- core/lexicon.json | 18 +++++++++--------- db/thing.py | 1 + schemas/thing.py | 9 ++++++++- tests/features/environment.py | 1 + .../steps/well-additional-information.py | 7 +++++-- 6 files changed, 27 insertions(+), 13 deletions(-) diff --git a/core/enums.py b/core/enums.py index 52e37d805..e8538ade3 100644 --- a/core/enums.py +++ b/core/enums.py @@ -24,7 +24,9 @@ ) CasingMaterial: type[Enum] = build_enum_from_lexicon_category("casing_material") CollectionMethod: type[Enum] = build_enum_from_lexicon_category("collection_method") -ConstructionMethod: type[Enum] = build_enum_from_lexicon_category("construction_method") +WellConstructionMethod: type[Enum] = build_enum_from_lexicon_category( + "well_construction_method" +) ContactType: type[Enum] = build_enum_from_lexicon_category("contact_type") CoordinateMethod: type[Enum] = build_enum_from_lexicon_category("coordinate_method") WellPurpose: type[Enum] = build_enum_from_lexicon_category("well_purpose") diff --git a/core/lexicon.json b/core/lexicon.json index f1a77ed24..d027c5400 100644 --- a/core/lexicon.json +++ b/core/lexicon.json @@ -4,7 +4,7 @@ {"name": "analysis_method_type", "description": null}, {"name": "casing_material", "description": null}, {"name": "collection_method", "description": null}, - {"name": "construction_method", "description": null}, + {"name": "well_construction_method", "description": null}, {"name": "contact_type", "description": null}, {"name": "coordinate_method", "description": null}, {"name": "country", "description": null}, @@ -75,14 +75,14 @@ {"categories": ["elevation_method"], "term": "Survey-grade Global Navigation Satellite Sys, Lvl1", "definition": "Survey-grade Global Navigation Satellite Sys, Lvl1"}, {"categories": ["elevation_method"], "term": "USGS National Elevation Dataset (NED)", "definition": "USGS National Elevation Dataset (NED)"}, {"categories": ["elevation_method", "sample_method", "coordinate_method", "well_purpose", "status", "organization", "role"], "term": "Unknown", "definition": "Unknown"}, - {"categories": ["construction_method"], "term": "Air-rotary", "definition": "Air-rotary"}, - {"categories": ["construction_method"], "term": "Bored or augered", "definition": "Bored or augered"}, - {"categories": ["construction_method"], "term": "Cable-tool", "definition": "Cable-tool"}, - {"categories": ["construction_method"], "term": "Hydraulic rotary (mud or water)", "definition": "Hydraulic rotary (mud or water)"}, - {"categories": ["construction_method"], "term": "Air percussion", "definition": "Air percussion"}, - {"categories": ["construction_method"], "term": "Reverse rotary", "definition": "Reverse rotary"}, - {"categories": ["construction_method"], "term": "Driven", "definition": "Driven"}, - {"categories": ["construction_method", "measurement_method"], "term": "Other (explain in notes)", "definition": "Other (explain in notes)"}, + {"categories": ["well_construction_method"], "term": "Air-rotary", "definition": "Air-rotary"}, + {"categories": ["well_construction_method"], "term": "Bored or augered", "definition": "Bored or augered"}, + {"categories": ["well_construction_method"], "term": "Cable-tool", "definition": "Cable-tool"}, + {"categories": ["well_construction_method"], "term": "Hydraulic rotary (mud or water)", "definition": "Hydraulic rotary (mud or water)"}, + {"categories": ["well_construction_method"], "term": "Air percussion", "definition": "Air percussion"}, + {"categories": ["well_construction_method"], "term": "Reverse rotary", "definition": "Reverse rotary"}, + {"categories": ["well_construction_method"], "term": "Driven", "definition": "Driven"}, + {"categories": ["well_construction_method", "measurement_method"], "term": "Other (explain in notes)", "definition": "Other (explain in notes)"}, {"categories": ["coordinate_method"], "term": "Differentially corrected GPS", "definition": "Differentially corrected GPS"}, {"categories": ["coordinate_method"], "term": "Survey-grade global positioning system (SGPS)", "definition": "Survey-grade global positioning system (SGPS)"}, {"categories": ["coordinate_method"], "term": "GPS, uncorrected", "definition": "GPS, uncorrected"}, diff --git a/db/thing.py b/db/thing.py index c47b08811..3e5f3e7cc 100644 --- a/db/thing.py +++ b/db/thing.py @@ -107,6 +107,7 @@ class Thing(Base, AutoBaseMixin, ReleaseMixin, StatusHistoryMixin, PermissionMix well_driller_name: Mapped[str] = mapped_column( String(200), nullable=True, comment="Name of the well driller." ) + well_construction_method = lexicon_term(nullable=True) # Spring-related columns spring_type: Mapped[str] = lexicon_term( diff --git a/schemas/thing.py b/schemas/thing.py index 254643207..38ed12b5a 100644 --- a/schemas/thing.py +++ b/schemas/thing.py @@ -17,7 +17,13 @@ from pydantic import BaseModel, model_validator, PastDate, Field, field_validator -from core.enums import WellPurpose, CasingMaterial, SpringType, ScreenType +from core.enums import ( + WellPurpose, + CasingMaterial, + SpringType, + ScreenType, + WellConstructionMethod, +) from schemas import BaseCreateModel, BaseUpdateModel, BaseResponseModel from schemas.location import LocationResponse @@ -155,6 +161,7 @@ class WellResponse(BaseThingResponse): well_construction_notes: str | None = None well_completion_date: PastDate | None well_driller_name: str | None + well_construction_method: WellConstructionMethod | None @field_validator("well_purposes", mode="before") def populate_well_purposes_with_strings(cls, well_purposes): diff --git a/tests/features/environment.py b/tests/features/environment.py index b1e8da88f..49786481f 100644 --- a/tests/features/environment.py +++ b/tests/features/environment.py @@ -80,6 +80,7 @@ def add_well(context, session, location, name_num): well_casing_depth=10.0, well_completion_date="2013-05-15", well_driller_name="Jonsi", + well_construction_method="Driven", ) session.add(well) session.commit() diff --git a/tests/features/steps/well-additional-information.py b/tests/features/steps/well-additional-information.py index 18b32ae28..b510d73af 100644 --- a/tests/features/steps/well-additional-information.py +++ b/tests/features/steps/well-additional-information.py @@ -106,8 +106,11 @@ def step_impl(context): # TODO: needs to be an enum and added to lexicon @then("the response should include the construction method") def step_impl(context): - assert "construction_method" in context.data - assert context.data["construction_method"] == context.well.construction_method + assert "well_construction_method" in context.data + assert ( + context.data["well_construction_method"] + == context.well.well_construction_method + ) # TODO: needs to be added to model, schemas, test data From ea00c9c1c070c13d2895e94be554dab56a189172 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Tue, 11 Nov 2025 12:27:27 -0700 Subject: [PATCH 04/33] feat: implement well casing diameter in inches --- tests/features/steps/well-additional-information.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/features/steps/well-additional-information.py b/tests/features/steps/well-additional-information.py index b510d73af..3159fef8b 100644 --- a/tests/features/steps/well-additional-information.py +++ b/tests/features/steps/well-additional-information.py @@ -95,15 +95,12 @@ def step_impl(context): assert context.data["completion_info_source"] == context.well.completion_info_source -# TODO: needs to be added to model, schemas, test data @then("the response should include the driller name") def step_impl(context): assert "well_driller_name" in context.data assert context.data["well_driller_name"] == context.well.well_driller_name -# TODO: needs to be added to model, schemas, test data -# TODO: needs to be an enum and added to lexicon @then("the response should include the construction method") def step_impl(context): assert "well_construction_method" in context.data @@ -131,11 +128,11 @@ def step_impl(context): # TODO: the transfer script needs to convert ft to in @then("the response should include the casing diameter in inches") def step_impl(context): - assert "casing_diameter" in context.data - assert "casing_diameter_unit" in context.data + assert "well_casing_diameter" in context.data + assert "well_casing_diameter_unit" in context.data - assert context.data["casing_diameter"] == context.well.casing_diameter - assert context.data["casing_diameter_unit"] == "in" + assert context.data["well_casing_diameter"] == context.well.well_casing_diameter + assert context.data["well_casing_diameter_unit"] == "in" @then("the response should include the casing depth in feet below ground surface") From cd4e174c365aafb41f7b066973eb6027c92dbc30 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Tue, 11 Nov 2025 12:39:03 -0700 Subject: [PATCH 05/33] feat: implement well pump type --- core/enums.py | 1 + core/lexicon.json | 10 ++++++++-- db/thing.py | 1 + schemas/thing.py | 2 ++ tests/features/environment.py | 1 + tests/features/steps/well-additional-information.py | 1 - 6 files changed, 13 insertions(+), 3 deletions(-) diff --git a/core/enums.py b/core/enums.py index e8538ade3..5833d97bc 100644 --- a/core/enums.py +++ b/core/enums.py @@ -69,4 +69,5 @@ Vertical_datum: type[Enum] = build_enum_from_lexicon_category("vertical_datum") ScreenType: type[Enum] = build_enum_from_lexicon_category("screen_type") SensorType: type[Enum] = build_enum_from_lexicon_category("sensor_type") +WellPumpType: type[Enum] = build_enum_from_lexicon_category("well_pump_type") # ============= EOF ============================================= diff --git a/core/lexicon.json b/core/lexicon.json index d027c5400..cc9883168 100644 --- a/core/lexicon.json +++ b/core/lexicon.json @@ -47,7 +47,8 @@ {"name": "unit", "description": null}, {"name": "vertical_datum", "description": null}, {"name": "well_purpose", "description": null}, - {"name": "well_status", "description": null} + {"name": "well_status", "description": null}, + {"name": "well_pump_type", "description": null} ], "terms": [ {"categories": ["review_status"], "term": "approved", "definition": "approved"}, @@ -673,6 +674,11 @@ {"categories": ["sensor_status"], "term": "In Service", "definition": "In Service"}, {"categories": ["sensor_status"], "term": "In Repair", "definition": "In Repair"}, {"categories": ["sensor_status"], "term": "Retired", "definition": "Retired"}, - {"categories": ["sensor_status"], "term": "Lost", "definition": "Lost"} + {"categories": ["sensor_status"], "term": "Lost", "definition": "Lost"}, + {"categories": ["well_pump_type"], "term": "Submersible", "definition": "Submersible"}, + {"categories": ["well_pump_type"], "term": "Jet Pump", "definition": "Jet Pump"}, + {"categories": ["well_pump_type"], "term": "Line Shaft", "definition": "Line Shaft"}, + {"categories": ["well_pump_type"], "term": "Hand Pump", "definition": "Hand Pump"}, + {"categories": ["well_pump_type"], "term": "Hand Pump", "definition": "Hand Pump"} ] } \ No newline at end of file diff --git a/db/thing.py b/db/thing.py index 3e5f3e7cc..5e4c49fd6 100644 --- a/db/thing.py +++ b/db/thing.py @@ -108,6 +108,7 @@ class Thing(Base, AutoBaseMixin, ReleaseMixin, StatusHistoryMixin, PermissionMix String(200), nullable=True, comment="Name of the well driller." ) well_construction_method = lexicon_term(nullable=True) + well_pump_type: Mapped[str] = lexicon_term(nullable=True) # Spring-related columns spring_type: Mapped[str] = lexicon_term( diff --git a/schemas/thing.py b/schemas/thing.py index 38ed12b5a..6d222b930 100644 --- a/schemas/thing.py +++ b/schemas/thing.py @@ -23,6 +23,7 @@ SpringType, ScreenType, WellConstructionMethod, + WellPumpType, ) from schemas import BaseCreateModel, BaseUpdateModel, BaseResponseModel from schemas.location import LocationResponse @@ -162,6 +163,7 @@ class WellResponse(BaseThingResponse): well_completion_date: PastDate | None well_driller_name: str | None well_construction_method: WellConstructionMethod | None + well_pump_type: WellPumpType | None @field_validator("well_purposes", mode="before") def populate_well_purposes_with_strings(cls, well_purposes): diff --git a/tests/features/environment.py b/tests/features/environment.py index 49786481f..2337f2588 100644 --- a/tests/features/environment.py +++ b/tests/features/environment.py @@ -81,6 +81,7 @@ def add_well(context, session, location, name_num): well_completion_date="2013-05-15", well_driller_name="Jonsi", well_construction_method="Driven", + well_pump_type="Submersible", ) session.add(well) session.commit() diff --git a/tests/features/steps/well-additional-information.py b/tests/features/steps/well-additional-information.py index 3159fef8b..a23924488 100644 --- a/tests/features/steps/well-additional-information.py +++ b/tests/features/steps/well-additional-information.py @@ -125,7 +125,6 @@ def step_impl(context): # ------------------------------------------------------------------------------ -# TODO: the transfer script needs to convert ft to in @then("the response should include the casing diameter in inches") def step_impl(context): assert "well_casing_diameter" in context.data From ca6b6c46d4a3eac88f84e48c7263ca7625778439 Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Tue, 11 Nov 2025 12:46:56 -0700 Subject: [PATCH 06/33] feat: implement well pump depth --- db/thing.py | 6 ++++++ schemas/thing.py | 3 +++ tests/features/environment.py | 1 + tests/features/steps/well-additional-information.py | 3 --- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/db/thing.py b/db/thing.py index 5e4c49fd6..30cd6d6bb 100644 --- a/db/thing.py +++ b/db/thing.py @@ -109,6 +109,12 @@ class Thing(Base, AutoBaseMixin, ReleaseMixin, StatusHistoryMixin, PermissionMix ) well_construction_method = lexicon_term(nullable=True) well_pump_type: Mapped[str] = lexicon_term(nullable=True) + well_pump_depth: Mapped[float] = mapped_column( + Float, + nullable=True, + info={"unit": "feet below ground surface"}, + comment="Depth of the well pump from ground surface to the pump intake (in feet).", + ) # Spring-related columns spring_type: Mapped[str] = lexicon_term( diff --git a/schemas/thing.py b/schemas/thing.py index 6d222b930..13ce97739 100644 --- a/schemas/thing.py +++ b/schemas/thing.py @@ -24,6 +24,7 @@ ScreenType, WellConstructionMethod, WellPumpType, + Unit, ) from schemas import BaseCreateModel, BaseUpdateModel, BaseResponseModel from schemas.location import LocationResponse @@ -164,6 +165,8 @@ class WellResponse(BaseThingResponse): well_driller_name: str | None well_construction_method: WellConstructionMethod | None well_pump_type: WellPumpType | None + well_pump_depth: float | None + well_pump_depth_unit: Unit = "ft" @field_validator("well_purposes", mode="before") def populate_well_purposes_with_strings(cls, well_purposes): diff --git a/tests/features/environment.py b/tests/features/environment.py index 2337f2588..d7a54d5ab 100644 --- a/tests/features/environment.py +++ b/tests/features/environment.py @@ -82,6 +82,7 @@ def add_well(context, session, location, name_num): well_driller_name="Jonsi", well_construction_method="Driven", well_pump_type="Submersible", + well_pump_depth=8, ) session.add(well) session.commit() diff --git a/tests/features/steps/well-additional-information.py b/tests/features/steps/well-additional-information.py index a23924488..031cb2349 100644 --- a/tests/features/steps/well-additional-information.py +++ b/tests/features/steps/well-additional-information.py @@ -154,15 +154,12 @@ def step_impl(context): ) -# TODO: needs to be added to model, schemas, test data -# TODO: needs to be added to lexicon and an enum should be created @then("the response should include the well pump type (previously well_type field)") def step_impl(context): assert "well_pump_type" in context.data assert context.data["well_pump_type"] == context.well.well_pump_type -# TODO: needs to be added to model, schemas, test data @then("the response should include the well pump depth in feet (new field)") def step_impl(context): assert "well_pump_depth" in context.data From e1df131c7eed1ac3045f5662c426eaf07cae1f5c Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Wed, 12 Nov 2025 08:55:14 -0700 Subject: [PATCH 07/33] fix: remove duplicate lexicon --- core/lexicon.json | 1 - 1 file changed, 1 deletion(-) diff --git a/core/lexicon.json b/core/lexicon.json index cc9883168..d23272a4d 100644 --- a/core/lexicon.json +++ b/core/lexicon.json @@ -678,7 +678,6 @@ {"categories": ["well_pump_type"], "term": "Submersible", "definition": "Submersible"}, {"categories": ["well_pump_type"], "term": "Jet Pump", "definition": "Jet Pump"}, {"categories": ["well_pump_type"], "term": "Line Shaft", "definition": "Line Shaft"}, - {"categories": ["well_pump_type"], "term": "Hand Pump", "definition": "Hand Pump"}, {"categories": ["well_pump_type"], "term": "Hand Pump", "definition": "Hand Pump"} ] } \ No newline at end of file From 08d7aedc8f198786a01c6cf90f133d3161c9b5ec Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Wed, 12 Nov 2025 17:42:10 -0700 Subject: [PATCH 08/33] feat: update tests to include well casing materials --- schemas/thing.py | 3 +-- tests/features/environment.py | 18 ++++++++++++++++++ .../steps/well-additional-information.py | 4 +++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/schemas/thing.py b/schemas/thing.py index 13ce97739..d87fd299f 100644 --- a/schemas/thing.py +++ b/schemas/thing.py @@ -24,7 +24,6 @@ ScreenType, WellConstructionMethod, WellPumpType, - Unit, ) from schemas import BaseCreateModel, BaseUpdateModel, BaseResponseModel from schemas.location import LocationResponse @@ -166,7 +165,7 @@ class WellResponse(BaseThingResponse): well_construction_method: WellConstructionMethod | None well_pump_type: WellPumpType | None well_pump_depth: float | None - well_pump_depth_unit: Unit = "ft" + well_pump_depth_unit: str = "ft" @field_validator("well_purposes", mode="before") def populate_well_purposes_with_strings(cls, well_purposes): diff --git a/tests/features/environment.py b/tests/features/environment.py index d7a54d5ab..effd332d1 100644 --- a/tests/features/environment.py +++ b/tests/features/environment.py @@ -29,6 +29,7 @@ Parameter, Deployment, TransducerObservationBlock, + WellCasingMaterial, ) from db.engine import session_ctx @@ -98,6 +99,20 @@ def add_well(context, session, location, name_num): return well +@add_context_object_container("well_casing_materials") +def add_well_casing_material(context, session, well): + wcm = WellCasingMaterial( + thing_id=well.id, + material="PVC", + ) + session.add(wcm) + session.commit() + session.refresh(wcm) + + context.objects["well_casing_materials"].append(wcm) + return wcm + + @add_context_object_container("springs") def add_spring(context, session, location, name_num): spring = Thing( @@ -214,6 +229,8 @@ def before_all(context): sensor_1 = add_sensor(context, session, well_1.id) deployment = add_deployment(context, session, well_1.id, sensor_1.id) + add_well_casing_material(context, session, well_1) + # parameter ID can be hardcoded because init_parameter always creates the same one parameter = session.get(Parameter, 1) add_obs = add_block(context, session, parameter) @@ -227,6 +244,7 @@ def before_all(context): ) session.add(obs) session.commit() + session.refresh(well_1) def after_all(context): diff --git a/tests/features/steps/well-additional-information.py b/tests/features/steps/well-additional-information.py index d9943c681..e606d95ff 100644 --- a/tests/features/steps/well-additional-information.py +++ b/tests/features/steps/well-additional-information.py @@ -147,7 +147,9 @@ def step_impl(context): @then("the response should include the casing materials") def step_impl(context): assert "well_casing_materials" in context.data - assert context.data["well_casing_materials"] == context.well.well_casing_materials + assert sorted(context.data["well_casing_materials"]) == sorted( + [m.material for m in context.well.well_casing_materials] + ) @then("the response should include the well pump type (previously well_type field)") From 79235515254ad10335be5675f2cba33bd2f8fe18 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Fri, 14 Nov 2025 15:25:17 -0700 Subject: [PATCH 09/33] feat: add 'AquiferSystem' model with relationships and controlled vocabulary fields - Implemented `AquiferSystem` model for master reference of aquifer systems and hydrogeologic units - Added table index for aquifer system name - Included categories and terms for aquifer type and significance level --- core/lexicon.json | 17 ++++++++++++++++- db/aquifer_system.py | 0 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 db/aquifer_system.py diff --git a/core/lexicon.json b/core/lexicon.json index 12df72c94..5b45332d8 100644 --- a/core/lexicon.json +++ b/core/lexicon.json @@ -2,6 +2,7 @@ {"name": "activity_type", "description": null}, {"name": "address_type", "description": null}, {"name": "analysis_method_type", "description": null}, + {"name": "aquifer_type", "description": null}, {"name": "casing_material", "description": null}, {"name": "collection_method", "description": null}, {"name": "well_construction_method", "description": null}, @@ -18,6 +19,7 @@ {"name": "email_type", "description": null}, {"name": "participant_role", "description": null}, {"name": "geochronology", "description": null}, + {"name": "geographic_scale", "description": null}, {"name": "groundwater_level_reason", "description": null}, {"name": "group_type", "description": null}, {"name": "horizontal_datum", "description": null}, @@ -678,6 +680,19 @@ {"categories": ["well_pump_type"], "term": "Submersible", "definition": "Submersible"}, {"categories": ["well_pump_type"], "term": "Jet Pump", "definition": "Jet Pump"}, {"categories": ["well_pump_type"], "term": "Line Shaft", "definition": "Line Shaft"}, - {"categories": ["well_pump_type"], "term": "Hand Pump", "definition": "Hand Pump"} + {"categories": ["well_pump_type"], "term": "Hand Pump", "definition": "Hand Pump"}, + {"categories": ["aquifer_type"], "term": "Artesian", "definition": "Artesian"}, + {"categories": ["aquifer_type"], "term": "Confined, single", "definition": "Confined single aquifer"}, + {"categories": ["aquifer_type"], "term": "Confined, multiple", "definition": "Confined multiple aquifers"}, + {"categories": ["aquifer_type"], "term": "Fractured", "definition": "Fractured"}, + {"categories": ["aquifer_type"], "term": "Mixed", "definition": "Mix of confined and unconfined aquifers"}, + {"categories": ["aquifer_type"], "term": "Perched", "definition": "Perched"}, + {"categories": ["aquifer_type"], "term": "Semi-confined", "definition": "Semi-confined"}, + {"categories": ["aquifer_type"], "term": "Unconfined, single", "definition": "Unconfined single aquifer"}, + {"categories": ["aquifer_type"], "term": "Unconfined, multiple", "definition": "Unconfined multiple aquifers"}, + {"categories": ["aquifer_type"], "term": "Unsaturated", "definition": "Unsaturated (dry)"}, + {"categories": ["geographic_scale"], "term": "Major", "definition": "Major"}, + {"categories": ["geographic_scale"], "term": "Intermediate", "definition": "Regional"}, + {"categories": ["geographic_scale"], "term": "Local", "definition": "Local"} ] } \ No newline at end of file diff --git a/db/aquifer_system.py b/db/aquifer_system.py new file mode 100644 index 000000000..e69de29bb From 4ddec89de36cac67d2122cfae08b721d89ed0983 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Fri, 14 Nov 2025 15:57:59 -0700 Subject: [PATCH 10/33] refactor: remove completed TODO about lexicon updates --- db/aquifer_system.py | 73 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/db/aquifer_system.py b/db/aquifer_system.py index e69de29bb..32da2e42a 100644 --- a/db/aquifer_system.py +++ b/db/aquifer_system.py @@ -0,0 +1,73 @@ +""" +SQLAlchemy model for the AquiferSystem table. + +This is a master reference table for aquifer systems and hydrogeologic units. +""" + +from typing import List, TYPE_CHECKING + +from sqlalchemy import Text, Index +from sqlalchemy.orm import relationship, Mapped, mapped_column +from geoalchemy2 import Geometry + +from db.base import Base, AutoBaseMixin, ReleaseMixin +from db.lexicon import lexicon_term + +if TYPE_CHECKING: + from db.thing import ( + WellScreen, + ThingAquiferAssociation, + ) # TODO: Add ThingAquiferAssociation class/model to Thing model. + + +class AquiferSystem(Base, AutoBaseMixin, ReleaseMixin): + __versioned__ = {} + + name: Mapped[str] = mapped_column( + nullable=False, + unique=True, + comment="The full, human-readable name of the aquifer system (e.g., 'Ogallala Aquifer').", + ) + description: Mapped[str] = mapped_column( + Text, + nullable=True, + comment="A detailed description of the aquifer system, its characteristics, and its significance.", + ) + # Lexicon terms were retrieved from NMAquifer's 'LU_AquiferType' table. + aquifer_type: Mapped[str] = lexicon_term( + nullable=False, + comment="A controlled vocabulary field to classify the aquifer's hydrologic properties (e.g., 'Unconfined', 'Confined', 'Perched').", + ) + geographic_scale: Mapped[str] = lexicon_term( + nullable=False, + comment="A controlled vocabulary field to classify the aquifer's geographic scale (e.g., 'Major', 'Regional', 'Local').", + ) + boundary: Mapped[Geometry] = mapped_column( + Geometry(geometry_type="MULTIPOLYGON", srid=4326, spatial_index=True), + nullable=True, + comment="A spatial representation of the aquifer system's boundary.", + ) + # Hierarchical relationship fields (may be implemented in future iterations) + # Example: High Plains Aquifer (parent) contains Ogallala Aquifer (child) + # parent_id = Column(Integer, ForeignKey('aquifer_system.id')) + # parent = relationship('AquiferSystem', remote_side=[id], backref='subsystems') + + # --- Relationships --- + # One-To-Many: An AquiferSystem can be associated with many wells (Things) via the ThingAquiferAssociation join table. + things: Mapped[List["ThingAquiferAssociation"]] = relationship( + "ThingAquiferAssociation", + back_populates="aquifer_system", + cascade="all, delete-orphan", + passive_deletes=True, + ) + + # One-To-Many: An AquiferSystem can be the target for many individual WellScreens. + well_screens: Mapped[List["WellScreen"]] = relationship( + "WellScreen", + back_populates="aquifer_system", + cascade="all, delete-orphan", + passive_deletes=True, + ) + + # --- Table Arguments --- + __table_args__ = Index("ix_aquifersystem_name", "name") From 29d7b910482c6cde9ce177b12042eb4fc8e5db52 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Fri, 14 Nov 2025 16:37:26 -0700 Subject: [PATCH 11/33] refactor: clarify relationship name and add association proxy Added an association proxy, `things`, to the `AquiferSystem` model. This provides direct access to the `Thing` objects. --- db/aquifer_system.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/db/aquifer_system.py b/db/aquifer_system.py index 32da2e42a..c0710b687 100644 --- a/db/aquifer_system.py +++ b/db/aquifer_system.py @@ -8,16 +8,14 @@ from sqlalchemy import Text, Index from sqlalchemy.orm import relationship, Mapped, mapped_column +from sqlalchemy.ext.associationproxy import association_proxy, AssociationProxy from geoalchemy2 import Geometry from db.base import Base, AutoBaseMixin, ReleaseMixin from db.lexicon import lexicon_term if TYPE_CHECKING: - from db.thing import ( - WellScreen, - ThingAquiferAssociation, - ) # TODO: Add ThingAquiferAssociation class/model to Thing model. + from db.thing import WellScreen, ThingAquiferAssociation, Thing class AquiferSystem(Base, AutoBaseMixin, ReleaseMixin): @@ -54,7 +52,7 @@ class AquiferSystem(Base, AutoBaseMixin, ReleaseMixin): # --- Relationships --- # One-To-Many: An AquiferSystem can be associated with many wells (Things) via the ThingAquiferAssociation join table. - things: Mapped[List["ThingAquiferAssociation"]] = relationship( + thing_associations: Mapped[List["ThingAquiferAssociation"]] = relationship( "ThingAquiferAssociation", back_populates="aquifer_system", cascade="all, delete-orphan", @@ -69,5 +67,11 @@ class AquiferSystem(Base, AutoBaseMixin, ReleaseMixin): passive_deletes=True, ) + # --- Association Proxies --- + # Many-To-Many Proxy: Provides direct access to the Thing objects. + things: AssociationProxy[List["Thing"]] = association_proxy( + "thing_associations", "thing" + ) + # --- Table Arguments --- __table_args__ = Index("ix_aquifersystem_name", "name") From 0d3f807c131aa902e62facc475818ce97b241a80 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Fri, 14 Nov 2025 16:41:50 -0700 Subject: [PATCH 12/33] refactor: clarify proxy purpose doc statement --- db/aquifer_system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/aquifer_system.py b/db/aquifer_system.py index c0710b687..fd121cd7b 100644 --- a/db/aquifer_system.py +++ b/db/aquifer_system.py @@ -68,7 +68,7 @@ class AquiferSystem(Base, AutoBaseMixin, ReleaseMixin): ) # --- Association Proxies --- - # Many-To-Many Proxy: Provides direct access to the Thing objects. + # Proxy to directly access Things (wells) associated with this AquiferSystem. things: AssociationProxy[List["Thing"]] = association_proxy( "thing_associations", "thing" ) From c3393d9abc41b8568fc53b936140dbc7aa6b94f3 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Sun, 16 Nov 2025 13:10:37 -0700 Subject: [PATCH 13/33] feat: add ThingAquiferAssociation model and update Thing relationships - Created ThingAquiferAssociation model to manage many-to-many relationships between Thing and AquiferSystem - Updated Thing model to include aquifer_associations relationship for linking to aquifer systems --- db/thing.py | 9 +++++++ db/thing_aquifer_association.py | 42 +++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 db/thing_aquifer_association.py diff --git a/db/thing.py b/db/thing.py index df0ef8611..03902c866 100644 --- a/db/thing.py +++ b/db/thing.py @@ -39,6 +39,7 @@ from db.sensor import Sensor from db.contact import Contact from db.group import Group, GroupThingAssociation + from db.thing_aquifer_association import ThingAquiferAssociation class Thing( @@ -262,6 +263,14 @@ class Thing( passive_deletes=True, ) + # One-To-Many: A Thing can be associated with many AquiferSystems via the ThingAquiferAssociation join table. + aquifer_associations: Mapped[List["ThingAquiferAssociation"]] = relationship( + "ThingAquiferAssociation", + back_populates="thing", + cascade="all, delete-orphan", + passive_deletes=True, + ) + # --- Association Proxies --- assets: AssociationProxy[list["Asset"]] = association_proxy( "asset_associations", "asset" diff --git a/db/thing_aquifer_association.py b/db/thing_aquifer_association.py new file mode 100644 index 000000000..5f14696ca --- /dev/null +++ b/db/thing_aquifer_association.py @@ -0,0 +1,42 @@ +""" +SQLAlchemy model for the ThingAquiferAssociation table. + +This table is a join table (or "association object") whose purpose is to manage +the many-to-many relationship between a Thing and an AquiferSystem. +""" + +from typing import TYPE_CHECKING + +from sqlalchemy import ForeignKey + +from sqlalchemy.orm import relationship, Mapped, mapped_column + +from db.base import Base, AutoBaseMixin, ReleaseMixin + +if TYPE_CHECKING: + from db.thing import Thing + from db.aquifer_system import AquiferSystem + + +class ThingAquiferAssociation(Base, AutoBaseMixin, ReleaseMixin): + """ + Represents the association of a Thing to an AquiferSystem. This is an Association Object. + """ + + thing_id: Mapped[int] = mapped_column( + ForeignKey("thing.id", ondelete="CASCADE"), nullable=False + ) + aquifer_system_id: Mapped[int] = mapped_column( + ForeignKey("aquifer_system.id", ondelete="CASCADE"), nullable=False + ) + + # --- Relationship Definitions --- + # Many-To-One: This association links to one Thing. + thing: Mapped["Thing"] = relationship( + "Thing", back_populates="aquifer_associations" + ) + + # Many-To-One: This association links to one AquiferSystem. + aquifer_system: Mapped["AquiferSystem"] = relationship( + "AquiferSystem", back_populates="thing_associations" + ) From af092ddac74090a56caceb5995e832f705a6d3a6 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Sun, 16 Nov 2025 13:15:55 -0700 Subject: [PATCH 14/33] feat: add `aqufiers` proxy to Thing model Added an `aquifers` proxy to the Thing model to directly access the AqufierSystems associated with a particular Thing. --- db/thing.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/db/thing.py b/db/thing.py index 03902c866..246d9536b 100644 --- a/db/thing.py +++ b/db/thing.py @@ -39,6 +39,7 @@ from db.sensor import Sensor from db.contact import Contact from db.group import Group, GroupThingAssociation + from db.aquifer_system import AquiferSystem from db.thing_aquifer_association import ThingAquiferAssociation @@ -296,6 +297,11 @@ class Thing( "group_associations", "group" ) + # Proxy to directly access AquiferSystems associated with this Thing + aquifers: AssociationProxy[List["AquiferSystem"]] = association_proxy( + "aquifer_associations", "aquifer_system" + ) + # Full-text search vector search_vector = Column(TSVectorType("name", "well_construction_notes")) From e330df46125468efa93fcff0d7f578520ce90a04 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Sun, 16 Nov 2025 13:55:39 -0700 Subject: [PATCH 15/33] feat: Link WellScreen to AquiferSystem This commit addresses the need to handle scenarios where multiple well screens may target different aquifers. Added the aquifer_system_id foreign key to the WellScreen model. This creates a direct, physical link between a screened interval and the specific hydrogeologic unit it monitors. --- db/thing.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/db/thing.py b/db/thing.py index 246d9536b..c43064fe0 100644 --- a/db/thing.py +++ b/db/thing.py @@ -346,6 +346,9 @@ class WellScreen(Base, AutoBaseMixin, ReleaseMixin): thing_id: Mapped[int] = mapped_column( ForeignKey("thing.id", ondelete="CASCADE"), nullable=False ) + aquifer_system_id: Mapped[int] = mapped_column( + ForeignKey("aquifer_system.id", ondelete="SET NULL"), nullable=True + ) screen_depth_top: Mapped[float] = mapped_column( info={"unit": "feet below ground surface"}, nullable=True ) @@ -363,6 +366,10 @@ class WellScreen(Base, AutoBaseMixin, ReleaseMixin): # Many-To-One: A WellScreen belongs to one Thing. thing: Mapped["Thing"] = relationship("Thing", back_populates="screens") + aquifer_system: Mapped["AquiferSystem"] = relationship( + "AquiferSystem", back_populates="well_screens", passive_deletes=True + ) + class WellPurpose(Base, AutoBaseMixin, ReleaseMixin): """ From 04a28be8849cdb4e77591816595cbb4fae53f2a5 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Mon, 17 Nov 2025 11:36:55 -0700 Subject: [PATCH 16/33] feat: add GeologicFormation and ThingFormationAssociation models with Thing relationship updates - Introduced GeologicFormation model as a master reference for geologic formations - Created ThingFormationAssociation model to manage many-to-many relationships between Thing and GeologicFormation, including depth interval fields - Updated Thing model to include formation_associations relationship and association proxy for direct access to related geologic formations --- db/geologic_formation.py | 73 +++++++++++++++++++++++++++++++ db/thing.py | 15 +++++++ db/thing_formation_association.py | 60 +++++++++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 db/geologic_formation.py create mode 100644 db/thing_formation_association.py diff --git a/db/geologic_formation.py b/db/geologic_formation.py new file mode 100644 index 000000000..a0119d50c --- /dev/null +++ b/db/geologic_formation.py @@ -0,0 +1,73 @@ +""" +SQLAlchemy model for the GeologicFormation table. + +This table is a master reference table for geologic formations. Its purpose is to store definitions and descriptions +of various geologic formations that can be referenced by other tables in the database. +""" + +from typing import List, TYPE_CHECKING + +from sqlalchemy import Text, Index +from sqlalchemy.orm import relationship, Mapped, mapped_column +from sqlalchemy.ext.associationproxy import association_proxy, AssociationProxy +from geoalchemy2 import Geometry + +from db.base import Base, AutoBaseMixin, ReleaseMixin +from db.lexicon import lexicon_term + +if TYPE_CHECKING: + from db.thing import Thing + from db.thing_formation_association import ThingFormationAssociation + + +class GeologicFormation(Base, AutoBaseMixin, ReleaseMixin): + __versioned__ = {} + + # TODO: Should `name` use a controlled vocabulary? + name: Mapped[str] = mapped_column( + nullable=False, + unique=True, + comment="The full, human-readable name of the geologic formation (e.g., 'Navajo Sandstone').", + ) + # TODO: Implement controlled vocabulary for `code` using the `LU_Formation` table from NMAquifer. + code: Mapped[str] = lexicon_term( + nullable=True, + unique=True, + comment="A short code or abbreviation for the geologic formation (e.g., '120ELRT').", + ) + description: Mapped[str] = mapped_column( + Text, + nullable=True, + comment="A detailed description of the geologic formation, its characteristics, and its significance.", + ) + # TODO: Implement controlled vocabularies for `lithology` using NMAquifer's 'LU_Lithology' table. + lithology: Mapped[str] = lexicon_term( + nullable=True, + comment="A controlled vocabulary for the primary, dominant rock type" + "(e.g., 'Tuff', 'Sandstone', 'Alluvium', 'Shale').", + ) + boundary: Mapped[Geometry] = mapped_column( + Geometry(geometry_type="MULTIPOLYGON", srid=4326, spatial_index=True), + nullable=True, + comment="A spatial representation of the geologic formation's extent.", + ) + + # --- Relationships --- + # One-To-Many (Association Object): A GeologicFormation can be associated with many Things (e.g., wells) via the + # ThingFormationAssociation join table. + thing_associations: Mapped[List["ThingFormationAssociation"]] = relationship( + "ThingFormationAssociation", + back_populates="geologic_formation", + cascade="all, delete-orphan", + passive_deletes=True, + ) + + # --- Association Proxies --- + # Provides direct access to Things (wells) that penetrate this formation. + things: AssociationProxy["Thing"] = association_proxy("thing_associations", "thing") + + # --- Table Arguments --- + __table_args__ = ( + Index("ix_geologicformation_name", "name"), + Index("ix_geologicformation_code", "code"), + ) diff --git a/db/thing.py b/db/thing.py index c43064fe0..b5a2a284a 100644 --- a/db/thing.py +++ b/db/thing.py @@ -41,6 +41,8 @@ from db.group import Group, GroupThingAssociation from db.aquifer_system import AquiferSystem from db.thing_aquifer_association import ThingAquiferAssociation + from db.geologic_formation import GeologicFormation + from db.thing_formation_association import ThingFormationAssociation class Thing( @@ -272,6 +274,14 @@ class Thing( passive_deletes=True, ) + # Many-To-Many: A Thing can penetrate many GeologicFormations. + formation_associations: Mapped[List["ThingFormationAssociation"]] = relationship( + "ThingFormationAssociation", + back_populates="thing", + cascade="all, delete-orphan", + passive_deletes=True, + ) + # --- Association Proxies --- assets: AssociationProxy[list["Asset"]] = association_proxy( "asset_associations", "asset" @@ -302,6 +312,11 @@ class Thing( "aquifer_associations", "aquifer_system" ) + # Proxy to directly access the GeologicFormations penetrated by this Thing. + formations: AssociationProxy[List["GeologicFormation"]] = association_proxy( + "formation_associations", "geologic_formation" + ) + # Full-text search vector search_vector = Column(TSVectorType("name", "well_construction_notes")) diff --git a/db/thing_formation_association.py b/db/thing_formation_association.py new file mode 100644 index 000000000..c5f0f9592 --- /dev/null +++ b/db/thing_formation_association.py @@ -0,0 +1,60 @@ +""" +SQLAlchemy model for the ThingFormationAssociation table. + +This table is an association object that creates a many-to-many relationship between a Thing (well) and a +GeologicFormation. It stores the lithology for a well, detailing the depth intervals for each formation it penetrates. +""" + +from typing import TYPE_CHECKING + +from sqlalchemy import ForeignKey +from sqlalchemy.orm import relationship, Mapped, mapped_column + +from db.base import Base, AutoBaseMixin, ReleaseMixin + +if TYPE_CHECKING: + from db.thing import Thing + from db.geologic_formation import GeologicFormation + + +class ThingFormationAssociation(Base, AutoBaseMixin, ReleaseMixin): + """ + This is a= join table (Association Object). It represents the association of a Thing to a + GeologicFormation at a specific depth interval. + """ + + # --- Foreign Keys --- + thing_id: Mapped[int] = mapped_column( + ForeignKey("thing.id", ondelete="CASCADE"), + nullable=False, + comment="The foreign key linking this record to the `Thing` table." + "Deleting a `Thing` will cascade and delete its formation log.", + ) + geologic_formation_id: Mapped[int] = mapped_column( + ForeignKey("geologic_formation.id", ondelete="SET NULL"), + nullable=True, + comment="The foreign key linking this record to the `GeologicFormation` table." + "This is set to `SET NULL` on delete, as deleting a formation definition (a rare admin action)" + "should not delete the historical fact that a well had a pick at this depth.", + ) + + # Depth interval fields + top_depth: Mapped[float] = mapped_column( + nullable=False, + comment="The depth (in feet) to the top of the geologic formation, as measured from ground surface.", + ) + bottom_depth: Mapped[float] = mapped_column( + nullable=False, + comment="The depth (in feet) to the bottom of the geologic formation, as measured from ground surface.", + ) + + # --- Relationship Definitions --- + # Many-To-One: This association links to one Thing. + thing: Mapped["Thing"] = relationship( + "Thing", back_populates="formation_associations" + ) + + # Many-To-One: This association links to one GeologicFormation. + geologic_formation: Mapped["GeologicFormation"] = relationship( + "GeologicFormation", back_populates="thing_associations" + ) From 477247a66165e9987b8709806d0d7326d4e65926 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Mon, 17 Nov 2025 12:35:55 -0700 Subject: [PATCH 17/33] feat(lexicon): add "formation_code" category and associated terms to lexicon --- core/lexicon.json | 302 ++++++++++++++++++++++++++++++++++++++- db/geologic_formation.py | 5 +- 2 files changed, 302 insertions(+), 5 deletions(-) diff --git a/core/lexicon.json b/core/lexicon.json index 5b45332d8..6d980ab43 100644 --- a/core/lexicon.json +++ b/core/lexicon.json @@ -52,7 +52,8 @@ {"name": "well_purpose", "description": null}, {"name": "status_type", "description": null}, {"name": "status_value", "description": null}, - {"name": "well_pump_type", "description": null} + {"name": "well_pump_type", "description": null}, + {"name": "formation_code", "description": null} ], "terms": [ {"categories": ["review_status"], "term": "approved", "definition": "approved"}, @@ -675,7 +676,7 @@ {"categories": ["monitoring_frequency"], "term": "Biannual", "definition": "Location is monitored twice a year."}, {"categories": ["monitoring_frequency"], "term": "Annual", "definition": "Location is monitored once a year."}, {"categories": ["monitoring_frequency"], "term": "Decadal", "definition": "Location is monitored once every ten years."}, - {"categories": ["monitoring_frequency"], "term": "Event-based", "definition": "Location is monitored based on specific events or triggers rather than a fixed schedule."} + {"categories": ["monitoring_frequency"], "term": "Event-based", "definition": "Location is monitored based on specific events or triggers rather than a fixed schedule."}, {"categories": ["sensor_status"], "term": "Lost", "definition": "Lost"}, {"categories": ["well_pump_type"], "term": "Submersible", "definition": "Submersible"}, {"categories": ["well_pump_type"], "term": "Jet Pump", "definition": "Jet Pump"}, @@ -693,6 +694,301 @@ {"categories": ["aquifer_type"], "term": "Unsaturated", "definition": "Unsaturated (dry)"}, {"categories": ["geographic_scale"], "term": "Major", "definition": "Major"}, {"categories": ["geographic_scale"], "term": "Intermediate", "definition": "Regional"}, - {"categories": ["geographic_scale"], "term": "Local", "definition": "Local"} + {"categories": ["geographic_scale"], "term": "Local", "definition": "Local"}, + {"categories": ["formation_code"],"term": "000EXRV","definition": "Extrusive Rocks"}, +{"categories": ["formation_code"],"term": "000IRSV","definition": "Intrusive Rocks"}, +{"categories": ["formation_code"],"term": "050QUAL","definition": "Quaternary Alluvium in Valleys"}, +{"categories": ["formation_code"],"term": "100QBAS","definition": "Quaternary basalt"}, +{"categories": ["formation_code"],"term": "110ALVM","definition": "Quaternary Alluvium"}, +{"categories": ["formation_code"],"term": "110AVMB","definition": "Alluvium, Bolson Deposits and Other Surface Deposits"}, +{"categories": ["formation_code"],"term": "110BLSN","definition": "Bolson Fill"}, +{"categories": ["formation_code"],"term": "110NTGU","definition": "Naha and Tsegi Alluvium Deposits, undifferentiated"}, +{"categories": ["formation_code"],"term": "110PTODC","definition": "Pediment, Terrace and Other Deposits of Gravel, Sand and Caliche"}, +{"categories": ["formation_code"],"term": "111MCCR","definition": "McCathys Basalt Flow"}, +{"categories": ["formation_code"],"term": "112ANCH","definition": "Upper Santa Fe Group, Ancha Formation (QTa)"}, +{"categories": ["formation_code"],"term": "112CURB","definition": "Cuerbio Basalt"}, +{"categories": ["formation_code"],"term": "112LAMA","definition": "Lama Formation (QTl, QTbh) and other mountain front alluvial fans"}, +{"categories": ["formation_code"],"term": "112LAMAb","definition": "Lama Fm (QTl, QTbh) between Servilleta Basalts"}, +{"categories": ["formation_code"],"term": "112LGUN","definition": "Laguna Basalt Flow"}, +{"categories": ["formation_code"],"term": "112QTBF","definition": "Quaternary-Tertiary basin fill (not in valleys)"}, +{"categories": ["formation_code"],"term": "112QTBFlac","definition": "Quaternary-Tertiary basin fill, lacustrian-playa lithofacies"}, +{"categories": ["formation_code"],"term": "112QTBFpd","definition": "Quaternary-Tertiary basin fill, distal piedmont lithofacies"}, +{"categories": ["formation_code"],"term": "112QTBFppm","definition": "Quaternary-Tertiary basin fill, proximal and medial piedmont lithofacies"}, +{"categories": ["formation_code"],"term": "112SNTF","definition": "Santa Fe Group, undivided"}, +{"categories": ["formation_code"],"term": "112SNTFA","definition": "Upper Santa Fe Group, axial facies"}, +{"categories": ["formation_code"],"term": "112SNTFOB","definition": "Upper SantaFe Group, Loma Barbon member of Arroyo Ojito Formatin"}, +{"categories": ["formation_code"],"term": "112SNTFP","definition": "Upper Santa Fe Group, piedmont facies"}, +{"categories": ["formation_code"],"term": "112TRTO","definition": "Tuerto Gravels (QTt)"}, +{"categories": ["formation_code"],"term": "120DTIL","definition": "Datil Formation"}, +{"categories": ["formation_code"],"term": "120ELRT","definition": "El Rito Formation"}, +{"categories": ["formation_code"],"term": "120IRSV","definition": "Tertiary Intrusives"}, +{"categories": ["formation_code"],"term": "120SBLC","definition": "Sierra Blanca Volcanics, undivided"}, +{"categories": ["formation_code"],"term": "120SRVB","definition": "Tertiary Servilletta Basalts (Tsb)"}, +{"categories": ["formation_code"],"term": "120SRVBf","definition": "Tertiary Servilletta Basalts, fractured (Tsbf)"}, +{"categories": ["formation_code"],"term": "120TSBV_Lower","definition": "Tertiary Sierra Blanca area lower volcanic unit (Hog Pen Fm)"}, +{"categories": ["formation_code"],"term": "120TSBV_Upper","definition": "Tertiary Sierra Blanca area upper volcanic unit (above Hog Pen Fm)"}, +{"categories": ["formation_code"],"term": "121CHMT","definition": "Chamita Formation (Tc)"}, +{"categories": ["formation_code"],"term": "121CHMTv","definition": "Chamita Fm, Vallito member (Tcv)"}, +{"categories": ["formation_code"],"term": "121CHMTvs","definition": "Chamita Fm, sandy Vallito member (Tcvs)"}, +{"categories": ["formation_code"],"term": "121OGLL","definition": "Ogallala Formation"}, +{"categories": ["formation_code"],"term": "121PUYEF","definition": "Puye Conglomerate, Fanglomerate Member"}, +{"categories": ["formation_code"],"term": "121TSUQ","definition": "Tesuque Formation, undifferentiated unit"}, +{"categories": ["formation_code"],"term": "121TSUQa","definition": "Tesuque Fm lithosome A (Tta)"}, +{"categories": ["formation_code"],"term": "121TSUQacu","definition": "Tesuque Fm (upper), Cuarteles member lithosome A (Ttacu)"}, +{"categories": ["formation_code"],"term": "121TSUQacuf","definition": "Tesuque Fm (upper), fine-grained Cuarteles member lithosome A (Ttacuf)"}, +{"categories": ["formation_code"],"term": "121TSUQaml","definition": "Tesuque Fm lower-middle lithosome A (Ttaml)"}, +{"categories": ["formation_code"],"term": "121TSUQb","definition": "Tesuque Fm lithosome B (Ttb)"}, +{"categories": ["formation_code"],"term": "121TSUQbfl","definition": "Tesuque Fm lower lithosome B, basin-floor deposits (Ttbfl)"}, +{"categories": ["formation_code"],"term": "121TSUQbfm","definition": "Tesuque Fm middle lithosome B, basin-floor deposits (Ttbfm)"}, +{"categories": ["formation_code"],"term": "121TSUQbp","definition": "Tesuque Fm lithosome B, Pojoaque member (Ttbp)"}, +{"categories": ["formation_code"],"term": "121TSUQce","definition": "Tesuque Fm, Cejita member (Ttce)"}, +{"categories": ["formation_code"],"term": "121TSUQe","definition": "Tesuque Fm lithosome E (Tte)"}, +{"categories": ["formation_code"],"term": "121TSUQs","definition": "Tesuque Fm lithosome S (Tts)"}, +{"categories": ["formation_code"],"term": "121TSUQsa","definition": "Tesuque Fm lateral gradation lithosomes S and A (Ttsag)"}, +{"categories": ["formation_code"],"term": "121TSUQsc","definition": "Tesuque Fm coarse-grained lithosome S (Ttsc)"}, +{"categories": ["formation_code"],"term": "121TSUQsf","definition": "Tesuque Fm, fine-grained lithosome S (Ttsf)"}, +{"categories": ["formation_code"],"term": "122CHOC","definition": "Chamita and Ojo Caliente interlayered (Ttoc)"}, +{"categories": ["formation_code"],"term": "122CRTO","definition": "Chama El Rito Formation (Tesuque member, Ttc)"}, +{"categories": ["formation_code"],"term": "122OJOC","definition": "Ojo Caliente Formation (Tesuque member, Tto)"}, +{"categories": ["formation_code"],"term": "122PICR","definition": "Picuris Tuff"}, +{"categories": ["formation_code"],"term": "122PPTS","definition": "Popotosa Formation"}, +{"categories": ["formation_code"],"term": "122SNTFP","definition": "Lower Santa Fe Group, piedmont facies"}, +{"categories": ["formation_code"],"term": "123DTILSPRS","definition": "Datil Group ignimbrites and lavas and Spears Group, interbedded"}, +{"categories": ["formation_code"],"term": "123DTMGandbas","definition": "Datil and Mogollon Group andesite, basaltic andesite, and basalt flows"}, +{"categories": ["formation_code"],"term": "123DTMGign","definition": "Datil and Mogollon Group ignimbrites"}, +{"categories": ["formation_code"],"term": "123DTMGrhydac","definition": "Datil and Mogollon Group rhyolite and dacite flows"}, +{"categories": ["formation_code"],"term": "123ESPN","definition": "T Espinaso Formation (Te)"}, +{"categories": ["formation_code"],"term": "123GLST","definition": "T Galisteo Formation"}, +{"categories": ["formation_code"],"term": "123PICS","definition": "T Picuris Formation (Tp)"}, +{"categories": ["formation_code"],"term": "123PICSc","definition": "T Picuris Formation, basal conglomerate (Tpc)"}, +{"categories": ["formation_code"],"term": "123PICSl","definition": "T lower Picuris Formation (Tpl)"}, +{"categories": ["formation_code"],"term": "123SPRSDTMGlava","definition": "Spears Group and Datil-Mogollon intermediate-mafic lavas, interbedded"}, +{"categories": ["formation_code"],"term": "123SPRSlower","definition": "Spears Group, lower part; tuffaceous, gravelly debris and mud flows"}, +{"categories": ["formation_code"],"term": "123SPRSmid_uppe","definition": "Spears Group, middle-upper part; excludes Dog Spring Formation"}, +{"categories": ["formation_code"],"term": "124BACA","definition": "Baca Formation"}, +{"categories": ["formation_code"],"term": "124CBMN","definition": "Cub Mountain Formation"}, +{"categories": ["formation_code"],"term": "124LLVS","definition": "Llaves Member of San Jose Formation"}, +{"categories": ["formation_code"],"term": "124PSCN","definition": "Poison Canyon Formation"}, +{"categories": ["formation_code"],"term": "124RGIN","definition": "Regina Member of San Jose Formation"}, +{"categories": ["formation_code"],"term": "124SNJS","definition": "San Jose Formation"}, +{"categories": ["formation_code"],"term": "124TPCS","definition": "TapicitosMember of San Jose Formation"}, +{"categories": ["formation_code"],"term": "125NCMN","definition": "Nacimiento Formation"}, +{"categories": ["formation_code"],"term": "125NCMNS","definition": "Nacimiento Formation, Sandy Shale Facies"}, +{"categories": ["formation_code"],"term": "125RTON","definition": "Raton Formation"}, +{"categories": ["formation_code"],"term": "130CALDFLOOR","definition": "Caldera Floor bedrock S. of San Agustin Plains. Mostly DTILSPRS & Paleo."}, +{"categories": ["formation_code"],"term": "180TKSCC_Upper","definition": "Tertiary-Cretaceous, Sanders Canyon, Cub Mtn. and upper Crevasse Canyon Fm"}, +{"categories": ["formation_code"],"term": "180TKTR","definition": "Tertiary-Cretaceous-Triassic, Baca, Crevasse Cyn, Gallup, Mancos, Dakota, T"}, +{"categories": ["formation_code"],"term": "210CRCS","definition": "Cretaceous System, undivided"}, +{"categories": ["formation_code"],"term": "210GLUPC_Lower","definition": "K Gallup Sandstone and lower Crevasse Canyon Fm"}, +{"categories": ["formation_code"],"term": "210HOSTD","definition": "K Hosta Dalton"}, +{"categories": ["formation_code"],"term": "210MCDK","definition": "K Mancos/Dakota undivided"}, +{"categories": ["formation_code"],"term": "210MNCS","definition": "Mancos Shale, undivided"}, +{"categories": ["formation_code"],"term": "210MNCSL","definition": "K Lower Mancos"}, +{"categories": ["formation_code"],"term": "210MNCSU","definition": "K Upper Mancos"}, +{"categories": ["formation_code"],"term": "211CLFHV","definition": "Cliff House Sandstone, includes La Ventana Tongues in NW Sandoval Co."}, +{"categories": ["formation_code"],"term": "211CRLL","definition": "Carlile Shale"}, +{"categories": ["formation_code"],"term": "211CRVC","definition": "Crevasse Canyon Formation of Mesaverde Group"}, +{"categories": ["formation_code"],"term": "211DKOT","definition": "Dakota Sandstone or Formation"}, +{"categories": ["formation_code"],"term": "211DLCO","definition": "Dilco Coal Member of Crevasse Canyon Formation of Mesaverde Group"}, +{"categories": ["formation_code"],"term": "211DLTN","definition": "Dalton Sandstone Member of Crevasse Canyon Formation of Mesaverde Group"}, +{"categories": ["formation_code"],"term": "211FRHS","definition": "Fort Hays Limestone Member of Niobrara Formation"}, +{"categories": ["formation_code"],"term": "211FRLD","definition": "Fruitland Formation"}, +{"categories": ["formation_code"],"term": "211FRMG","definition": "Farmington Sandstone Member of Kirtland Shale"}, +{"categories": ["formation_code"],"term": "211GBSNC","definition": "Gibson Coal Member of Crevasse Canyon Formation of Mesaverde Group"}, +{"categories": ["formation_code"],"term": "211GLLG","definition": "Gallego Sandstone Member of Gallup Sandstone"}, +{"categories": ["formation_code"],"term": "211GLLP","definition": "Gallup Sandstone"}, +{"categories": ["formation_code"],"term": "211GRRG","definition": "Greenhorn and Graneros Formations"}, +{"categories": ["formation_code"],"term": "211GRRS","definition": "Graneros Shale"}, +{"categories": ["formation_code"],"term": "211HOST","definition": "Hosta Tongue of Point Lookout Sandstone of Mesaverde Group"}, +{"categories": ["formation_code"],"term": "211KRLD","definition": "Kirtland Shale"}, +{"categories": ["formation_code"],"term": "211LWIS","definition": "Lewis Shale"}, +{"categories": ["formation_code"],"term": "211MENF","definition": "Menefee Formation"}, +{"categories": ["formation_code"],"term": "211MENFU","definition": "K Upper Menefee (above Harmon Sandstone)"}, +{"categories": ["formation_code"],"term": "211MVRD","definition": "Mesaverde Group"}, +{"categories": ["formation_code"],"term": "211OJAM","definition": "Ojo Alamo Sandstone"}, +{"categories": ["formation_code"],"term": "211PCCF","definition": "Pictured Cliffs Sandstone"}, +{"categories": ["formation_code"],"term": "211PIRR","definition": "Pierre Shale"}, +{"categories": ["formation_code"],"term": "211PNLK","definition": "Point Lookout Sandstone"}, +{"categories": ["formation_code"],"term": "211SMKH","definition": "Smoky Hill Marl Member"}, +{"categories": ["formation_code"],"term": "211TLLS","definition": "Twowells Sandstone Lentil of Pike of Dakota Sandstone"}, +{"categories": ["formation_code"],"term": "212KTRP","definition": "K Dakota Sandstone, Moenkopi Fm, Artesia Group"}, +{"categories": ["formation_code"],"term": "217PRGR","definition": "Purgatoire Formation"}, +{"categories": ["formation_code"],"term": "220ENRD","definition": "Entrada Sandstone"}, +{"categories": ["formation_code"],"term": "220JURC","definition": "Jurassic undivided"}, +{"categories": ["formation_code"],"term": "220NAVJ","definition": "Navajo Sandstone"}, +{"categories": ["formation_code"],"term": "221BLFF","definition": "Bluff Sandstone of Morrison Formation"}, +{"categories": ["formation_code"],"term": "221CSPG","definition": "Cow Springs Sandstone of Morrison Formation"}, +{"categories": ["formation_code"],"term": "221ERADU","definition": "Entrada Sandstone of San Rafael Group, Upper"}, +{"categories": ["formation_code"],"term": "221MRSN","definition": "Morrison Formation"}, +{"categories": ["formation_code"],"term": "221MRSN/BBSN","definition": "Brushy Basin Member of Morrison"}, +{"categories": ["formation_code"],"term": "221MRSN/JCKP","definition": "Jackpile Sandstone Member of Morrison"}, +{"categories": ["formation_code"],"term": "221MRSN/RCAP","definition": "Recapture Shale Member of Morrison"}, +{"categories": ["formation_code"],"term": "221MRSN/WWCN","definition": "Westwater Canyon Member of Morrison"}, +{"categories": ["formation_code"],"term": "221SLWS","definition": "Salt Wash Sandstone Member of Morrison Formation"}, +{"categories": ["formation_code"],"term": "221SMVL","definition": "Summerville Formation of San Rafael Group"}, +{"categories": ["formation_code"],"term": "221TDLT","definition": "J Todilto"}, +{"categories": ["formation_code"],"term": "221WSRC","definition": "Westwater Canyon Sandstone Member of Morrison Formation"}, +{"categories": ["formation_code"],"term": "221ZUNIS","definition": "Zuni Sandstone"}, +{"categories": ["formation_code"],"term": "231AGZC","definition": "Tr Agua Zarca"}, +{"categories": ["formation_code"],"term": "231AGZCU","definition": "Tr Upper Agua Zarca"}, +{"categories": ["formation_code"],"term": "231CHNL","definition": "Chinle Formation"}, +{"categories": ["formation_code"],"term": "231CORR","definition": "Correo Sandstone Member of Chinle Formation"}, +{"categories": ["formation_code"],"term": "231DCKM","definition": "Dockum Group"}, +{"categories": ["formation_code"],"term": "231PFDF","definition": "Tr Petrified Forest"}, +{"categories": ["formation_code"],"term": "231PFDFL","definition": "Tr Lower Petrified Forest (below middle sandstone)"}, +{"categories": ["formation_code"],"term": "231PFDFM","definition": "Tr Middle Petrified Forest sandstone"}, +{"categories": ["formation_code"],"term": "231PFDFU","definition": "Tr Upper Petrified Forest (above middle sandstone)"}, +{"categories": ["formation_code"],"term": "231RCKP","definition": "Rock Point Member of Wingate Sandstone"}, +{"categories": ["formation_code"],"term": "231SNRS","definition": "Santa Rosa Sandstone"}, +{"categories": ["formation_code"],"term": "231SNSL","definition": "Sonsela Sandstone Bed of Petrified Forest Member of Chinle Formation"}, +{"categories": ["formation_code"],"term": "231SRMP","definition": "Shinarump Member of Chinle Formation"}, +{"categories": ["formation_code"],"term": "231WNGT","definition": "Wingate Sandstone"}, +{"categories": ["formation_code"],"term": "260SNAN","definition": "P San Andres"}, +{"categories": ["formation_code"],"term": "260SNAN_lower","definition": "Lower San Andres Formation"}, +{"categories": ["formation_code"],"term": "261SNGL","definition": "P San Andres - Glorieta Sandstone in Rio Bonito member"}, +{"categories": ["formation_code"],"term": "300YESO","definition": "P Yeso"}, +{"categories": ["formation_code"],"term": "300YESO_lower","definition": "Lower Yeso Formation"}, +{"categories": ["formation_code"],"term": "300YESO_upper","definition": "Upper Yeso Formation"}, +{"categories": ["formation_code"],"term": "310ABO","definition": "P Abo"}, +{"categories": ["formation_code"],"term": "310DCLL","definition": "De Chelly Sandstone Member of Cutler Formation"}, +{"categories": ["formation_code"],"term": "310GLOR","definition": "Glorieta Sandstone Member of San Andres Formation (of Manzano Group)"}, +{"categories": ["formation_code"],"term": "310MBLC","definition": "Meseta Blanca Sandstone Member of Yeso Formation"}, +{"categories": ["formation_code"],"term": "310TRRS","definition": "Torres Member of Yeso Formation"}, +{"categories": ["formation_code"],"term": "310YESO","definition": "Yeso Formation"}, +{"categories": ["formation_code"],"term": "310YESOG","definition": "Yeso Formation, Manzono Group"}, +{"categories": ["formation_code"],"term": "312CSTL","definition": "Castile Formation"}, +{"categories": ["formation_code"],"term": "312RSLR","definition": "Rustler Formation"}, +{"categories": ["formation_code"],"term": "313ARTS","definition": "Artesia Group"}, +{"categories": ["formation_code"],"term": "313BLCN","definition": "Bell Canyon Formation"}, +{"categories": ["formation_code"],"term": "313BRUC","definition": "Brushy Canyon Formation of Delaware Mountain Group"}, +{"categories": ["formation_code"],"term": "313CKBF","definition": "Chalk Bluff Formation"}, +{"categories": ["formation_code"],"term": "313CLBD","definition": "Carlsbad Limestone"}, +{"categories": ["formation_code"],"term": "313CPTN","definition": "Capitan Limestone"}, +{"categories": ["formation_code"],"term": "313GDLP","definition": "Guadalupian Series"}, +{"categories": ["formation_code"],"term": "313GOSP","definition": "Goat Seep Dolomite"}, +{"categories": ["formation_code"],"term": "313SADG","definition": "San Andres Limestone and Glorieta Sandstone"}, +{"categories": ["formation_code"],"term": "313SADR","definition": "San Andres Limestone, undivided"}, +{"categories": ["formation_code"],"term": "313TNSL","definition": "Tansill Formation"}, +{"categories": ["formation_code"],"term": "313YATS","definition": "Yates Formation, Guadalupe Group"}, +{"categories": ["formation_code"],"term": "315LABR","definition": "P Laborcita (Bursum)"}, +{"categories": ["formation_code"],"term": "315YESOABO","definition": "Alamosa Creek and San Agustin Plains area - Yeso and Abo Formations"}, +{"categories": ["formation_code"],"term": "318ABO","definition": "P Abo"}, +{"categories": ["formation_code"],"term": "318BSPG","definition": "Bone Spring Limestone"}, +{"categories": ["formation_code"],"term": "318JOYT","definition": "Joyita Sandstone Member of Yeso Formation"}, +{"categories": ["formation_code"],"term": "318YESO","definition": "Yeso Formation"}, +{"categories": ["formation_code"],"term": "319BRSM","definition": "Bursum Formation and Equivalent Rocks"}, +{"categories": ["formation_code"],"term": "320HLDR","definition": "Penn Holder"}, +{"categories": ["formation_code"],"term": "320PENN","definition": "Pennsylvanian undivided"}, +{"categories": ["formation_code"],"term": "320SNDI","definition": "Sandia Formation"}, +{"categories": ["formation_code"],"term": "321SGDC","definition": "Sangre de Cristo Formation"}, +{"categories": ["formation_code"],"term": "322BEMN","definition": "Penn Beeman"}, +{"categories": ["formation_code"],"term": "325GBLR","definition": "Penn Gobbler"}, +{"categories": ["formation_code"],"term": "325MDER","definition": "Madera Limestone, undivided"}, +{"categories": ["formation_code"],"term": "325MDERL","definition": "Penn Lower Madera"}, +{"categories": ["formation_code"],"term": "325MDERU","definition": "Penn Upper Madera"}, +{"categories": ["formation_code"],"term": "325SAND","definition": "Penn Sandia"}, +{"categories": ["formation_code"],"term": "326MGDL","definition": "Magdalena Group"}, +{"categories": ["formation_code"],"term": "340EPRS","definition": "Espiritu Santo Formation"}, +{"categories": ["formation_code"],"term": "350PZBA","definition": "Alamosa Creek and San Agustin Plains area - Paleozoic strata beneath Abo Fm"}, +{"categories": ["formation_code"],"term": "350PZBB","definition": "Tul Basin area - Paleozoic strata below Bursum Fm"}, +{"categories": ["formation_code"],"term": "400EMBD","definition": "Embudo Granite (undifferentiated PreCambrian near Santa Fe)"}, +{"categories": ["formation_code"],"term": "400PCMB","definition": "Precambrian Erathem"}, +{"categories": ["formation_code"],"term": "400PREC","definition": "undifferentiated PreCambrian crystalline rocks (X)"}, +{"categories": ["formation_code"],"term": "400PRECintr","definition": "PreCambrian crystalline rocks and local Tertiary intrusives"}, +{"categories": ["formation_code"],"term": "400PRST","definition": "Priest Granite"}, +{"categories": ["formation_code"],"term": "400TUSS","definition": "Tusas Granite"}, +{"categories": ["formation_code"],"term": "410PRCG","definition": "PreCambrian granite (Xg)"}, +{"categories": ["formation_code"],"term": "410PRCGf","definition": "PreCambrian granite, fractured (Xgf)"}, +{"categories": ["formation_code"],"term": "410PRCQ","definition": "PreCambrian quartzite (Xq)"}, +{"categories": ["formation_code"],"term": "410PRCQf","definition": "PreCambrian quartzite, fractured (Xqf)"}, +{"categories": ["formation_code"],"term": "121GILA","definition": "Gila Conglomerate (group)"}, +{"categories": ["formation_code"],"term": "312DYLK","definition": "Dewey Lake Redbeds"}, +{"categories": ["formation_code"],"term": "120WMVL","definition": "Wimsattville Formation"}, +{"categories": ["formation_code"],"term": "313GRBG","definition": "Grayburg Formation of Artesia Group"}, +{"categories": ["formation_code"],"term": "318ABOL","definition": "Abo Sandstone (Lower Tongue)"}, +{"categories": ["formation_code"],"term": "318ABOU","definition": "Abo Sandstone (Upper Tongue)"}, +{"categories": ["formation_code"],"term": "112SNTFU","definition": "Santa Fe Group, Upper Part"}, +{"categories": ["formation_code"],"term": "310FRNR","definition": "Forty-Niner Member of Rustler Formation"}, +{"categories": ["formation_code"],"term": "312OCHO","definition": "Ochoan Series"}, +{"categories": ["formation_code"],"term": "313AZOT","definition": "Azotea Tongue of Seven Rivers Formation"}, +{"categories": ["formation_code"],"term": "313QUEN","definition": "Queen Formation"}, +{"categories": ["formation_code"],"term": "319HUCO","definition": "Hueco Limestone"}, +{"categories": ["formation_code"],"term": "313SVRV","definition": "Seven Rivers Formation"}, +{"categories": ["formation_code"],"term": "313CABD","definition": "Carlsbad Group"}, +{"categories": ["formation_code"],"term": "320GRMS","definition": "Gray Mesa Member of Madera Formation"}, +{"categories": ["formation_code"],"term": "211CLRDH","definition": "Colorado Shale"}, +{"categories": ["formation_code"],"term": "120BRLM","definition": "Bearwallow Mountain Andesite"}, +{"categories": ["formation_code"],"term": "122RUBO","definition": "Rubio Peak Formation"}, +{"categories": ["formation_code"],"term": "313SADRL","definition": "San Andres Limestone, Lower Cherty Member"}, +{"categories": ["formation_code"],"term": "313SADRU","definition": "San Andres Limestone, Upper Clastic Member"}, +{"categories": ["formation_code"],"term": "313BRNL","definition": "Bernal Formation of Artesia Group"}, +{"categories": ["formation_code"],"term": "318CPDR","definition": "Chupadera Formation"}, +{"categories": ["formation_code"],"term": "121BDHC","definition": "Bidahochi Formation"}, +{"categories": ["formation_code"],"term": "313SADY","definition": "San Andres Limestone and Yeso Formation, undivided"}, +{"categories": ["formation_code"],"term": "221SRFLL","definition": "San Rafael Group, Lower Part"}, +{"categories": ["formation_code"],"term": "221BLUF","definition": "Bluff Sandstone of Morrison Formation"}, +{"categories": ["formation_code"],"term": "221COSP","definition": "Cow Springs Sandstone of Morrison Formation"}, +{"categories": ["formation_code"],"term": "317ABYS","definition": "Abo and Yeso, undifferentiated"}, +{"categories": ["formation_code"],"term": "221BRSB","definition": "Brushy Basin Shale Member of Morrison Formation"}, +{"categories": ["formation_code"],"term": "310SYDR","definition": "San Ysidro Member of Yeso Formation"}, +{"categories": ["formation_code"],"term": "400SDVL","definition": "Sandoval Granite"}, +{"categories": ["formation_code"],"term": "221SRFL","definition": "San Rafael Group"}, +{"categories": ["formation_code"],"term": "310SGRC","definition": "Sangre de Cristo Formation"}, +{"categories": ["formation_code"],"term": "231TCVS","definition": "Tecovas Formation of Dockum Group"}, +{"categories": ["formation_code"],"term": "211DCRS","definition": "D-Cross Tongue of Mancos Shale of Mesaverde Group"}, +{"categories": ["formation_code"],"term": "211ALSN","definition": "Allison Member of Menefee Formation of Mesaverde Group"}, +{"categories": ["formation_code"],"term": "211LVNN","definition": "La Ventana Tongue of Cliff House Sandstone"}, +{"categories": ["formation_code"],"term": "211MORD","definition": "Madrid Formation"}, +{"categories": ["formation_code"],"term": "210PRMD","definition": "Pyramid Shale"}, +{"categories": ["formation_code"],"term": "124ANMS","definition": "Animas Formation"}, +{"categories": ["formation_code"],"term": "211NBRR","definition": "Niobrara Formation"}, +{"categories": ["formation_code"],"term": "111ALVM","definition": "Holocene Alluvium"}, +{"categories": ["formation_code"],"term": "122SNTFL","definition": "Santa Fe Group, Lower Part"}, +{"categories": ["formation_code"],"term": "111CPLN","definition": "Capulin Basalts"}, +{"categories": ["formation_code"],"term": "120CRSN","definition": "Carson Conflomerate"}, +{"categories": ["formation_code"],"term": "111CRMS","definition": "Covered/Reclaimed Mine Spoil"}, +{"categories": ["formation_code"],"term": "111CRMSA","definition": "Covered/Reclaimed Mine Spoil and Ash"}, +{"categories": ["formation_code"],"term": "111SPOL","definition": "Spoil"}, +{"categories": ["formation_code"],"term": "110TURT","definition": "Tuerto Gravel of Santa Fe Group"}, +{"categories": ["formation_code"],"term": "221RCPR","definition": "Recapture Shale Member of Morrison Formation"}, +{"categories": ["formation_code"],"term": "320BLNG","definition": "Bullington Member of Magdalena Formation"}, +{"categories": ["formation_code"],"term": "112ANCHsr","definition": "Upper Santa Fe Group, Ancha Formation & ancestral Santa Fe river deposits"}, +{"categories": ["formation_code"],"term": "121TSUQae","definition": "Tesuque Fm Lithosomes A and E"}, +{"categories": ["formation_code"],"term": "230TRSC","definition": "Triassic undifferentiated"}, +{"categories": ["formation_code"],"term": "122TSUQdx","definition": "Tesuque Fm, Dixon member (Ttd)"}, +{"categories": ["formation_code"],"term": "123PICSu","definition": "T upper Picuris Formation (Tpu)"}, +{"categories": ["formation_code"],"term": "123PICSm","definition": "T middle Picuris Formation (Tpm)"}, +{"categories": ["formation_code"],"term": "123PICSmc","definition": "T middle conglomerate Picuris Formation (Tpmc)"}, +{"categories": ["formation_code"],"term": "120VBVC","definition": "Tertiary volcanic breccia/volcaniclastic conglomerate"}, +{"categories": ["formation_code"],"term": "120VCSS","definition": "Tertiary volcaniclastic sandstone"}, +{"categories": ["formation_code"],"term": "124DMDT","definition": "Diamond Tail Formation"}, +{"categories": ["formation_code"],"term": "325ALMT","definition": "Penn Alamitos Formation"}, +{"categories": ["formation_code"],"term": "400SAND","definition": "Sandia Granite"}, +{"categories": ["formation_code"],"term": "318VCPK","definition": "Victorio Peak Limestone"}, +{"categories": ["formation_code"],"term": "318BSVP","definition": "Bone Spring and Victorio Peak Limestones"}, +{"categories": ["formation_code"],"term": "100ALVM","definition": "Alluvium"}, +{"categories": ["formation_code"],"term": "310PRMN","definition": "Permian System"}, +{"categories": ["formation_code"],"term": "110AVPS","definition": "Alluvium and Permian System"}, +{"categories": ["formation_code"],"term": "313CRCX","definition": "Capitan Reef Complex and Associated Limestones"}, +{"categories": ["formation_code"],"term": "112SLBL","definition": "Salt Bolson"}, +{"categories": ["formation_code"],"term": "112SBCRC","definition": "Salt Bolson and Capitan Reef Complex"}, +{"categories": ["formation_code"],"term": "313CRDM","definition": "Capitan Reef Complex - Delaware Mountain Group"}, +{"categories": ["formation_code"],"term": "112SBDM","definition": "Salt Bolson and Delaware Mountain Group"}, +{"categories": ["formation_code"],"term": "120BLSN","definition": "Bolson Deposits"}, +{"categories": ["formation_code"],"term": "112SBCR","definition": "Salt Bolson and Cretaceous Rocks"}, +{"categories": ["formation_code"],"term": "112HCBL","definition": "Hueco Bolson"}, +{"categories": ["formation_code"],"term": "120IVIG","definition": "Intrusive Rocks"}, +{"categories": ["formation_code"],"term": "112RLBL","definition": "Red Light Draw Bolson"}, +{"categories": ["formation_code"],"term": "112EFBL","definition": "Eagle Flat Bolson"}, +{"categories": ["formation_code"],"term": "112GRBL","definition": "Green River Bolson"}, +{"categories": ["formation_code"],"term": "123SAND","definition": "Sanders Canyon Formation"}, +{"categories": ["formation_code"],"term": "210MRNH","definition": "Moreno Hill Formation"}, +{"categories": ["formation_code"],"term": "320ALMT","definition": "Alamito Shale"}, +{"categories": ["formation_code"],"term": "313DLRM","definition": "Delaware Mountain Group"}, +{"categories": ["formation_code"],"term": "300PLZC","definition": "Paleozoic Erathem"}, +{"categories": ["formation_code"],"term": "122SPRS","definition": "Spears Member of Datil Formation"}, +{"categories": ["formation_code"],"term": "110AVTV","definition": "Alluvium and Tertiary Volcanics"}, +{"categories": ["formation_code"],"term": "313DMBS","definition": "Delaware Mountain Group - Bone Spring Limestone"}, +{"categories": ["formation_code"],"term": "120ERSV","definition": "Tertiary extrusives"} ] } \ No newline at end of file diff --git a/db/geologic_formation.py b/db/geologic_formation.py index a0119d50c..ff6ce0a85 100644 --- a/db/geologic_formation.py +++ b/db/geologic_formation.py @@ -30,7 +30,7 @@ class GeologicFormation(Base, AutoBaseMixin, ReleaseMixin): comment="The full, human-readable name of the geologic formation (e.g., 'Navajo Sandstone').", ) # TODO: Implement controlled vocabulary for `code` using the `LU_Formation` table from NMAquifer. - code: Mapped[str] = lexicon_term( + formation_code: Mapped[str] = lexicon_term( nullable=True, unique=True, comment="A short code or abbreviation for the geologic formation (e.g., '120ELRT').", @@ -41,7 +41,8 @@ class GeologicFormation(Base, AutoBaseMixin, ReleaseMixin): comment="A detailed description of the geologic formation, its characteristics, and its significance.", ) # TODO: Implement controlled vocabularies for `lithology` using NMAquifer's 'LU_Lithology' table. - lithology: Mapped[str] = lexicon_term( + # This should be implemented after AMMP reviews and cleans up their formation terms and codes. + lithology: Mapped[str] = mapped_column( nullable=True, comment="A controlled vocabulary for the primary, dominant rock type" "(e.g., 'Tuff', 'Sandstone', 'Alluvium', 'Shale').", From d359fa1639879f241d5536236434993ba69f99fa Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Mon, 17 Nov 2025 12:40:23 -0700 Subject: [PATCH 18/33] rafactor(model): Remove TODO about adding formation_code values to lexicon --- db/geologic_formation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/db/geologic_formation.py b/db/geologic_formation.py index ff6ce0a85..35332aac3 100644 --- a/db/geologic_formation.py +++ b/db/geologic_formation.py @@ -29,7 +29,6 @@ class GeologicFormation(Base, AutoBaseMixin, ReleaseMixin): unique=True, comment="The full, human-readable name of the geologic formation (e.g., 'Navajo Sandstone').", ) - # TODO: Implement controlled vocabulary for `code` using the `LU_Formation` table from NMAquifer. formation_code: Mapped[str] = lexicon_term( nullable=True, unique=True, From fc8132768a6cec563034795f89099e59305679ff Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Mon, 17 Nov 2025 16:53:15 -0700 Subject: [PATCH 19/33] refactor(lexicon): refine terms associated with "geographic_scale". --- core/lexicon.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/lexicon.json b/core/lexicon.json index 6d980ab43..0461f530d 100644 --- a/core/lexicon.json +++ b/core/lexicon.json @@ -692,9 +692,10 @@ {"categories": ["aquifer_type"], "term": "Unconfined, single", "definition": "Unconfined single aquifer"}, {"categories": ["aquifer_type"], "term": "Unconfined, multiple", "definition": "Unconfined multiple aquifers"}, {"categories": ["aquifer_type"], "term": "Unsaturated", "definition": "Unsaturated (dry)"}, - {"categories": ["geographic_scale"], "term": "Major", "definition": "Major"}, - {"categories": ["geographic_scale"], "term": "Intermediate", "definition": "Regional"}, - {"categories": ["geographic_scale"], "term": "Local", "definition": "Local"}, + {"categories": ["geographic_scale"], "term": "Major", "definition": "Major aquifers of national significance"}, + {"categories": ["geographic_scale"], "term": "Regional", "definition": "Important aquifers serving regions"}, + {"categories": ["geographic_scale"], "term": "Local", "definition": "Smaller, locally important aquifers"}, + {"categories": ["geographic_scale"], "term": "Minor", "definition": "Limited extent or yield"}, {"categories": ["formation_code"],"term": "000EXRV","definition": "Extrusive Rocks"}, {"categories": ["formation_code"],"term": "000IRSV","definition": "Intrusive Rocks"}, {"categories": ["formation_code"],"term": "050QUAL","definition": "Quaternary Alluvium in Valleys"}, From b01d172179ed75e1f0a389323c19d52ef09af2f1 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Mon, 17 Nov 2025 17:00:29 -0700 Subject: [PATCH 20/33] refactor(model): Replace hardcoded srid with SRID_WGS84 from constants Replace the hardcoded `srid` in the `aquifer_system` and `geologic_formation` models with the SRID_WGS84 from constants. --- db/aquifer_system.py | 4 +++- db/geologic_formation.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/db/aquifer_system.py b/db/aquifer_system.py index fd121cd7b..95066c2e8 100644 --- a/db/aquifer_system.py +++ b/db/aquifer_system.py @@ -14,6 +14,8 @@ from db.base import Base, AutoBaseMixin, ReleaseMixin from db.lexicon import lexicon_term +from constants import SRID_WGS84 + if TYPE_CHECKING: from db.thing import WellScreen, ThingAquiferAssociation, Thing @@ -41,7 +43,7 @@ class AquiferSystem(Base, AutoBaseMixin, ReleaseMixin): comment="A controlled vocabulary field to classify the aquifer's geographic scale (e.g., 'Major', 'Regional', 'Local').", ) boundary: Mapped[Geometry] = mapped_column( - Geometry(geometry_type="MULTIPOLYGON", srid=4326, spatial_index=True), + Geometry(geometry_type="MULTIPOLYGON", srid=SRID_WGS84, spatial_index=True), nullable=True, comment="A spatial representation of the aquifer system's boundary.", ) diff --git a/db/geologic_formation.py b/db/geologic_formation.py index 35332aac3..d916f402a 100644 --- a/db/geologic_formation.py +++ b/db/geologic_formation.py @@ -15,6 +15,8 @@ from db.base import Base, AutoBaseMixin, ReleaseMixin from db.lexicon import lexicon_term +from constants import SRID_WGS84 + if TYPE_CHECKING: from db.thing import Thing from db.thing_formation_association import ThingFormationAssociation @@ -47,7 +49,7 @@ class GeologicFormation(Base, AutoBaseMixin, ReleaseMixin): "(e.g., 'Tuff', 'Sandstone', 'Alluvium', 'Shale').", ) boundary: Mapped[Geometry] = mapped_column( - Geometry(geometry_type="MULTIPOLYGON", srid=4326, spatial_index=True), + Geometry(geometry_type="MULTIPOLYGON", srid=SRID_WGS84, spatial_index=True), nullable=True, comment="A spatial representation of the geologic formation's extent.", ) From f35e61d61faf664d756f6505e86410dbb7c28463 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Mon, 17 Nov 2025 19:42:54 -0700 Subject: [PATCH 21/33] refactor(model): Add geologic_formation foreign key to WellScreen model. --- db/geologic_formation.py | 6 +++++- db/thing.py | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/db/geologic_formation.py b/db/geologic_formation.py index d916f402a..a29c20010 100644 --- a/db/geologic_formation.py +++ b/db/geologic_formation.py @@ -18,7 +18,7 @@ from constants import SRID_WGS84 if TYPE_CHECKING: - from db.thing import Thing + from db.thing import Thing, WellScreen from db.thing_formation_association import ThingFormationAssociation @@ -63,6 +63,10 @@ class GeologicFormation(Base, AutoBaseMixin, ReleaseMixin): cascade="all, delete-orphan", passive_deletes=True, ) + # One-To-Many: A GeologicFormation can have many physical WellScreens installed in it. + screens: Mapped[List["WellScreen"]] = relationship( + "WellScreen", back_populates="geologic_formation", passive_deletes=True + ) # --- Association Proxies --- # Provides direct access to Things (wells) that penetrate this formation. diff --git a/db/thing.py b/db/thing.py index b5a2a284a..25dcb0969 100644 --- a/db/thing.py +++ b/db/thing.py @@ -364,6 +364,9 @@ class WellScreen(Base, AutoBaseMixin, ReleaseMixin): aquifer_system_id: Mapped[int] = mapped_column( ForeignKey("aquifer_system.id", ondelete="SET NULL"), nullable=True ) + geologic_formation_id: Mapped[int] = mapped_column( + ForeignKey("geologic_formation.id", ondelete="SET NULL"), nullable=True + ) screen_depth_top: Mapped[float] = mapped_column( info={"unit": "feet below ground surface"}, nullable=True ) From 390d6f39aacc4e92fc1064111daa3b8d1a56025b Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Tue, 18 Nov 2025 09:10:01 -0700 Subject: [PATCH 22/33] refactor(model): enhance relationships for `ThingAquiferAssociation` and `ThingFormationAssociation` models Add `lazy="joined"` to `ThingAquiferAssociation` and `ThingFormationAssociation` models to eagerly load relationships. --- db/thing_aquifer_association.py | 4 ++-- db/thing_formation_association.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/db/thing_aquifer_association.py b/db/thing_aquifer_association.py index 5f14696ca..d5fb2881a 100644 --- a/db/thing_aquifer_association.py +++ b/db/thing_aquifer_association.py @@ -33,10 +33,10 @@ class ThingAquiferAssociation(Base, AutoBaseMixin, ReleaseMixin): # --- Relationship Definitions --- # Many-To-One: This association links to one Thing. thing: Mapped["Thing"] = relationship( - "Thing", back_populates="aquifer_associations" + "Thing", back_populates="aquifer_associations", lazy="joined" ) # Many-To-One: This association links to one AquiferSystem. aquifer_system: Mapped["AquiferSystem"] = relationship( - "AquiferSystem", back_populates="thing_associations" + "AquiferSystem", back_populates="thing_associations", lazy="joined" ) diff --git a/db/thing_formation_association.py b/db/thing_formation_association.py index c5f0f9592..8904fa089 100644 --- a/db/thing_formation_association.py +++ b/db/thing_formation_association.py @@ -51,10 +51,10 @@ class ThingFormationAssociation(Base, AutoBaseMixin, ReleaseMixin): # --- Relationship Definitions --- # Many-To-One: This association links to one Thing. thing: Mapped["Thing"] = relationship( - "Thing", back_populates="formation_associations" + "Thing", back_populates="formation_associations", lazy="joined" ) # Many-To-One: This association links to one GeologicFormation. geologic_formation: Mapped["GeologicFormation"] = relationship( - "GeologicFormation", back_populates="thing_associations" + "GeologicFormation", back_populates="thing_associations", lazy="joined" ) From c738afba94d576c945d35ac1cec9807497996762 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Tue, 18 Nov 2025 15:51:39 -0700 Subject: [PATCH 23/33] refactor(model): refactor `lithology` field to a lexicon_term - Refactor `lithology` field to a lexicon_term - Add lithology values to lexicon --- core/lexicon.json | 667 ++++++++++++++++++++++----------------- db/geologic_formation.py | 16 +- 2 files changed, 381 insertions(+), 302 deletions(-) diff --git a/core/lexicon.json b/core/lexicon.json index 0461f530d..15479d6b4 100644 --- a/core/lexicon.json +++ b/core/lexicon.json @@ -53,7 +53,8 @@ {"name": "status_type", "description": null}, {"name": "status_value", "description": null}, {"name": "well_pump_type", "description": null}, - {"name": "formation_code", "description": null} + {"name": "formation_code", "description": null}, + {"name": "lithology", "description": null} ], "terms": [ {"categories": ["review_status"], "term": "approved", "definition": "approved"}, @@ -697,299 +698,375 @@ {"categories": ["geographic_scale"], "term": "Local", "definition": "Smaller, locally important aquifers"}, {"categories": ["geographic_scale"], "term": "Minor", "definition": "Limited extent or yield"}, {"categories": ["formation_code"],"term": "000EXRV","definition": "Extrusive Rocks"}, -{"categories": ["formation_code"],"term": "000IRSV","definition": "Intrusive Rocks"}, -{"categories": ["formation_code"],"term": "050QUAL","definition": "Quaternary Alluvium in Valleys"}, -{"categories": ["formation_code"],"term": "100QBAS","definition": "Quaternary basalt"}, -{"categories": ["formation_code"],"term": "110ALVM","definition": "Quaternary Alluvium"}, -{"categories": ["formation_code"],"term": "110AVMB","definition": "Alluvium, Bolson Deposits and Other Surface Deposits"}, -{"categories": ["formation_code"],"term": "110BLSN","definition": "Bolson Fill"}, -{"categories": ["formation_code"],"term": "110NTGU","definition": "Naha and Tsegi Alluvium Deposits, undifferentiated"}, -{"categories": ["formation_code"],"term": "110PTODC","definition": "Pediment, Terrace and Other Deposits of Gravel, Sand and Caliche"}, -{"categories": ["formation_code"],"term": "111MCCR","definition": "McCathys Basalt Flow"}, -{"categories": ["formation_code"],"term": "112ANCH","definition": "Upper Santa Fe Group, Ancha Formation (QTa)"}, -{"categories": ["formation_code"],"term": "112CURB","definition": "Cuerbio Basalt"}, -{"categories": ["formation_code"],"term": "112LAMA","definition": "Lama Formation (QTl, QTbh) and other mountain front alluvial fans"}, -{"categories": ["formation_code"],"term": "112LAMAb","definition": "Lama Fm (QTl, QTbh) between Servilleta Basalts"}, -{"categories": ["formation_code"],"term": "112LGUN","definition": "Laguna Basalt Flow"}, -{"categories": ["formation_code"],"term": "112QTBF","definition": "Quaternary-Tertiary basin fill (not in valleys)"}, -{"categories": ["formation_code"],"term": "112QTBFlac","definition": "Quaternary-Tertiary basin fill, lacustrian-playa lithofacies"}, -{"categories": ["formation_code"],"term": "112QTBFpd","definition": "Quaternary-Tertiary basin fill, distal piedmont lithofacies"}, -{"categories": ["formation_code"],"term": "112QTBFppm","definition": "Quaternary-Tertiary basin fill, proximal and medial piedmont lithofacies"}, -{"categories": ["formation_code"],"term": "112SNTF","definition": "Santa Fe Group, undivided"}, -{"categories": ["formation_code"],"term": "112SNTFA","definition": "Upper Santa Fe Group, axial facies"}, -{"categories": ["formation_code"],"term": "112SNTFOB","definition": "Upper SantaFe Group, Loma Barbon member of Arroyo Ojito Formatin"}, -{"categories": ["formation_code"],"term": "112SNTFP","definition": "Upper Santa Fe Group, piedmont facies"}, -{"categories": ["formation_code"],"term": "112TRTO","definition": "Tuerto Gravels (QTt)"}, -{"categories": ["formation_code"],"term": "120DTIL","definition": "Datil Formation"}, -{"categories": ["formation_code"],"term": "120ELRT","definition": "El Rito Formation"}, -{"categories": ["formation_code"],"term": "120IRSV","definition": "Tertiary Intrusives"}, -{"categories": ["formation_code"],"term": "120SBLC","definition": "Sierra Blanca Volcanics, undivided"}, -{"categories": ["formation_code"],"term": "120SRVB","definition": "Tertiary Servilletta Basalts (Tsb)"}, -{"categories": ["formation_code"],"term": "120SRVBf","definition": "Tertiary Servilletta Basalts, fractured (Tsbf)"}, -{"categories": ["formation_code"],"term": "120TSBV_Lower","definition": "Tertiary Sierra Blanca area lower volcanic unit (Hog Pen Fm)"}, -{"categories": ["formation_code"],"term": "120TSBV_Upper","definition": "Tertiary Sierra Blanca area upper volcanic unit (above Hog Pen Fm)"}, -{"categories": ["formation_code"],"term": "121CHMT","definition": "Chamita Formation (Tc)"}, -{"categories": ["formation_code"],"term": "121CHMTv","definition": "Chamita Fm, Vallito member (Tcv)"}, -{"categories": ["formation_code"],"term": "121CHMTvs","definition": "Chamita Fm, sandy Vallito member (Tcvs)"}, -{"categories": ["formation_code"],"term": "121OGLL","definition": "Ogallala Formation"}, -{"categories": ["formation_code"],"term": "121PUYEF","definition": "Puye Conglomerate, Fanglomerate Member"}, -{"categories": ["formation_code"],"term": "121TSUQ","definition": "Tesuque Formation, undifferentiated unit"}, -{"categories": ["formation_code"],"term": "121TSUQa","definition": "Tesuque Fm lithosome A (Tta)"}, -{"categories": ["formation_code"],"term": "121TSUQacu","definition": "Tesuque Fm (upper), Cuarteles member lithosome A (Ttacu)"}, -{"categories": ["formation_code"],"term": "121TSUQacuf","definition": "Tesuque Fm (upper), fine-grained Cuarteles member lithosome A (Ttacuf)"}, -{"categories": ["formation_code"],"term": "121TSUQaml","definition": "Tesuque Fm lower-middle lithosome A (Ttaml)"}, -{"categories": ["formation_code"],"term": "121TSUQb","definition": "Tesuque Fm lithosome B (Ttb)"}, -{"categories": ["formation_code"],"term": "121TSUQbfl","definition": "Tesuque Fm lower lithosome B, basin-floor deposits (Ttbfl)"}, -{"categories": ["formation_code"],"term": "121TSUQbfm","definition": "Tesuque Fm middle lithosome B, basin-floor deposits (Ttbfm)"}, -{"categories": ["formation_code"],"term": "121TSUQbp","definition": "Tesuque Fm lithosome B, Pojoaque member (Ttbp)"}, -{"categories": ["formation_code"],"term": "121TSUQce","definition": "Tesuque Fm, Cejita member (Ttce)"}, -{"categories": ["formation_code"],"term": "121TSUQe","definition": "Tesuque Fm lithosome E (Tte)"}, -{"categories": ["formation_code"],"term": "121TSUQs","definition": "Tesuque Fm lithosome S (Tts)"}, -{"categories": ["formation_code"],"term": "121TSUQsa","definition": "Tesuque Fm lateral gradation lithosomes S and A (Ttsag)"}, -{"categories": ["formation_code"],"term": "121TSUQsc","definition": "Tesuque Fm coarse-grained lithosome S (Ttsc)"}, -{"categories": ["formation_code"],"term": "121TSUQsf","definition": "Tesuque Fm, fine-grained lithosome S (Ttsf)"}, -{"categories": ["formation_code"],"term": "122CHOC","definition": "Chamita and Ojo Caliente interlayered (Ttoc)"}, -{"categories": ["formation_code"],"term": "122CRTO","definition": "Chama El Rito Formation (Tesuque member, Ttc)"}, -{"categories": ["formation_code"],"term": "122OJOC","definition": "Ojo Caliente Formation (Tesuque member, Tto)"}, -{"categories": ["formation_code"],"term": "122PICR","definition": "Picuris Tuff"}, -{"categories": ["formation_code"],"term": "122PPTS","definition": "Popotosa Formation"}, -{"categories": ["formation_code"],"term": "122SNTFP","definition": "Lower Santa Fe Group, piedmont facies"}, -{"categories": ["formation_code"],"term": "123DTILSPRS","definition": "Datil Group ignimbrites and lavas and Spears Group, interbedded"}, -{"categories": ["formation_code"],"term": "123DTMGandbas","definition": "Datil and Mogollon Group andesite, basaltic andesite, and basalt flows"}, -{"categories": ["formation_code"],"term": "123DTMGign","definition": "Datil and Mogollon Group ignimbrites"}, -{"categories": ["formation_code"],"term": "123DTMGrhydac","definition": "Datil and Mogollon Group rhyolite and dacite flows"}, -{"categories": ["formation_code"],"term": "123ESPN","definition": "T Espinaso Formation (Te)"}, -{"categories": ["formation_code"],"term": "123GLST","definition": "T Galisteo Formation"}, -{"categories": ["formation_code"],"term": "123PICS","definition": "T Picuris Formation (Tp)"}, -{"categories": ["formation_code"],"term": "123PICSc","definition": "T Picuris Formation, basal conglomerate (Tpc)"}, -{"categories": ["formation_code"],"term": "123PICSl","definition": "T lower Picuris Formation (Tpl)"}, -{"categories": ["formation_code"],"term": "123SPRSDTMGlava","definition": "Spears Group and Datil-Mogollon intermediate-mafic lavas, interbedded"}, -{"categories": ["formation_code"],"term": "123SPRSlower","definition": "Spears Group, lower part; tuffaceous, gravelly debris and mud flows"}, -{"categories": ["formation_code"],"term": "123SPRSmid_uppe","definition": "Spears Group, middle-upper part; excludes Dog Spring Formation"}, -{"categories": ["formation_code"],"term": "124BACA","definition": "Baca Formation"}, -{"categories": ["formation_code"],"term": "124CBMN","definition": "Cub Mountain Formation"}, -{"categories": ["formation_code"],"term": "124LLVS","definition": "Llaves Member of San Jose Formation"}, -{"categories": ["formation_code"],"term": "124PSCN","definition": "Poison Canyon Formation"}, -{"categories": ["formation_code"],"term": "124RGIN","definition": "Regina Member of San Jose Formation"}, -{"categories": ["formation_code"],"term": "124SNJS","definition": "San Jose Formation"}, -{"categories": ["formation_code"],"term": "124TPCS","definition": "TapicitosMember of San Jose Formation"}, -{"categories": ["formation_code"],"term": "125NCMN","definition": "Nacimiento Formation"}, -{"categories": ["formation_code"],"term": "125NCMNS","definition": "Nacimiento Formation, Sandy Shale Facies"}, -{"categories": ["formation_code"],"term": "125RTON","definition": "Raton Formation"}, -{"categories": ["formation_code"],"term": "130CALDFLOOR","definition": "Caldera Floor bedrock S. of San Agustin Plains. Mostly DTILSPRS & Paleo."}, -{"categories": ["formation_code"],"term": "180TKSCC_Upper","definition": "Tertiary-Cretaceous, Sanders Canyon, Cub Mtn. and upper Crevasse Canyon Fm"}, -{"categories": ["formation_code"],"term": "180TKTR","definition": "Tertiary-Cretaceous-Triassic, Baca, Crevasse Cyn, Gallup, Mancos, Dakota, T"}, -{"categories": ["formation_code"],"term": "210CRCS","definition": "Cretaceous System, undivided"}, -{"categories": ["formation_code"],"term": "210GLUPC_Lower","definition": "K Gallup Sandstone and lower Crevasse Canyon Fm"}, -{"categories": ["formation_code"],"term": "210HOSTD","definition": "K Hosta Dalton"}, -{"categories": ["formation_code"],"term": "210MCDK","definition": "K Mancos/Dakota undivided"}, -{"categories": ["formation_code"],"term": "210MNCS","definition": "Mancos Shale, undivided"}, -{"categories": ["formation_code"],"term": "210MNCSL","definition": "K Lower Mancos"}, -{"categories": ["formation_code"],"term": "210MNCSU","definition": "K Upper Mancos"}, -{"categories": ["formation_code"],"term": "211CLFHV","definition": "Cliff House Sandstone, includes La Ventana Tongues in NW Sandoval Co."}, -{"categories": ["formation_code"],"term": "211CRLL","definition": "Carlile Shale"}, -{"categories": ["formation_code"],"term": "211CRVC","definition": "Crevasse Canyon Formation of Mesaverde Group"}, -{"categories": ["formation_code"],"term": "211DKOT","definition": "Dakota Sandstone or Formation"}, -{"categories": ["formation_code"],"term": "211DLCO","definition": "Dilco Coal Member of Crevasse Canyon Formation of Mesaverde Group"}, -{"categories": ["formation_code"],"term": "211DLTN","definition": "Dalton Sandstone Member of Crevasse Canyon Formation of Mesaverde Group"}, -{"categories": ["formation_code"],"term": "211FRHS","definition": "Fort Hays Limestone Member of Niobrara Formation"}, -{"categories": ["formation_code"],"term": "211FRLD","definition": "Fruitland Formation"}, -{"categories": ["formation_code"],"term": "211FRMG","definition": "Farmington Sandstone Member of Kirtland Shale"}, -{"categories": ["formation_code"],"term": "211GBSNC","definition": "Gibson Coal Member of Crevasse Canyon Formation of Mesaverde Group"}, -{"categories": ["formation_code"],"term": "211GLLG","definition": "Gallego Sandstone Member of Gallup Sandstone"}, -{"categories": ["formation_code"],"term": "211GLLP","definition": "Gallup Sandstone"}, -{"categories": ["formation_code"],"term": "211GRRG","definition": "Greenhorn and Graneros Formations"}, -{"categories": ["formation_code"],"term": "211GRRS","definition": "Graneros Shale"}, -{"categories": ["formation_code"],"term": "211HOST","definition": "Hosta Tongue of Point Lookout Sandstone of Mesaverde Group"}, -{"categories": ["formation_code"],"term": "211KRLD","definition": "Kirtland Shale"}, -{"categories": ["formation_code"],"term": "211LWIS","definition": "Lewis Shale"}, -{"categories": ["formation_code"],"term": "211MENF","definition": "Menefee Formation"}, -{"categories": ["formation_code"],"term": "211MENFU","definition": "K Upper Menefee (above Harmon Sandstone)"}, -{"categories": ["formation_code"],"term": "211MVRD","definition": "Mesaverde Group"}, -{"categories": ["formation_code"],"term": "211OJAM","definition": "Ojo Alamo Sandstone"}, -{"categories": ["formation_code"],"term": "211PCCF","definition": "Pictured Cliffs Sandstone"}, -{"categories": ["formation_code"],"term": "211PIRR","definition": "Pierre Shale"}, -{"categories": ["formation_code"],"term": "211PNLK","definition": "Point Lookout Sandstone"}, -{"categories": ["formation_code"],"term": "211SMKH","definition": "Smoky Hill Marl Member"}, -{"categories": ["formation_code"],"term": "211TLLS","definition": "Twowells Sandstone Lentil of Pike of Dakota Sandstone"}, -{"categories": ["formation_code"],"term": "212KTRP","definition": "K Dakota Sandstone, Moenkopi Fm, Artesia Group"}, -{"categories": ["formation_code"],"term": "217PRGR","definition": "Purgatoire Formation"}, -{"categories": ["formation_code"],"term": "220ENRD","definition": "Entrada Sandstone"}, -{"categories": ["formation_code"],"term": "220JURC","definition": "Jurassic undivided"}, -{"categories": ["formation_code"],"term": "220NAVJ","definition": "Navajo Sandstone"}, -{"categories": ["formation_code"],"term": "221BLFF","definition": "Bluff Sandstone of Morrison Formation"}, -{"categories": ["formation_code"],"term": "221CSPG","definition": "Cow Springs Sandstone of Morrison Formation"}, -{"categories": ["formation_code"],"term": "221ERADU","definition": "Entrada Sandstone of San Rafael Group, Upper"}, -{"categories": ["formation_code"],"term": "221MRSN","definition": "Morrison Formation"}, -{"categories": ["formation_code"],"term": "221MRSN/BBSN","definition": "Brushy Basin Member of Morrison"}, -{"categories": ["formation_code"],"term": "221MRSN/JCKP","definition": "Jackpile Sandstone Member of Morrison"}, -{"categories": ["formation_code"],"term": "221MRSN/RCAP","definition": "Recapture Shale Member of Morrison"}, -{"categories": ["formation_code"],"term": "221MRSN/WWCN","definition": "Westwater Canyon Member of Morrison"}, -{"categories": ["formation_code"],"term": "221SLWS","definition": "Salt Wash Sandstone Member of Morrison Formation"}, -{"categories": ["formation_code"],"term": "221SMVL","definition": "Summerville Formation of San Rafael Group"}, -{"categories": ["formation_code"],"term": "221TDLT","definition": "J Todilto"}, -{"categories": ["formation_code"],"term": "221WSRC","definition": "Westwater Canyon Sandstone Member of Morrison Formation"}, -{"categories": ["formation_code"],"term": "221ZUNIS","definition": "Zuni Sandstone"}, -{"categories": ["formation_code"],"term": "231AGZC","definition": "Tr Agua Zarca"}, -{"categories": ["formation_code"],"term": "231AGZCU","definition": "Tr Upper Agua Zarca"}, -{"categories": ["formation_code"],"term": "231CHNL","definition": "Chinle Formation"}, -{"categories": ["formation_code"],"term": "231CORR","definition": "Correo Sandstone Member of Chinle Formation"}, -{"categories": ["formation_code"],"term": "231DCKM","definition": "Dockum Group"}, -{"categories": ["formation_code"],"term": "231PFDF","definition": "Tr Petrified Forest"}, -{"categories": ["formation_code"],"term": "231PFDFL","definition": "Tr Lower Petrified Forest (below middle sandstone)"}, -{"categories": ["formation_code"],"term": "231PFDFM","definition": "Tr Middle Petrified Forest sandstone"}, -{"categories": ["formation_code"],"term": "231PFDFU","definition": "Tr Upper Petrified Forest (above middle sandstone)"}, -{"categories": ["formation_code"],"term": "231RCKP","definition": "Rock Point Member of Wingate Sandstone"}, -{"categories": ["formation_code"],"term": "231SNRS","definition": "Santa Rosa Sandstone"}, -{"categories": ["formation_code"],"term": "231SNSL","definition": "Sonsela Sandstone Bed of Petrified Forest Member of Chinle Formation"}, -{"categories": ["formation_code"],"term": "231SRMP","definition": "Shinarump Member of Chinle Formation"}, -{"categories": ["formation_code"],"term": "231WNGT","definition": "Wingate Sandstone"}, -{"categories": ["formation_code"],"term": "260SNAN","definition": "P San Andres"}, -{"categories": ["formation_code"],"term": "260SNAN_lower","definition": "Lower San Andres Formation"}, -{"categories": ["formation_code"],"term": "261SNGL","definition": "P San Andres - Glorieta Sandstone in Rio Bonito member"}, -{"categories": ["formation_code"],"term": "300YESO","definition": "P Yeso"}, -{"categories": ["formation_code"],"term": "300YESO_lower","definition": "Lower Yeso Formation"}, -{"categories": ["formation_code"],"term": "300YESO_upper","definition": "Upper Yeso Formation"}, -{"categories": ["formation_code"],"term": "310ABO","definition": "P Abo"}, -{"categories": ["formation_code"],"term": "310DCLL","definition": "De Chelly Sandstone Member of Cutler Formation"}, -{"categories": ["formation_code"],"term": "310GLOR","definition": "Glorieta Sandstone Member of San Andres Formation (of Manzano Group)"}, -{"categories": ["formation_code"],"term": "310MBLC","definition": "Meseta Blanca Sandstone Member of Yeso Formation"}, -{"categories": ["formation_code"],"term": "310TRRS","definition": "Torres Member of Yeso Formation"}, -{"categories": ["formation_code"],"term": "310YESO","definition": "Yeso Formation"}, -{"categories": ["formation_code"],"term": "310YESOG","definition": "Yeso Formation, Manzono Group"}, -{"categories": ["formation_code"],"term": "312CSTL","definition": "Castile Formation"}, -{"categories": ["formation_code"],"term": "312RSLR","definition": "Rustler Formation"}, -{"categories": ["formation_code"],"term": "313ARTS","definition": "Artesia Group"}, -{"categories": ["formation_code"],"term": "313BLCN","definition": "Bell Canyon Formation"}, -{"categories": ["formation_code"],"term": "313BRUC","definition": "Brushy Canyon Formation of Delaware Mountain Group"}, -{"categories": ["formation_code"],"term": "313CKBF","definition": "Chalk Bluff Formation"}, -{"categories": ["formation_code"],"term": "313CLBD","definition": "Carlsbad Limestone"}, -{"categories": ["formation_code"],"term": "313CPTN","definition": "Capitan Limestone"}, -{"categories": ["formation_code"],"term": "313GDLP","definition": "Guadalupian Series"}, -{"categories": ["formation_code"],"term": "313GOSP","definition": "Goat Seep Dolomite"}, -{"categories": ["formation_code"],"term": "313SADG","definition": "San Andres Limestone and Glorieta Sandstone"}, -{"categories": ["formation_code"],"term": "313SADR","definition": "San Andres Limestone, undivided"}, -{"categories": ["formation_code"],"term": "313TNSL","definition": "Tansill Formation"}, -{"categories": ["formation_code"],"term": "313YATS","definition": "Yates Formation, Guadalupe Group"}, -{"categories": ["formation_code"],"term": "315LABR","definition": "P Laborcita (Bursum)"}, -{"categories": ["formation_code"],"term": "315YESOABO","definition": "Alamosa Creek and San Agustin Plains area - Yeso and Abo Formations"}, -{"categories": ["formation_code"],"term": "318ABO","definition": "P Abo"}, -{"categories": ["formation_code"],"term": "318BSPG","definition": "Bone Spring Limestone"}, -{"categories": ["formation_code"],"term": "318JOYT","definition": "Joyita Sandstone Member of Yeso Formation"}, -{"categories": ["formation_code"],"term": "318YESO","definition": "Yeso Formation"}, -{"categories": ["formation_code"],"term": "319BRSM","definition": "Bursum Formation and Equivalent Rocks"}, -{"categories": ["formation_code"],"term": "320HLDR","definition": "Penn Holder"}, -{"categories": ["formation_code"],"term": "320PENN","definition": "Pennsylvanian undivided"}, -{"categories": ["formation_code"],"term": "320SNDI","definition": "Sandia Formation"}, -{"categories": ["formation_code"],"term": "321SGDC","definition": "Sangre de Cristo Formation"}, -{"categories": ["formation_code"],"term": "322BEMN","definition": "Penn Beeman"}, -{"categories": ["formation_code"],"term": "325GBLR","definition": "Penn Gobbler"}, -{"categories": ["formation_code"],"term": "325MDER","definition": "Madera Limestone, undivided"}, -{"categories": ["formation_code"],"term": "325MDERL","definition": "Penn Lower Madera"}, -{"categories": ["formation_code"],"term": "325MDERU","definition": "Penn Upper Madera"}, -{"categories": ["formation_code"],"term": "325SAND","definition": "Penn Sandia"}, -{"categories": ["formation_code"],"term": "326MGDL","definition": "Magdalena Group"}, -{"categories": ["formation_code"],"term": "340EPRS","definition": "Espiritu Santo Formation"}, -{"categories": ["formation_code"],"term": "350PZBA","definition": "Alamosa Creek and San Agustin Plains area - Paleozoic strata beneath Abo Fm"}, -{"categories": ["formation_code"],"term": "350PZBB","definition": "Tul Basin area - Paleozoic strata below Bursum Fm"}, -{"categories": ["formation_code"],"term": "400EMBD","definition": "Embudo Granite (undifferentiated PreCambrian near Santa Fe)"}, -{"categories": ["formation_code"],"term": "400PCMB","definition": "Precambrian Erathem"}, -{"categories": ["formation_code"],"term": "400PREC","definition": "undifferentiated PreCambrian crystalline rocks (X)"}, -{"categories": ["formation_code"],"term": "400PRECintr","definition": "PreCambrian crystalline rocks and local Tertiary intrusives"}, -{"categories": ["formation_code"],"term": "400PRST","definition": "Priest Granite"}, -{"categories": ["formation_code"],"term": "400TUSS","definition": "Tusas Granite"}, -{"categories": ["formation_code"],"term": "410PRCG","definition": "PreCambrian granite (Xg)"}, -{"categories": ["formation_code"],"term": "410PRCGf","definition": "PreCambrian granite, fractured (Xgf)"}, -{"categories": ["formation_code"],"term": "410PRCQ","definition": "PreCambrian quartzite (Xq)"}, -{"categories": ["formation_code"],"term": "410PRCQf","definition": "PreCambrian quartzite, fractured (Xqf)"}, -{"categories": ["formation_code"],"term": "121GILA","definition": "Gila Conglomerate (group)"}, -{"categories": ["formation_code"],"term": "312DYLK","definition": "Dewey Lake Redbeds"}, -{"categories": ["formation_code"],"term": "120WMVL","definition": "Wimsattville Formation"}, -{"categories": ["formation_code"],"term": "313GRBG","definition": "Grayburg Formation of Artesia Group"}, -{"categories": ["formation_code"],"term": "318ABOL","definition": "Abo Sandstone (Lower Tongue)"}, -{"categories": ["formation_code"],"term": "318ABOU","definition": "Abo Sandstone (Upper Tongue)"}, -{"categories": ["formation_code"],"term": "112SNTFU","definition": "Santa Fe Group, Upper Part"}, -{"categories": ["formation_code"],"term": "310FRNR","definition": "Forty-Niner Member of Rustler Formation"}, -{"categories": ["formation_code"],"term": "312OCHO","definition": "Ochoan Series"}, -{"categories": ["formation_code"],"term": "313AZOT","definition": "Azotea Tongue of Seven Rivers Formation"}, -{"categories": ["formation_code"],"term": "313QUEN","definition": "Queen Formation"}, -{"categories": ["formation_code"],"term": "319HUCO","definition": "Hueco Limestone"}, -{"categories": ["formation_code"],"term": "313SVRV","definition": "Seven Rivers Formation"}, -{"categories": ["formation_code"],"term": "313CABD","definition": "Carlsbad Group"}, -{"categories": ["formation_code"],"term": "320GRMS","definition": "Gray Mesa Member of Madera Formation"}, -{"categories": ["formation_code"],"term": "211CLRDH","definition": "Colorado Shale"}, -{"categories": ["formation_code"],"term": "120BRLM","definition": "Bearwallow Mountain Andesite"}, -{"categories": ["formation_code"],"term": "122RUBO","definition": "Rubio Peak Formation"}, -{"categories": ["formation_code"],"term": "313SADRL","definition": "San Andres Limestone, Lower Cherty Member"}, -{"categories": ["formation_code"],"term": "313SADRU","definition": "San Andres Limestone, Upper Clastic Member"}, -{"categories": ["formation_code"],"term": "313BRNL","definition": "Bernal Formation of Artesia Group"}, -{"categories": ["formation_code"],"term": "318CPDR","definition": "Chupadera Formation"}, -{"categories": ["formation_code"],"term": "121BDHC","definition": "Bidahochi Formation"}, -{"categories": ["formation_code"],"term": "313SADY","definition": "San Andres Limestone and Yeso Formation, undivided"}, -{"categories": ["formation_code"],"term": "221SRFLL","definition": "San Rafael Group, Lower Part"}, -{"categories": ["formation_code"],"term": "221BLUF","definition": "Bluff Sandstone of Morrison Formation"}, -{"categories": ["formation_code"],"term": "221COSP","definition": "Cow Springs Sandstone of Morrison Formation"}, -{"categories": ["formation_code"],"term": "317ABYS","definition": "Abo and Yeso, undifferentiated"}, -{"categories": ["formation_code"],"term": "221BRSB","definition": "Brushy Basin Shale Member of Morrison Formation"}, -{"categories": ["formation_code"],"term": "310SYDR","definition": "San Ysidro Member of Yeso Formation"}, -{"categories": ["formation_code"],"term": "400SDVL","definition": "Sandoval Granite"}, -{"categories": ["formation_code"],"term": "221SRFL","definition": "San Rafael Group"}, -{"categories": ["formation_code"],"term": "310SGRC","definition": "Sangre de Cristo Formation"}, -{"categories": ["formation_code"],"term": "231TCVS","definition": "Tecovas Formation of Dockum Group"}, -{"categories": ["formation_code"],"term": "211DCRS","definition": "D-Cross Tongue of Mancos Shale of Mesaverde Group"}, -{"categories": ["formation_code"],"term": "211ALSN","definition": "Allison Member of Menefee Formation of Mesaverde Group"}, -{"categories": ["formation_code"],"term": "211LVNN","definition": "La Ventana Tongue of Cliff House Sandstone"}, -{"categories": ["formation_code"],"term": "211MORD","definition": "Madrid Formation"}, -{"categories": ["formation_code"],"term": "210PRMD","definition": "Pyramid Shale"}, -{"categories": ["formation_code"],"term": "124ANMS","definition": "Animas Formation"}, -{"categories": ["formation_code"],"term": "211NBRR","definition": "Niobrara Formation"}, -{"categories": ["formation_code"],"term": "111ALVM","definition": "Holocene Alluvium"}, -{"categories": ["formation_code"],"term": "122SNTFL","definition": "Santa Fe Group, Lower Part"}, -{"categories": ["formation_code"],"term": "111CPLN","definition": "Capulin Basalts"}, -{"categories": ["formation_code"],"term": "120CRSN","definition": "Carson Conflomerate"}, -{"categories": ["formation_code"],"term": "111CRMS","definition": "Covered/Reclaimed Mine Spoil"}, -{"categories": ["formation_code"],"term": "111CRMSA","definition": "Covered/Reclaimed Mine Spoil and Ash"}, -{"categories": ["formation_code"],"term": "111SPOL","definition": "Spoil"}, -{"categories": ["formation_code"],"term": "110TURT","definition": "Tuerto Gravel of Santa Fe Group"}, -{"categories": ["formation_code"],"term": "221RCPR","definition": "Recapture Shale Member of Morrison Formation"}, -{"categories": ["formation_code"],"term": "320BLNG","definition": "Bullington Member of Magdalena Formation"}, -{"categories": ["formation_code"],"term": "112ANCHsr","definition": "Upper Santa Fe Group, Ancha Formation & ancestral Santa Fe river deposits"}, -{"categories": ["formation_code"],"term": "121TSUQae","definition": "Tesuque Fm Lithosomes A and E"}, -{"categories": ["formation_code"],"term": "230TRSC","definition": "Triassic undifferentiated"}, -{"categories": ["formation_code"],"term": "122TSUQdx","definition": "Tesuque Fm, Dixon member (Ttd)"}, -{"categories": ["formation_code"],"term": "123PICSu","definition": "T upper Picuris Formation (Tpu)"}, -{"categories": ["formation_code"],"term": "123PICSm","definition": "T middle Picuris Formation (Tpm)"}, -{"categories": ["formation_code"],"term": "123PICSmc","definition": "T middle conglomerate Picuris Formation (Tpmc)"}, -{"categories": ["formation_code"],"term": "120VBVC","definition": "Tertiary volcanic breccia/volcaniclastic conglomerate"}, -{"categories": ["formation_code"],"term": "120VCSS","definition": "Tertiary volcaniclastic sandstone"}, -{"categories": ["formation_code"],"term": "124DMDT","definition": "Diamond Tail Formation"}, -{"categories": ["formation_code"],"term": "325ALMT","definition": "Penn Alamitos Formation"}, -{"categories": ["formation_code"],"term": "400SAND","definition": "Sandia Granite"}, -{"categories": ["formation_code"],"term": "318VCPK","definition": "Victorio Peak Limestone"}, -{"categories": ["formation_code"],"term": "318BSVP","definition": "Bone Spring and Victorio Peak Limestones"}, -{"categories": ["formation_code"],"term": "100ALVM","definition": "Alluvium"}, -{"categories": ["formation_code"],"term": "310PRMN","definition": "Permian System"}, -{"categories": ["formation_code"],"term": "110AVPS","definition": "Alluvium and Permian System"}, -{"categories": ["formation_code"],"term": "313CRCX","definition": "Capitan Reef Complex and Associated Limestones"}, -{"categories": ["formation_code"],"term": "112SLBL","definition": "Salt Bolson"}, -{"categories": ["formation_code"],"term": "112SBCRC","definition": "Salt Bolson and Capitan Reef Complex"}, -{"categories": ["formation_code"],"term": "313CRDM","definition": "Capitan Reef Complex - Delaware Mountain Group"}, -{"categories": ["formation_code"],"term": "112SBDM","definition": "Salt Bolson and Delaware Mountain Group"}, -{"categories": ["formation_code"],"term": "120BLSN","definition": "Bolson Deposits"}, -{"categories": ["formation_code"],"term": "112SBCR","definition": "Salt Bolson and Cretaceous Rocks"}, -{"categories": ["formation_code"],"term": "112HCBL","definition": "Hueco Bolson"}, -{"categories": ["formation_code"],"term": "120IVIG","definition": "Intrusive Rocks"}, -{"categories": ["formation_code"],"term": "112RLBL","definition": "Red Light Draw Bolson"}, -{"categories": ["formation_code"],"term": "112EFBL","definition": "Eagle Flat Bolson"}, -{"categories": ["formation_code"],"term": "112GRBL","definition": "Green River Bolson"}, -{"categories": ["formation_code"],"term": "123SAND","definition": "Sanders Canyon Formation"}, -{"categories": ["formation_code"],"term": "210MRNH","definition": "Moreno Hill Formation"}, -{"categories": ["formation_code"],"term": "320ALMT","definition": "Alamito Shale"}, -{"categories": ["formation_code"],"term": "313DLRM","definition": "Delaware Mountain Group"}, -{"categories": ["formation_code"],"term": "300PLZC","definition": "Paleozoic Erathem"}, -{"categories": ["formation_code"],"term": "122SPRS","definition": "Spears Member of Datil Formation"}, -{"categories": ["formation_code"],"term": "110AVTV","definition": "Alluvium and Tertiary Volcanics"}, -{"categories": ["formation_code"],"term": "313DMBS","definition": "Delaware Mountain Group - Bone Spring Limestone"}, -{"categories": ["formation_code"],"term": "120ERSV","definition": "Tertiary extrusives"} + {"categories": ["formation_code"],"term": "000IRSV","definition": "Intrusive Rocks"}, + {"categories": ["formation_code"],"term": "050QUAL","definition": "Quaternary Alluvium in Valleys"}, + {"categories": ["formation_code"],"term": "100QBAS","definition": "Quaternary basalt"}, + {"categories": ["formation_code"],"term": "110ALVM","definition": "Quaternary Alluvium"}, + {"categories": ["formation_code"],"term": "110AVMB","definition": "Alluvium, Bolson Deposits and Other Surface Deposits"}, + {"categories": ["formation_code"],"term": "110BLSN","definition": "Bolson Fill"}, + {"categories": ["formation_code"],"term": "110NTGU","definition": "Naha and Tsegi Alluvium Deposits, undifferentiated"}, + {"categories": ["formation_code"],"term": "110PTODC","definition": "Pediment, Terrace and Other Deposits of Gravel, Sand and Caliche"}, + {"categories": ["formation_code"],"term": "111MCCR","definition": "McCathys Basalt Flow"}, + {"categories": ["formation_code"],"term": "112ANCH","definition": "Upper Santa Fe Group, Ancha Formation (QTa)"}, + {"categories": ["formation_code"],"term": "112CURB","definition": "Cuerbio Basalt"}, + {"categories": ["formation_code"],"term": "112LAMA","definition": "Lama Formation (QTl, QTbh) and other mountain front alluvial fans"}, + {"categories": ["formation_code"],"term": "112LAMAb","definition": "Lama Fm (QTl, QTbh) between Servilleta Basalts"}, + {"categories": ["formation_code"],"term": "112LGUN","definition": "Laguna Basalt Flow"}, + {"categories": ["formation_code"],"term": "112QTBF","definition": "Quaternary-Tertiary basin fill (not in valleys)"}, + {"categories": ["formation_code"],"term": "112QTBFlac","definition": "Quaternary-Tertiary basin fill, lacustrian-playa lithofacies"}, + {"categories": ["formation_code"],"term": "112QTBFpd","definition": "Quaternary-Tertiary basin fill, distal piedmont lithofacies"}, + {"categories": ["formation_code"],"term": "112QTBFppm","definition": "Quaternary-Tertiary basin fill, proximal and medial piedmont lithofacies"}, + {"categories": ["formation_code"],"term": "112SNTF","definition": "Santa Fe Group, undivided"}, + {"categories": ["formation_code"],"term": "112SNTFA","definition": "Upper Santa Fe Group, axial facies"}, + {"categories": ["formation_code"],"term": "112SNTFOB","definition": "Upper SantaFe Group, Loma Barbon member of Arroyo Ojito Formatin"}, + {"categories": ["formation_code"],"term": "112SNTFP","definition": "Upper Santa Fe Group, piedmont facies"}, + {"categories": ["formation_code"],"term": "112TRTO","definition": "Tuerto Gravels (QTt)"}, + {"categories": ["formation_code"],"term": "120DTIL","definition": "Datil Formation"}, + {"categories": ["formation_code"],"term": "120ELRT","definition": "El Rito Formation"}, + {"categories": ["formation_code"],"term": "120IRSV","definition": "Tertiary Intrusives"}, + {"categories": ["formation_code"],"term": "120SBLC","definition": "Sierra Blanca Volcanics, undivided"}, + {"categories": ["formation_code"],"term": "120SRVB","definition": "Tertiary Servilletta Basalts (Tsb)"}, + {"categories": ["formation_code"],"term": "120SRVBf","definition": "Tertiary Servilletta Basalts, fractured (Tsbf)"}, + {"categories": ["formation_code"],"term": "120TSBV_Lower","definition": "Tertiary Sierra Blanca area lower volcanic unit (Hog Pen Fm)"}, + {"categories": ["formation_code"],"term": "120TSBV_Upper","definition": "Tertiary Sierra Blanca area upper volcanic unit (above Hog Pen Fm)"}, + {"categories": ["formation_code"],"term": "121CHMT","definition": "Chamita Formation (Tc)"}, + {"categories": ["formation_code"],"term": "121CHMTv","definition": "Chamita Fm, Vallito member (Tcv)"}, + {"categories": ["formation_code"],"term": "121CHMTvs","definition": "Chamita Fm, sandy Vallito member (Tcvs)"}, + {"categories": ["formation_code"],"term": "121OGLL","definition": "Ogallala Formation"}, + {"categories": ["formation_code"],"term": "121PUYEF","definition": "Puye Conglomerate, Fanglomerate Member"}, + {"categories": ["formation_code"],"term": "121TSUQ","definition": "Tesuque Formation, undifferentiated unit"}, + {"categories": ["formation_code"],"term": "121TSUQa","definition": "Tesuque Fm lithosome A (Tta)"}, + {"categories": ["formation_code"],"term": "121TSUQacu","definition": "Tesuque Fm (upper), Cuarteles member lithosome A (Ttacu)"}, + {"categories": ["formation_code"],"term": "121TSUQacuf","definition": "Tesuque Fm (upper), fine-grained Cuarteles member lithosome A (Ttacuf)"}, + {"categories": ["formation_code"],"term": "121TSUQaml","definition": "Tesuque Fm lower-middle lithosome A (Ttaml)"}, + {"categories": ["formation_code"],"term": "121TSUQb","definition": "Tesuque Fm lithosome B (Ttb)"}, + {"categories": ["formation_code"],"term": "121TSUQbfl","definition": "Tesuque Fm lower lithosome B, basin-floor deposits (Ttbfl)"}, + {"categories": ["formation_code"],"term": "121TSUQbfm","definition": "Tesuque Fm middle lithosome B, basin-floor deposits (Ttbfm)"}, + {"categories": ["formation_code"],"term": "121TSUQbp","definition": "Tesuque Fm lithosome B, Pojoaque member (Ttbp)"}, + {"categories": ["formation_code"],"term": "121TSUQce","definition": "Tesuque Fm, Cejita member (Ttce)"}, + {"categories": ["formation_code"],"term": "121TSUQe","definition": "Tesuque Fm lithosome E (Tte)"}, + {"categories": ["formation_code"],"term": "121TSUQs","definition": "Tesuque Fm lithosome S (Tts)"}, + {"categories": ["formation_code"],"term": "121TSUQsa","definition": "Tesuque Fm lateral gradation lithosomes S and A (Ttsag)"}, + {"categories": ["formation_code"],"term": "121TSUQsc","definition": "Tesuque Fm coarse-grained lithosome S (Ttsc)"}, + {"categories": ["formation_code"],"term": "121TSUQsf","definition": "Tesuque Fm, fine-grained lithosome S (Ttsf)"}, + {"categories": ["formation_code"],"term": "122CHOC","definition": "Chamita and Ojo Caliente interlayered (Ttoc)"}, + {"categories": ["formation_code"],"term": "122CRTO","definition": "Chama El Rito Formation (Tesuque member, Ttc)"}, + {"categories": ["formation_code"],"term": "122OJOC","definition": "Ojo Caliente Formation (Tesuque member, Tto)"}, + {"categories": ["formation_code"],"term": "122PICR","definition": "Picuris Tuff"}, + {"categories": ["formation_code"],"term": "122PPTS","definition": "Popotosa Formation"}, + {"categories": ["formation_code"],"term": "122SNTFP","definition": "Lower Santa Fe Group, piedmont facies"}, + {"categories": ["formation_code"],"term": "123DTILSPRS","definition": "Datil Group ignimbrites and lavas and Spears Group, interbedded"}, + {"categories": ["formation_code"],"term": "123DTMGandbas","definition": "Datil and Mogollon Group andesite, basaltic andesite, and basalt flows"}, + {"categories": ["formation_code"],"term": "123DTMGign","definition": "Datil and Mogollon Group ignimbrites"}, + {"categories": ["formation_code"],"term": "123DTMGrhydac","definition": "Datil and Mogollon Group rhyolite and dacite flows"}, + {"categories": ["formation_code"],"term": "123ESPN","definition": "T Espinaso Formation (Te)"}, + {"categories": ["formation_code"],"term": "123GLST","definition": "T Galisteo Formation"}, + {"categories": ["formation_code"],"term": "123PICS","definition": "T Picuris Formation (Tp)"}, + {"categories": ["formation_code"],"term": "123PICSc","definition": "T Picuris Formation, basal conglomerate (Tpc)"}, + {"categories": ["formation_code"],"term": "123PICSl","definition": "T lower Picuris Formation (Tpl)"}, + {"categories": ["formation_code"],"term": "123SPRSDTMGlava","definition": "Spears Group and Datil-Mogollon intermediate-mafic lavas, interbedded"}, + {"categories": ["formation_code"],"term": "123SPRSlower","definition": "Spears Group, lower part; tuffaceous, gravelly debris and mud flows"}, + {"categories": ["formation_code"],"term": "123SPRSmid_uppe","definition": "Spears Group, middle-upper part; excludes Dog Spring Formation"}, + {"categories": ["formation_code"],"term": "124BACA","definition": "Baca Formation"}, + {"categories": ["formation_code"],"term": "124CBMN","definition": "Cub Mountain Formation"}, + {"categories": ["formation_code"],"term": "124LLVS","definition": "Llaves Member of San Jose Formation"}, + {"categories": ["formation_code"],"term": "124PSCN","definition": "Poison Canyon Formation"}, + {"categories": ["formation_code"],"term": "124RGIN","definition": "Regina Member of San Jose Formation"}, + {"categories": ["formation_code"],"term": "124SNJS","definition": "San Jose Formation"}, + {"categories": ["formation_code"],"term": "124TPCS","definition": "TapicitosMember of San Jose Formation"}, + {"categories": ["formation_code"],"term": "125NCMN","definition": "Nacimiento Formation"}, + {"categories": ["formation_code"],"term": "125NCMNS","definition": "Nacimiento Formation, Sandy Shale Facies"}, + {"categories": ["formation_code"],"term": "125RTON","definition": "Raton Formation"}, + {"categories": ["formation_code"],"term": "130CALDFLOOR","definition": "Caldera Floor bedrock S. of San Agustin Plains. Mostly DTILSPRS & Paleo."}, + {"categories": ["formation_code"],"term": "180TKSCC_Upper","definition": "Tertiary-Cretaceous, Sanders Canyon, Cub Mtn. and upper Crevasse Canyon Fm"}, + {"categories": ["formation_code"],"term": "180TKTR","definition": "Tertiary-Cretaceous-Triassic, Baca, Crevasse Cyn, Gallup, Mancos, Dakota, T"}, + {"categories": ["formation_code"],"term": "210CRCS","definition": "Cretaceous System, undivided"}, + {"categories": ["formation_code"],"term": "210GLUPC_Lower","definition": "K Gallup Sandstone and lower Crevasse Canyon Fm"}, + {"categories": ["formation_code"],"term": "210HOSTD","definition": "K Hosta Dalton"}, + {"categories": ["formation_code"],"term": "210MCDK","definition": "K Mancos/Dakota undivided"}, + {"categories": ["formation_code"],"term": "210MNCS","definition": "Mancos Shale, undivided"}, + {"categories": ["formation_code"],"term": "210MNCSL","definition": "K Lower Mancos"}, + {"categories": ["formation_code"],"term": "210MNCSU","definition": "K Upper Mancos"}, + {"categories": ["formation_code"],"term": "211CLFHV","definition": "Cliff House Sandstone, includes La Ventana Tongues in NW Sandoval Co."}, + {"categories": ["formation_code"],"term": "211CRLL","definition": "Carlile Shale"}, + {"categories": ["formation_code"],"term": "211CRVC","definition": "Crevasse Canyon Formation of Mesaverde Group"}, + {"categories": ["formation_code"],"term": "211DKOT","definition": "Dakota Sandstone or Formation"}, + {"categories": ["formation_code"],"term": "211DLCO","definition": "Dilco Coal Member of Crevasse Canyon Formation of Mesaverde Group"}, + {"categories": ["formation_code"],"term": "211DLTN","definition": "Dalton Sandstone Member of Crevasse Canyon Formation of Mesaverde Group"}, + {"categories": ["formation_code"],"term": "211FRHS","definition": "Fort Hays Limestone Member of Niobrara Formation"}, + {"categories": ["formation_code"],"term": "211FRLD","definition": "Fruitland Formation"}, + {"categories": ["formation_code"],"term": "211FRMG","definition": "Farmington Sandstone Member of Kirtland Shale"}, + {"categories": ["formation_code"],"term": "211GBSNC","definition": "Gibson Coal Member of Crevasse Canyon Formation of Mesaverde Group"}, + {"categories": ["formation_code"],"term": "211GLLG","definition": "Gallego Sandstone Member of Gallup Sandstone"}, + {"categories": ["formation_code"],"term": "211GLLP","definition": "Gallup Sandstone"}, + {"categories": ["formation_code"],"term": "211GRRG","definition": "Greenhorn and Graneros Formations"}, + {"categories": ["formation_code"],"term": "211GRRS","definition": "Graneros Shale"}, + {"categories": ["formation_code"],"term": "211HOST","definition": "Hosta Tongue of Point Lookout Sandstone of Mesaverde Group"}, + {"categories": ["formation_code"],"term": "211KRLD","definition": "Kirtland Shale"}, + {"categories": ["formation_code"],"term": "211LWIS","definition": "Lewis Shale"}, + {"categories": ["formation_code"],"term": "211MENF","definition": "Menefee Formation"}, + {"categories": ["formation_code"],"term": "211MENFU","definition": "K Upper Menefee (above Harmon Sandstone)"}, + {"categories": ["formation_code"],"term": "211MVRD","definition": "Mesaverde Group"}, + {"categories": ["formation_code"],"term": "211OJAM","definition": "Ojo Alamo Sandstone"}, + {"categories": ["formation_code"],"term": "211PCCF","definition": "Pictured Cliffs Sandstone"}, + {"categories": ["formation_code"],"term": "211PIRR","definition": "Pierre Shale"}, + {"categories": ["formation_code"],"term": "211PNLK","definition": "Point Lookout Sandstone"}, + {"categories": ["formation_code"],"term": "211SMKH","definition": "Smoky Hill Marl Member"}, + {"categories": ["formation_code"],"term": "211TLLS","definition": "Twowells Sandstone Lentil of Pike of Dakota Sandstone"}, + {"categories": ["formation_code"],"term": "212KTRP","definition": "K Dakota Sandstone, Moenkopi Fm, Artesia Group"}, + {"categories": ["formation_code"],"term": "217PRGR","definition": "Purgatoire Formation"}, + {"categories": ["formation_code"],"term": "220ENRD","definition": "Entrada Sandstone"}, + {"categories": ["formation_code"],"term": "220JURC","definition": "Jurassic undivided"}, + {"categories": ["formation_code"],"term": "220NAVJ","definition": "Navajo Sandstone"}, + {"categories": ["formation_code"],"term": "221BLFF","definition": "Bluff Sandstone of Morrison Formation"}, + {"categories": ["formation_code"],"term": "221CSPG","definition": "Cow Springs Sandstone of Morrison Formation"}, + {"categories": ["formation_code"],"term": "221ERADU","definition": "Entrada Sandstone of San Rafael Group, Upper"}, + {"categories": ["formation_code"],"term": "221MRSN","definition": "Morrison Formation"}, + {"categories": ["formation_code"],"term": "221MRSN/BBSN","definition": "Brushy Basin Member of Morrison"}, + {"categories": ["formation_code"],"term": "221MRSN/JCKP","definition": "Jackpile Sandstone Member of Morrison"}, + {"categories": ["formation_code"],"term": "221MRSN/RCAP","definition": "Recapture Shale Member of Morrison"}, + {"categories": ["formation_code"],"term": "221MRSN/WWCN","definition": "Westwater Canyon Member of Morrison"}, + {"categories": ["formation_code"],"term": "221SLWS","definition": "Salt Wash Sandstone Member of Morrison Formation"}, + {"categories": ["formation_code"],"term": "221SMVL","definition": "Summerville Formation of San Rafael Group"}, + {"categories": ["formation_code"],"term": "221TDLT","definition": "J Todilto"}, + {"categories": ["formation_code"],"term": "221WSRC","definition": "Westwater Canyon Sandstone Member of Morrison Formation"}, + {"categories": ["formation_code"],"term": "221ZUNIS","definition": "Zuni Sandstone"}, + {"categories": ["formation_code"],"term": "231AGZC","definition": "Tr Agua Zarca"}, + {"categories": ["formation_code"],"term": "231AGZCU","definition": "Tr Upper Agua Zarca"}, + {"categories": ["formation_code"],"term": "231CHNL","definition": "Chinle Formation"}, + {"categories": ["formation_code"],"term": "231CORR","definition": "Correo Sandstone Member of Chinle Formation"}, + {"categories": ["formation_code"],"term": "231DCKM","definition": "Dockum Group"}, + {"categories": ["formation_code"],"term": "231PFDF","definition": "Tr Petrified Forest"}, + {"categories": ["formation_code"],"term": "231PFDFL","definition": "Tr Lower Petrified Forest (below middle sandstone)"}, + {"categories": ["formation_code"],"term": "231PFDFM","definition": "Tr Middle Petrified Forest sandstone"}, + {"categories": ["formation_code"],"term": "231PFDFU","definition": "Tr Upper Petrified Forest (above middle sandstone)"}, + {"categories": ["formation_code"],"term": "231RCKP","definition": "Rock Point Member of Wingate Sandstone"}, + {"categories": ["formation_code"],"term": "231SNRS","definition": "Santa Rosa Sandstone"}, + {"categories": ["formation_code"],"term": "231SNSL","definition": "Sonsela Sandstone Bed of Petrified Forest Member of Chinle Formation"}, + {"categories": ["formation_code"],"term": "231SRMP","definition": "Shinarump Member of Chinle Formation"}, + {"categories": ["formation_code"],"term": "231WNGT","definition": "Wingate Sandstone"}, + {"categories": ["formation_code"],"term": "260SNAN","definition": "P San Andres"}, + {"categories": ["formation_code"],"term": "260SNAN_lower","definition": "Lower San Andres Formation"}, + {"categories": ["formation_code"],"term": "261SNGL","definition": "P San Andres - Glorieta Sandstone in Rio Bonito member"}, + {"categories": ["formation_code"],"term": "300YESO","definition": "P Yeso"}, + {"categories": ["formation_code"],"term": "300YESO_lower","definition": "Lower Yeso Formation"}, + {"categories": ["formation_code"],"term": "300YESO_upper","definition": "Upper Yeso Formation"}, + {"categories": ["formation_code"],"term": "310ABO","definition": "P Abo"}, + {"categories": ["formation_code"],"term": "310DCLL","definition": "De Chelly Sandstone Member of Cutler Formation"}, + {"categories": ["formation_code"],"term": "310GLOR","definition": "Glorieta Sandstone Member of San Andres Formation (of Manzano Group)"}, + {"categories": ["formation_code"],"term": "310MBLC","definition": "Meseta Blanca Sandstone Member of Yeso Formation"}, + {"categories": ["formation_code"],"term": "310TRRS","definition": "Torres Member of Yeso Formation"}, + {"categories": ["formation_code"],"term": "310YESO","definition": "Yeso Formation"}, + {"categories": ["formation_code"],"term": "310YESOG","definition": "Yeso Formation, Manzono Group"}, + {"categories": ["formation_code"],"term": "312CSTL","definition": "Castile Formation"}, + {"categories": ["formation_code"],"term": "312RSLR","definition": "Rustler Formation"}, + {"categories": ["formation_code"],"term": "313ARTS","definition": "Artesia Group"}, + {"categories": ["formation_code"],"term": "313BLCN","definition": "Bell Canyon Formation"}, + {"categories": ["formation_code"],"term": "313BRUC","definition": "Brushy Canyon Formation of Delaware Mountain Group"}, + {"categories": ["formation_code"],"term": "313CKBF","definition": "Chalk Bluff Formation"}, + {"categories": ["formation_code"],"term": "313CLBD","definition": "Carlsbad Limestone"}, + {"categories": ["formation_code"],"term": "313CPTN","definition": "Capitan Limestone"}, + {"categories": ["formation_code"],"term": "313GDLP","definition": "Guadalupian Series"}, + {"categories": ["formation_code"],"term": "313GOSP","definition": "Goat Seep Dolomite"}, + {"categories": ["formation_code"],"term": "313SADG","definition": "San Andres Limestone and Glorieta Sandstone"}, + {"categories": ["formation_code"],"term": "313SADR","definition": "San Andres Limestone, undivided"}, + {"categories": ["formation_code"],"term": "313TNSL","definition": "Tansill Formation"}, + {"categories": ["formation_code"],"term": "313YATS","definition": "Yates Formation, Guadalupe Group"}, + {"categories": ["formation_code"],"term": "315LABR","definition": "P Laborcita (Bursum)"}, + {"categories": ["formation_code"],"term": "315YESOABO","definition": "Alamosa Creek and San Agustin Plains area - Yeso and Abo Formations"}, + {"categories": ["formation_code"],"term": "318ABO","definition": "P Abo"}, + {"categories": ["formation_code"],"term": "318BSPG","definition": "Bone Spring Limestone"}, + {"categories": ["formation_code"],"term": "318JOYT","definition": "Joyita Sandstone Member of Yeso Formation"}, + {"categories": ["formation_code"],"term": "318YESO","definition": "Yeso Formation"}, + {"categories": ["formation_code"],"term": "319BRSM","definition": "Bursum Formation and Equivalent Rocks"}, + {"categories": ["formation_code"],"term": "320HLDR","definition": "Penn Holder"}, + {"categories": ["formation_code"],"term": "320PENN","definition": "Pennsylvanian undivided"}, + {"categories": ["formation_code"],"term": "320SNDI","definition": "Sandia Formation"}, + {"categories": ["formation_code"],"term": "321SGDC","definition": "Sangre de Cristo Formation"}, + {"categories": ["formation_code"],"term": "322BEMN","definition": "Penn Beeman"}, + {"categories": ["formation_code"],"term": "325GBLR","definition": "Penn Gobbler"}, + {"categories": ["formation_code"],"term": "325MDER","definition": "Madera Limestone, undivided"}, + {"categories": ["formation_code"],"term": "325MDERL","definition": "Penn Lower Madera"}, + {"categories": ["formation_code"],"term": "325MDERU","definition": "Penn Upper Madera"}, + {"categories": ["formation_code"],"term": "325SAND","definition": "Penn Sandia"}, + {"categories": ["formation_code"],"term": "326MGDL","definition": "Magdalena Group"}, + {"categories": ["formation_code"],"term": "340EPRS","definition": "Espiritu Santo Formation"}, + {"categories": ["formation_code"],"term": "350PZBA","definition": "Alamosa Creek and San Agustin Plains area - Paleozoic strata beneath Abo Fm"}, + {"categories": ["formation_code"],"term": "350PZBB","definition": "Tul Basin area - Paleozoic strata below Bursum Fm"}, + {"categories": ["formation_code"],"term": "400EMBD","definition": "Embudo Granite (undifferentiated PreCambrian near Santa Fe)"}, + {"categories": ["formation_code"],"term": "400PCMB","definition": "Precambrian Erathem"}, + {"categories": ["formation_code"],"term": "400PREC","definition": "undifferentiated PreCambrian crystalline rocks (X)"}, + {"categories": ["formation_code"],"term": "400PRECintr","definition": "PreCambrian crystalline rocks and local Tertiary intrusives"}, + {"categories": ["formation_code"],"term": "400PRST","definition": "Priest Granite"}, + {"categories": ["formation_code"],"term": "400TUSS","definition": "Tusas Granite"}, + {"categories": ["formation_code"],"term": "410PRCG","definition": "PreCambrian granite (Xg)"}, + {"categories": ["formation_code"],"term": "410PRCGf","definition": "PreCambrian granite, fractured (Xgf)"}, + {"categories": ["formation_code"],"term": "410PRCQ","definition": "PreCambrian quartzite (Xq)"}, + {"categories": ["formation_code"],"term": "410PRCQf","definition": "PreCambrian quartzite, fractured (Xqf)"}, + {"categories": ["formation_code"],"term": "121GILA","definition": "Gila Conglomerate (group)"}, + {"categories": ["formation_code"],"term": "312DYLK","definition": "Dewey Lake Redbeds"}, + {"categories": ["formation_code"],"term": "120WMVL","definition": "Wimsattville Formation"}, + {"categories": ["formation_code"],"term": "313GRBG","definition": "Grayburg Formation of Artesia Group"}, + {"categories": ["formation_code"],"term": "318ABOL","definition": "Abo Sandstone (Lower Tongue)"}, + {"categories": ["formation_code"],"term": "318ABOU","definition": "Abo Sandstone (Upper Tongue)"}, + {"categories": ["formation_code"],"term": "112SNTFU","definition": "Santa Fe Group, Upper Part"}, + {"categories": ["formation_code"],"term": "310FRNR","definition": "Forty-Niner Member of Rustler Formation"}, + {"categories": ["formation_code"],"term": "312OCHO","definition": "Ochoan Series"}, + {"categories": ["formation_code"],"term": "313AZOT","definition": "Azotea Tongue of Seven Rivers Formation"}, + {"categories": ["formation_code"],"term": "313QUEN","definition": "Queen Formation"}, + {"categories": ["formation_code"],"term": "319HUCO","definition": "Hueco Limestone"}, + {"categories": ["formation_code"],"term": "313SVRV","definition": "Seven Rivers Formation"}, + {"categories": ["formation_code"],"term": "313CABD","definition": "Carlsbad Group"}, + {"categories": ["formation_code"],"term": "320GRMS","definition": "Gray Mesa Member of Madera Formation"}, + {"categories": ["formation_code"],"term": "211CLRDH","definition": "Colorado Shale"}, + {"categories": ["formation_code"],"term": "120BRLM","definition": "Bearwallow Mountain Andesite"}, + {"categories": ["formation_code"],"term": "122RUBO","definition": "Rubio Peak Formation"}, + {"categories": ["formation_code"],"term": "313SADRL","definition": "San Andres Limestone, Lower Cherty Member"}, + {"categories": ["formation_code"],"term": "313SADRU","definition": "San Andres Limestone, Upper Clastic Member"}, + {"categories": ["formation_code"],"term": "313BRNL","definition": "Bernal Formation of Artesia Group"}, + {"categories": ["formation_code"],"term": "318CPDR","definition": "Chupadera Formation"}, + {"categories": ["formation_code"],"term": "121BDHC","definition": "Bidahochi Formation"}, + {"categories": ["formation_code"],"term": "313SADY","definition": "San Andres Limestone and Yeso Formation, undivided"}, + {"categories": ["formation_code"],"term": "221SRFLL","definition": "San Rafael Group, Lower Part"}, + {"categories": ["formation_code"],"term": "221BLUF","definition": "Bluff Sandstone of Morrison Formation"}, + {"categories": ["formation_code"],"term": "221COSP","definition": "Cow Springs Sandstone of Morrison Formation"}, + {"categories": ["formation_code"],"term": "317ABYS","definition": "Abo and Yeso, undifferentiated"}, + {"categories": ["formation_code"],"term": "221BRSB","definition": "Brushy Basin Shale Member of Morrison Formation"}, + {"categories": ["formation_code"],"term": "310SYDR","definition": "San Ysidro Member of Yeso Formation"}, + {"categories": ["formation_code"],"term": "400SDVL","definition": "Sandoval Granite"}, + {"categories": ["formation_code"],"term": "221SRFL","definition": "San Rafael Group"}, + {"categories": ["formation_code"],"term": "310SGRC","definition": "Sangre de Cristo Formation"}, + {"categories": ["formation_code"],"term": "231TCVS","definition": "Tecovas Formation of Dockum Group"}, + {"categories": ["formation_code"],"term": "211DCRS","definition": "D-Cross Tongue of Mancos Shale of Mesaverde Group"}, + {"categories": ["formation_code"],"term": "211ALSN","definition": "Allison Member of Menefee Formation of Mesaverde Group"}, + {"categories": ["formation_code"],"term": "211LVNN","definition": "La Ventana Tongue of Cliff House Sandstone"}, + {"categories": ["formation_code"],"term": "211MORD","definition": "Madrid Formation"}, + {"categories": ["formation_code"],"term": "210PRMD","definition": "Pyramid Shale"}, + {"categories": ["formation_code"],"term": "124ANMS","definition": "Animas Formation"}, + {"categories": ["formation_code"],"term": "211NBRR","definition": "Niobrara Formation"}, + {"categories": ["formation_code"],"term": "111ALVM","definition": "Holocene Alluvium"}, + {"categories": ["formation_code"],"term": "122SNTFL","definition": "Santa Fe Group, Lower Part"}, + {"categories": ["formation_code"],"term": "111CPLN","definition": "Capulin Basalts"}, + {"categories": ["formation_code"],"term": "120CRSN","definition": "Carson Conflomerate"}, + {"categories": ["formation_code"],"term": "111CRMS","definition": "Covered/Reclaimed Mine Spoil"}, + {"categories": ["formation_code"],"term": "111CRMSA","definition": "Covered/Reclaimed Mine Spoil and Ash"}, + {"categories": ["formation_code"],"term": "111SPOL","definition": "Spoil"}, + {"categories": ["formation_code"],"term": "110TURT","definition": "Tuerto Gravel of Santa Fe Group"}, + {"categories": ["formation_code"],"term": "221RCPR","definition": "Recapture Shale Member of Morrison Formation"}, + {"categories": ["formation_code"],"term": "320BLNG","definition": "Bullington Member of Magdalena Formation"}, + {"categories": ["formation_code"],"term": "112ANCHsr","definition": "Upper Santa Fe Group, Ancha Formation & ancestral Santa Fe river deposits"}, + {"categories": ["formation_code"],"term": "121TSUQae","definition": "Tesuque Fm Lithosomes A and E"}, + {"categories": ["formation_code"],"term": "230TRSC","definition": "Triassic undifferentiated"}, + {"categories": ["formation_code"],"term": "122TSUQdx","definition": "Tesuque Fm, Dixon member (Ttd)"}, + {"categories": ["formation_code"],"term": "123PICSu","definition": "T upper Picuris Formation (Tpu)"}, + {"categories": ["formation_code"],"term": "123PICSm","definition": "T middle Picuris Formation (Tpm)"}, + {"categories": ["formation_code"],"term": "123PICSmc","definition": "T middle conglomerate Picuris Formation (Tpmc)"}, + {"categories": ["formation_code"],"term": "120VBVC","definition": "Tertiary volcanic breccia/volcaniclastic conglomerate"}, + {"categories": ["formation_code"],"term": "120VCSS","definition": "Tertiary volcaniclastic sandstone"}, + {"categories": ["formation_code"],"term": "124DMDT","definition": "Diamond Tail Formation"}, + {"categories": ["formation_code"],"term": "325ALMT","definition": "Penn Alamitos Formation"}, + {"categories": ["formation_code"],"term": "400SAND","definition": "Sandia Granite"}, + {"categories": ["formation_code"],"term": "318VCPK","definition": "Victorio Peak Limestone"}, + {"categories": ["formation_code"],"term": "318BSVP","definition": "Bone Spring and Victorio Peak Limestones"}, + {"categories": ["formation_code"],"term": "100ALVM","definition": "Alluvium"}, + {"categories": ["formation_code"],"term": "310PRMN","definition": "Permian System"}, + {"categories": ["formation_code"],"term": "110AVPS","definition": "Alluvium and Permian System"}, + {"categories": ["formation_code"],"term": "313CRCX","definition": "Capitan Reef Complex and Associated Limestones"}, + {"categories": ["formation_code"],"term": "112SLBL","definition": "Salt Bolson"}, + {"categories": ["formation_code"],"term": "112SBCRC","definition": "Salt Bolson and Capitan Reef Complex"}, + {"categories": ["formation_code"],"term": "313CRDM","definition": "Capitan Reef Complex - Delaware Mountain Group"}, + {"categories": ["formation_code"],"term": "112SBDM","definition": "Salt Bolson and Delaware Mountain Group"}, + {"categories": ["formation_code"],"term": "120BLSN","definition": "Bolson Deposits"}, + {"categories": ["formation_code"],"term": "112SBCR","definition": "Salt Bolson and Cretaceous Rocks"}, + {"categories": ["formation_code"],"term": "112HCBL","definition": "Hueco Bolson"}, + {"categories": ["formation_code"],"term": "120IVIG","definition": "Intrusive Rocks"}, + {"categories": ["formation_code"],"term": "112RLBL","definition": "Red Light Draw Bolson"}, + {"categories": ["formation_code"],"term": "112EFBL","definition": "Eagle Flat Bolson"}, + {"categories": ["formation_code"],"term": "112GRBL","definition": "Green River Bolson"}, + {"categories": ["formation_code"],"term": "123SAND","definition": "Sanders Canyon Formation"}, + {"categories": ["formation_code"],"term": "210MRNH","definition": "Moreno Hill Formation"}, + {"categories": ["formation_code"],"term": "320ALMT","definition": "Alamito Shale"}, + {"categories": ["formation_code"],"term": "313DLRM","definition": "Delaware Mountain Group"}, + {"categories": ["formation_code"],"term": "300PLZC","definition": "Paleozoic Erathem"}, + {"categories": ["formation_code"],"term": "122SPRS","definition": "Spears Member of Datil Formation"}, + {"categories": ["formation_code"],"term": "110AVTV","definition": "Alluvium and Tertiary Volcanics"}, + {"categories": ["formation_code"],"term": "313DMBS","definition": "Delaware Mountain Group - Bone Spring Limestone"}, + {"categories": ["formation_code"],"term": "120ERSV","definition": "Tertiary extrusives"}, + {"categories": ["lithology"],"term": "Alluvium","definition": "Alluvium"}, + {"categories": ["lithology"],"term": "Anhydrite","definition": "Anhydrite"}, + {"categories": ["lithology"],"term": "Arkose","definition": "Arkose"}, + {"categories": ["lithology"],"term": "Boulders","definition": "Boulders"}, + {"categories": ["lithology"],"term": "Boulders, silt and clay","definition": "Boulders, silt and clay"}, + {"categories": ["lithology"],"term": "Boulders and sand","definition": "Boulders and sand"}, + {"categories": ["lithology"],"term": "Bentonite","definition": "Bentonite"}, + {"categories": ["lithology"],"term": "Breccia","definition": "Breccia"}, + {"categories": ["lithology"],"term": "Basalt","definition": "Basalt"}, + {"categories": ["lithology"],"term": "Conglomerate","definition": "Conglomerate"}, + {"categories": ["lithology"],"term": "Chalk","definition": "Chalk"}, + {"categories": ["lithology"],"term": "Chert","definition": "Chert"}, + {"categories": ["lithology"],"term": "Clay","definition": "Clay"}, + {"categories": ["lithology"],"term": "Caliche","definition": "Caliche"}, + {"categories": ["lithology"],"term": "Calcite","definition": "Calcite"}, + {"categories": ["lithology"],"term": "Clay, some sand","definition": "Clay, some sand"}, + {"categories": ["lithology"],"term": "Claystone","definition": "Claystone"}, + {"categories": ["lithology"],"term": "Coal","definition": "Coal"}, + {"categories": ["lithology"],"term": "Cobbles","definition": "Cobbles"}, + {"categories": ["lithology"],"term": "Cobbles, silt and clay","definition": "Cobbles, silt and clay"}, + {"categories": ["lithology"],"term": "Cobbles and sand","definition": "Cobbles and sand"}, + {"categories": ["lithology"],"term": "Dolomite","definition": "Dolomite"}, + {"categories": ["lithology"],"term": "Dolomite and shale","definition": "Dolomite and shale"}, + {"categories": ["lithology"],"term": "Evaporite","definition": "Evaporite"}, + {"categories": ["lithology"],"term": "Gneiss","definition": "Gneiss"}, + {"categories": ["lithology"],"term": "Gypsum","definition": "Gypsum"}, + {"categories": ["lithology"],"term": "Graywacke","definition": "Graywacke"}, + {"categories": ["lithology"],"term": "Gravel and clay","definition": "Gravel and clay"}, + {"categories": ["lithology"],"term": "Gravel, cemented","definition": "Gravel, cemented"}, + {"categories": ["lithology"],"term": "Gravel, sand and silt","definition": "Gravel, sand and silt"}, + {"categories": ["lithology"],"term": "Granite, gneiss","definition": "Granite, gneiss"}, + {"categories": ["lithology"],"term": "Granite","definition": "Granite"}, + {"categories": ["lithology"],"term": "Gravel, silt and clay","definition": "Gravel, silt and clay"}, + {"categories": ["lithology"],"term": "Gravel","definition": "Gravel"}, + {"categories": ["lithology"],"term": "Igneous undifferentiated","definition": "Igneous undifferentiated"}, + {"categories": ["lithology"],"term": "Lignite","definition": "Lignite"}, + {"categories": ["lithology"],"term": "Limestone and dolomite","definition": "Limestone and dolomite"}, + {"categories": ["lithology"],"term": "Limestone and shale","definition": "Limestone and shale"}, + {"categories": ["lithology"],"term": "Limestone","definition": "Limestone"}, + {"categories": ["lithology"],"term": "Marl","definition": "Marl"}, + {"categories": ["lithology"],"term": "Mudstone","definition": "Mudstone"}, + {"categories": ["lithology"],"term": "Metamorphic undifferentiated","definition": "Metamorphic undifferentiated"}, + {"categories": ["lithology"],"term": "Marlstone","definition": "Marlstone"}, + {"categories": ["lithology"],"term": "No Recovery","definition": "No Recovery"}, + {"categories": ["lithology"],"term": "Peat","definition": "Peat"}, + {"categories": ["lithology"],"term": "Quartzite","definition": "Quartzite"}, + {"categories": ["lithology"],"term": "Rhyolite","definition": "Rhyolite"}, + {"categories": ["lithology"],"term": "Sand","definition": "Sand"}, + {"categories": ["lithology"],"term": "Schist","definition": "Schist"}, + {"categories": ["lithology"],"term": "Sand and clay","definition": "Sand and clay"}, + {"categories": ["lithology"],"term": "Sand and gravel","definition": "Sand and gravel"}, + {"categories": ["lithology"],"term": "Sandstone and shale","definition": "Sandstone and shale"}, + {"categories": ["lithology"],"term": "Sand and silt","definition": "Sand and silt"}, + {"categories": ["lithology"],"term": "Sand, gravel and clay","definition": "Sand, gravel and clay"}, + {"categories": ["lithology"],"term": "Shale","definition": "Shale"}, + {"categories": ["lithology"],"term": "Silt","definition": "Silt"}, + {"categories": ["lithology"],"term": "Siltstone and shale","definition": "Siltstone and shale"}, + {"categories": ["lithology"],"term": "Siltstone","definition": "Siltstone"}, + {"categories": ["lithology"],"term": "Slate","definition": "Slate"}, + {"categories": ["lithology"],"term": "Sand, some clay","definition": "Sand, some clay"}, + {"categories": ["lithology"],"term": "Sandstone","definition": "Sandstone"}, + {"categories": ["lithology"],"term": "Silt and clay","definition": "Silt and clay"}, + {"categories": ["lithology"],"term": "Travertine","definition": "Travertine"}, + {"categories": ["lithology"],"term": "Tuff","definition": "Tuff"}, + {"categories": ["lithology"],"term": "Volcanic undifferentiated","definition": "Volcanic undifferentiated"}, + {"categories": ["lithology"],"term": "Clay, yellow","definition": "Clay, yellow"}, + {"categories": ["lithology"],"term": "Clay, red","definition": "Clay, red"}, + {"categories": ["lithology"],"term": "Surficial sediment","definition": "Surficial sediment"}, + {"categories": ["lithology"],"term": "Limestone and sandstone, interbedded","definition": "Limestone and sandstone, interbedded"}, + {"categories": ["lithology"],"term": "Gravel and boulders","definition": "Gravel and boulders"}, + {"categories": ["lithology"],"term": "Sand, silt and gravel","definition": "Sand, silt and gravel"}, + {"categories": ["lithology"],"term": "Sand, gravel, silt and clay","definition": "Sand, gravel, silt and clay"}, + {"categories": ["lithology"],"term": "Andesite","definition": "Andesite"}, + {"categories": ["lithology"],"term": "Ignesous, intrusive, undifferentiated","definition": "Ignesous, intrusive, undifferentiated"}, + {"categories": ["lithology"],"term": "Limestone, sandstone and shale","definition": "Limestone, sandstone and shale"}, + {"categories": ["lithology"],"term": "Sand, silt and clay","definition": "Sand, silt and clay"} ] } \ No newline at end of file diff --git a/db/geologic_formation.py b/db/geologic_formation.py index a29c20010..d8a7c7515 100644 --- a/db/geologic_formation.py +++ b/db/geologic_formation.py @@ -25,12 +25,14 @@ class GeologicFormation(Base, AutoBaseMixin, ReleaseMixin): __versioned__ = {} - # TODO: Should `name` use a controlled vocabulary? - name: Mapped[str] = mapped_column( - nullable=False, - unique=True, - comment="The full, human-readable name of the geologic formation (e.g., 'Navajo Sandstone').", - ) + # TODO: Let the API map formation codes to names using a formations.json file that can be periodically updated + # from the authoritative source (.e.g USGS). A placeholder `formations.json` file had been added to the `core` + # directory. + # name: Mapped[str] = mapped_column( + # nullable=False, + # unique=True, + # comment="The full, human-readable name of the geologic formation (e.g., 'Navajo Sandstone').", + # ) formation_code: Mapped[str] = lexicon_term( nullable=True, unique=True, @@ -43,7 +45,7 @@ class GeologicFormation(Base, AutoBaseMixin, ReleaseMixin): ) # TODO: Implement controlled vocabularies for `lithology` using NMAquifer's 'LU_Lithology' table. # This should be implemented after AMMP reviews and cleans up their formation terms and codes. - lithology: Mapped[str] = mapped_column( + lithology: Mapped[str] = lexicon_term( nullable=True, comment="A controlled vocabulary for the primary, dominant rock type" "(e.g., 'Tuff', 'Sandstone', 'Alluvium', 'Shale').", From 4d55177c3ee2e65fbffc3f5f80e890ee8e7aae56 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Tue, 18 Nov 2025 15:58:30 -0700 Subject: [PATCH 24/33] feat(core): create placeholder `formations.json` This is a placeholder file to allow the API to map formation names to codes. It can be periodically updated from the authoritative source (.e.g USGS). --- core/formations.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 core/formations.json diff --git a/core/formations.json b/core/formations.json new file mode 100644 index 000000000..e69de29bb From 8362f9c5a3f695380f453cf543663b94b2c8fa92 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Tue, 18 Nov 2025 17:07:12 -0700 Subject: [PATCH 25/33] refactor: add aquifer and geology related models to `db/__init__.py` --- db/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/db/__init__.py b/db/__init__.py index efb23a418..68c94d598 100644 --- a/db/__init__.py +++ b/db/__init__.py @@ -40,6 +40,10 @@ from db.status_history import * from db.thing import * from db.transducer import * +from db.aquifer_system import * +from db.geologic_formation import * +from db.thing_aquifer_association import * +from db.thing_formation_association import * from sqlalchemy import ( func, From 39aeefa597bfd91ecedf72ac58ea899f0f857d7b Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Tue, 18 Nov 2025 17:12:15 -0700 Subject: [PATCH 26/33] feat: add aquifer and geology related enums. --- core/enums.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/enums.py b/core/enums.py index 5833d97bc..0da1d2eef 100644 --- a/core/enums.py +++ b/core/enums.py @@ -70,4 +70,7 @@ ScreenType: type[Enum] = build_enum_from_lexicon_category("screen_type") SensorType: type[Enum] = build_enum_from_lexicon_category("sensor_type") WellPumpType: type[Enum] = build_enum_from_lexicon_category("well_pump_type") +AquiferType: type[Enum] = build_enum_from_lexicon_category("aquifer_type") +GeographicScale: type[Enum] = build_enum_from_lexicon_category("geographic_scale") +Lithology: type[Enum] = build_enum_from_lexicon_category("lithology") # ============= EOF ============================================= From 47b8415f05b7a4f37dfb3d5d4f3deeb383b81148 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Tue, 18 Nov 2025 19:59:14 -0700 Subject: [PATCH 27/33] feat(schemas): add response schemas for aquifer systems and geologic formations Add comprehensive response schemas for AquiferSystem and GeologicFormation models. Integrate these schemas into Thing/Well response models to provide complete geological context. Changes: - Add AquiferSystemResponse with name, type, scale, and boundary fields - Add GeologicFormationResponse with formation code, lithology, and boundary - Add ThingFormationAssociationResponse with depth interval data - Update WellResponse to include aquifers and formations lists - Add field validators to convert ORM association objects to response schemas - Update WellScreenResponse to include aquifer_system and geologic_formation - Update CreateWellScreen and UpdateWellScreen with geological associations --- schemas/aquifer_system.py | 32 ++++++++++++++++++++++++ schemas/geologic_formation.py | 46 +++++++++++++++++++++++++++++++++++ schemas/thing.py | 32 ++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 schemas/aquifer_system.py create mode 100644 schemas/geologic_formation.py diff --git a/schemas/aquifer_system.py b/schemas/aquifer_system.py new file mode 100644 index 000000000..5f5b3ed4d --- /dev/null +++ b/schemas/aquifer_system.py @@ -0,0 +1,32 @@ +from typing import List + +from pydantic import BaseModel + +from schemas import BaseResponseModel + + +# ------ RESPONSE ---------- +class GeoJSONGeometry(BaseModel): + """ + Geometry schema for GeoJSON response. + """ + + type: str + coordinates: ( + List[float] + | List[List[float]] + | List[List[List[float]]] + | List[List[List[List[float]]]] + ) + + +class AquiferSystemResponse(BaseResponseModel): + """ + Response schema for aquifer system details. + """ + + name: str + description: str | None = None + aquifer_type: str + geographic_scale: str + boundary: GeoJSONGeometry | None = None diff --git a/schemas/geologic_formation.py b/schemas/geologic_formation.py new file mode 100644 index 000000000..2f5f0432d --- /dev/null +++ b/schemas/geologic_formation.py @@ -0,0 +1,46 @@ +from typing import List + +from pydantic import BaseModel + +from schemas import BaseResponseModel + + +# ------ RESPONSE ---------- +class GeoJSONGeometry(BaseModel): + """ + Geometry schema for GeoJSON response. + """ + + type: str + coordinates: ( + List[float] + | List[List[float]] + | List[List[List[float]]] + | List[List[List[List[float]]]] + ) + + +class GeologicFormationResponse(BaseResponseModel): + """ + Response schema for geologic formation details. + """ + + formation_code: str | None = None + description: str | None = None + lithology: str | None = None + boundary: GeoJSONGeometry | None = None + + +class ThingFormationAssociationResponse(BaseResponseModel): + """ + Response schema for the association between a Thing and a GeologicFormation. + Includes depth interval information. + """ + + thing_id: int + geologic_formation_id: int | None = None + geologic_formation: GeologicFormationResponse | None = None + top_depth: float + top_depth_unit: str = "ft" + bottom_depth: float + bottom_depth_unit: str = "ft" diff --git a/schemas/thing.py b/schemas/thing.py index d87fd299f..36f3eb757 100644 --- a/schemas/thing.py +++ b/schemas/thing.py @@ -27,6 +27,11 @@ ) from schemas import BaseCreateModel, BaseUpdateModel, BaseResponseModel from schemas.location import LocationResponse +from schemas.aquifer_system import AquiferSystemResponse +from schemas.geologic_formation import ( + GeologicFormationResponse, + ThingFormationAssociationResponse, +) # -------- VALIDATE ---------- @@ -121,6 +126,8 @@ class CreateWellScreen(BaseCreateModel): """ thing_id: int + aquifer_system_id: int | None = None + geologic_formation_id: int | None = None screen_depth_bottom: float = Field(gt=0, description="Screen depth bottom in feet") screen_depth_top: float = Field(gt=0, description="Screen depth top in feet") screen_type: ScreenType | None = None @@ -166,6 +173,8 @@ class WellResponse(BaseThingResponse): well_pump_type: WellPumpType | None well_pump_depth: float | None well_pump_depth_unit: str = "ft" + aquifers: list[AquiferSystemResponse] = [] + formations: list[ThingFormationAssociationResponse] = [] @field_validator("well_purposes", mode="before") def populate_well_purposes_with_strings(cls, well_purposes): @@ -186,6 +195,23 @@ def populate_well_casing_materials_with_strings(cls, well_casing_materials): materials = [] return materials + @field_validator("aquifers", mode="before") + def populate_aquifers(cls, aquifers): + """Convert aquifer association objects to aquifer system objects.""" + if aquifers is not None: + # Handle if aquifers are already AquiferSystem objects + if hasattr(aquifers[0] if aquifers else None, "aquifer_system"): + return [assoc.aquifer_system for assoc in aquifers] + return aquifers or [] + + @field_validator("formations", mode="before") + def populate_formations(cls, formations): + """Convert formation association objects to response objects.""" + if formations is not None: + # formations should already be ThingFormationAssociation objects + return formations + return [] + class SpringResponse(BaseThingResponse): """ @@ -222,6 +248,10 @@ class WellScreenResponse(BaseResponseModel): thing_id: int thing: WellResponse + aquifer_system_id: int | None = None + aquifer_system: AquiferSystemResponse | None = None + geologic_formation_id: int | None = None + geologic_formation: GeologicFormationResponse | None = None screen_depth_bottom: float screen_depth_bottom_unit: str = "ft" screen_depth_top: float @@ -295,6 +325,8 @@ class UpdateThingIdLink(BaseUpdateModel): class UpdateWellScreen(BaseUpdateModel): + aquifer_system_id: int | None = None + geologic_formation_id: int | None = None screen_depth_bottom: float | None = None screen_depth_top: float | None = None screen_description: str | None = None From d1149f500e7fb2de6618b94970c8b9393824bc42 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Wed, 19 Nov 2025 12:20:22 -0700 Subject: [PATCH 28/33] refactor: rename `ThingFormationAssociation` usages to `ThingGeologicFormationAssociation` --- db/geologic_formation.py | 16 +++++++++------- db/thing.py | 14 ++++++++------ db/thing_formation_association.py | 4 ++-- schemas/geologic_formation.py | 2 +- schemas/thing.py | 6 +++--- 5 files changed, 23 insertions(+), 19 deletions(-) diff --git a/db/geologic_formation.py b/db/geologic_formation.py index d8a7c7515..af12d7a8a 100644 --- a/db/geologic_formation.py +++ b/db/geologic_formation.py @@ -19,7 +19,7 @@ if TYPE_CHECKING: from db.thing import Thing, WellScreen - from db.thing_formation_association import ThingFormationAssociation + from db.thing_formation_association import ThingGeologicFormationAssociation class GeologicFormation(Base, AutoBaseMixin, ReleaseMixin): @@ -58,12 +58,14 @@ class GeologicFormation(Base, AutoBaseMixin, ReleaseMixin): # --- Relationships --- # One-To-Many (Association Object): A GeologicFormation can be associated with many Things (e.g., wells) via the - # ThingFormationAssociation join table. - thing_associations: Mapped[List["ThingFormationAssociation"]] = relationship( - "ThingFormationAssociation", - back_populates="geologic_formation", - cascade="all, delete-orphan", - passive_deletes=True, + # ThingGeologicFormationAssociation join table. + thing_associations: Mapped[List["ThingGeologicFormationAssociation"]] = ( + relationship( + "ThingGeologicFormationAssociation", + back_populates="geologic_formation", + cascade="all, delete-orphan", + passive_deletes=True, + ) ) # One-To-Many: A GeologicFormation can have many physical WellScreens installed in it. screens: Mapped[List["WellScreen"]] = relationship( diff --git a/db/thing.py b/db/thing.py index 25dcb0969..106af773e 100644 --- a/db/thing.py +++ b/db/thing.py @@ -42,7 +42,7 @@ from db.aquifer_system import AquiferSystem from db.thing_aquifer_association import ThingAquiferAssociation from db.geologic_formation import GeologicFormation - from db.thing_formation_association import ThingFormationAssociation + from db.thing_formation_association import ThingGeologicFormationAssociation class Thing( @@ -275,11 +275,13 @@ class Thing( ) # Many-To-Many: A Thing can penetrate many GeologicFormations. - formation_associations: Mapped[List["ThingFormationAssociation"]] = relationship( - "ThingFormationAssociation", - back_populates="thing", - cascade="all, delete-orphan", - passive_deletes=True, + formation_associations: Mapped[List["ThingGeologicFormationAssociation"]] = ( + relationship( + "ThingGeologicFormationAssociation", + back_populates="thing", + cascade="all, delete-orphan", + passive_deletes=True, + ) ) # --- Association Proxies --- diff --git a/db/thing_formation_association.py b/db/thing_formation_association.py index 8904fa089..0707df269 100644 --- a/db/thing_formation_association.py +++ b/db/thing_formation_association.py @@ -1,5 +1,5 @@ """ -SQLAlchemy model for the ThingFormationAssociation table. +SQLAlchemy model for the ThingGeologicFormationAssociation table. This table is an association object that creates a many-to-many relationship between a Thing (well) and a GeologicFormation. It stores the lithology for a well, detailing the depth intervals for each formation it penetrates. @@ -17,7 +17,7 @@ from db.geologic_formation import GeologicFormation -class ThingFormationAssociation(Base, AutoBaseMixin, ReleaseMixin): +class ThingGeologicFormationAssociation(Base, AutoBaseMixin, ReleaseMixin): """ This is a= join table (Association Object). It represents the association of a Thing to a GeologicFormation at a specific depth interval. diff --git a/schemas/geologic_formation.py b/schemas/geologic_formation.py index 2f5f0432d..f6b3083d3 100644 --- a/schemas/geologic_formation.py +++ b/schemas/geologic_formation.py @@ -31,7 +31,7 @@ class GeologicFormationResponse(BaseResponseModel): boundary: GeoJSONGeometry | None = None -class ThingFormationAssociationResponse(BaseResponseModel): +class ThingGeologicFormationAssociationResponse(BaseResponseModel): """ Response schema for the association between a Thing and a GeologicFormation. Includes depth interval information. diff --git a/schemas/thing.py b/schemas/thing.py index 36f3eb757..398630e66 100644 --- a/schemas/thing.py +++ b/schemas/thing.py @@ -30,7 +30,7 @@ from schemas.aquifer_system import AquiferSystemResponse from schemas.geologic_formation import ( GeologicFormationResponse, - ThingFormationAssociationResponse, + ThingGeologicFormationAssociationResponse, ) @@ -174,7 +174,7 @@ class WellResponse(BaseThingResponse): well_pump_depth: float | None well_pump_depth_unit: str = "ft" aquifers: list[AquiferSystemResponse] = [] - formations: list[ThingFormationAssociationResponse] = [] + formations: list[ThingGeologicFormationAssociationResponse] = [] @field_validator("well_purposes", mode="before") def populate_well_purposes_with_strings(cls, well_purposes): @@ -208,7 +208,7 @@ def populate_aquifers(cls, aquifers): def populate_formations(cls, formations): """Convert formation association objects to response objects.""" if formations is not None: - # formations should already be ThingFormationAssociation objects + # formations should already be ThingGeologicFormationAssociation objects return formations return [] From c85f9714d2619db92b2cfdc84e28eddb9199c7a5 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Wed, 19 Nov 2025 12:22:39 -0700 Subject: [PATCH 29/33] refactor: rename `ThingFormationAssociation` file`ThingGeologicFormationAssociation` --- db/__init__.py | 2 +- db/geologic_formation.py | 4 +++- db/thing.py | 4 +++- ...association.py => thing_geologic_formation_association.py} | 0 4 files changed, 7 insertions(+), 3 deletions(-) rename db/{thing_formation_association.py => thing_geologic_formation_association.py} (100%) diff --git a/db/__init__.py b/db/__init__.py index 68c94d598..ef14f7c06 100644 --- a/db/__init__.py +++ b/db/__init__.py @@ -43,7 +43,7 @@ from db.aquifer_system import * from db.geologic_formation import * from db.thing_aquifer_association import * -from db.thing_formation_association import * +from db.thing_geologic_formation_association import * from sqlalchemy import ( func, diff --git a/db/geologic_formation.py b/db/geologic_formation.py index af12d7a8a..130ed8d45 100644 --- a/db/geologic_formation.py +++ b/db/geologic_formation.py @@ -19,7 +19,9 @@ if TYPE_CHECKING: from db.thing import Thing, WellScreen - from db.thing_formation_association import ThingGeologicFormationAssociation + from db.thing_geologic_formation_association import ( + ThingGeologicFormationAssociation, + ) class GeologicFormation(Base, AutoBaseMixin, ReleaseMixin): diff --git a/db/thing.py b/db/thing.py index 106af773e..12a6ae5ed 100644 --- a/db/thing.py +++ b/db/thing.py @@ -42,7 +42,9 @@ from db.aquifer_system import AquiferSystem from db.thing_aquifer_association import ThingAquiferAssociation from db.geologic_formation import GeologicFormation - from db.thing_formation_association import ThingGeologicFormationAssociation + from db.thing_geologic_formation_association import ( + ThingGeologicFormationAssociation, + ) class Thing( diff --git a/db/thing_formation_association.py b/db/thing_geologic_formation_association.py similarity index 100% rename from db/thing_formation_association.py rename to db/thing_geologic_formation_association.py From 853e4507743fe9ed25a14b9fa757894e2c323eb5 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Wed, 19 Nov 2025 12:49:14 -0700 Subject: [PATCH 30/33] refactor(schema): remove aquifer and formation field validators from `schemas/thing.py` --- schemas/thing.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/schemas/thing.py b/schemas/thing.py index 398630e66..8a63c431f 100644 --- a/schemas/thing.py +++ b/schemas/thing.py @@ -195,23 +195,6 @@ def populate_well_casing_materials_with_strings(cls, well_casing_materials): materials = [] return materials - @field_validator("aquifers", mode="before") - def populate_aquifers(cls, aquifers): - """Convert aquifer association objects to aquifer system objects.""" - if aquifers is not None: - # Handle if aquifers are already AquiferSystem objects - if hasattr(aquifers[0] if aquifers else None, "aquifer_system"): - return [assoc.aquifer_system for assoc in aquifers] - return aquifers or [] - - @field_validator("formations", mode="before") - def populate_formations(cls, formations): - """Convert formation association objects to response objects.""" - if formations is not None: - # formations should already be ThingGeologicFormationAssociation objects - return formations - return [] - class SpringResponse(BaseThingResponse): """ From e9639bff8de328c9cf9cd213b3c0366c6119a6f5 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Sun, 23 Nov 2025 14:46:08 -0700 Subject: [PATCH 31/33] feat(model): Add `AquiferType` model and rename `aquifer_type` to `primary_aquifer_type` - Add AquiferType model to store multiple aquifer characteristics per association - Rename AquiferSystem.aquifer_type to primary_aquifer_type for clarity - Update ThingAquiferAssociation with aquifer_types relationship - Update related schemas to use primary_type This enables capturing wells that encounter multiple aquifer characteristics (e.g., both fractured and confined) without compound naming. --- db/__init__.py | 1 + db/aquifer_system.py | 9 +++-- db/aquifer_type.py | 58 +++++++++++++++++++++++++++++++++ db/thing_aquifer_association.py | 8 +++++ schemas/aquifer_system.py | 16 ++++++++- 5 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 db/aquifer_type.py diff --git a/db/__init__.py b/db/__init__.py index ef14f7c06..65ab4cd32 100644 --- a/db/__init__.py +++ b/db/__init__.py @@ -44,6 +44,7 @@ from db.geologic_formation import * from db.thing_aquifer_association import * from db.thing_geologic_formation_association import * +from db.aquifer_type import * from sqlalchemy import ( func, diff --git a/db/aquifer_system.py b/db/aquifer_system.py index 95066c2e8..b72e91949 100644 --- a/db/aquifer_system.py +++ b/db/aquifer_system.py @@ -18,6 +18,7 @@ if TYPE_CHECKING: from db.thing import WellScreen, ThingAquiferAssociation, Thing + from db.aquifer_type import AquiferType class AquiferSystem(Base, AutoBaseMixin, ReleaseMixin): @@ -34,9 +35,9 @@ class AquiferSystem(Base, AutoBaseMixin, ReleaseMixin): comment="A detailed description of the aquifer system, its characteristics, and its significance.", ) # Lexicon terms were retrieved from NMAquifer's 'LU_AquiferType' table. - aquifer_type: Mapped[str] = lexicon_term( + primary_aquifer_type: Mapped[str] = lexicon_term( nullable=False, - comment="A controlled vocabulary field to classify the aquifer's hydrologic properties (e.g., 'Unconfined', 'Confined', 'Perched').", + comment="A controlled vocabulary field to classify the aquifer's primary hydrologic properties (e.g., 'Unconfined', 'Confined', 'Perched').", ) geographic_scale: Mapped[str] = lexicon_term( nullable=False, @@ -74,6 +75,10 @@ class AquiferSystem(Base, AutoBaseMixin, ReleaseMixin): things: AssociationProxy[List["Thing"]] = association_proxy( "thing_associations", "thing" ) + # Proxy to directly access all AquiferTypes associated with this AquiferSystem. + aquifer_types: AssociationProxy[List["AquiferType"]] = association_proxy( + "thing_associations", "aquifer_types" + ) # --- Table Arguments --- __table_args__ = Index("ix_aquifersystem_name", "name") diff --git a/db/aquifer_type.py b/db/aquifer_type.py new file mode 100644 index 000000000..32900d801 --- /dev/null +++ b/db/aquifer_type.py @@ -0,0 +1,58 @@ +""" +SQLAlchemy model for the AquiferType table. + +This table stores the specific aquifer characteristics/types associated with +a Thing-AquiferSystem relationship. It allows capturing that a single aquifer +can have multiple characteristics simultaneously. + +Example: + A well in the "Ogallala" aquifer might tap portions that are both + "Fractured" AND "Confined". This would create: + - One AquiferSystem: "Ogallala" + - One ThingAquiferAssociation: linking well to Ogallala + - Two AquiferType records: "Fractured" and "Confined" +""" + +from typing import TYPE_CHECKING + +from sqlalchemy import ForeignKey +from sqlalchemy.orm import relationship, Mapped, mapped_column + +from db.base import Base, AutoBaseMixin, ReleaseMixin, lexicon_term + +if TYPE_CHECKING: + from db.thing_aquifer_association import ThingAquiferAssociation + + +class AquiferType(Base, AutoBaseMixin, ReleaseMixin): + """ + Represents the specific aquifer types/characteristics for a + Thing-AquiferSystem association. + + This allows modeling the fact that: + - A single aquifer can have multiple characteristics + - Different wells may tap different characteristics of the same aquifer + - Characteristics are attributes of the relationship, not the aquifer itself + + Fields from WellData CSV: + - AquiferType: May contain multiple codes (e.g., "FC" = Fractured + Confined) + - Each code becomes a separate AquiferType record + """ + + # --- Columns --- + thing_aquifer_association_id: Mapped[int] = mapped_column( + ForeignKey("thing_aquifer_association.id", ondelete="CASCADE"), + nullable=False, + comment="Links to the Thing-Aquifer association this type describes.", + ) + aquifer_type: Mapped[str] = lexicon_term( + nullable=False, + comment="Controlled vocabulary for aquifer hydrologic properties. " + "Examples: 'Unconfined', 'Confined', 'Perched', 'Fractured', 'Unconsolidated'.", + ) + + # --- Relationships --- + # Many-to-One: Multiple aquifer types can belong to one association + thing_aquifer_association: Mapped["ThingAquiferAssociation"] = relationship( + "ThingAquiferAssociation", back_populates="aquifer_types" + ) diff --git a/db/thing_aquifer_association.py b/db/thing_aquifer_association.py index d5fb2881a..414f09269 100644 --- a/db/thing_aquifer_association.py +++ b/db/thing_aquifer_association.py @@ -16,6 +16,7 @@ if TYPE_CHECKING: from db.thing import Thing from db.aquifer_system import AquiferSystem + from db.aquifer_type import AquiferType class ThingAquiferAssociation(Base, AutoBaseMixin, ReleaseMixin): @@ -40,3 +41,10 @@ class ThingAquiferAssociation(Base, AutoBaseMixin, ReleaseMixin): aquifer_system: Mapped["AquiferSystem"] = relationship( "AquiferSystem", back_populates="thing_associations", lazy="joined" ) + # One-To-Many: An association can have multiple aquifer types. + aquifer_types: Mapped[list["AquiferType"]] = relationship( + "AquiferType", + back_populates="thing_aquifer_association", + cascade="all, delete-orphan", + passive_deletes=True, + ) diff --git a/schemas/aquifer_system.py b/schemas/aquifer_system.py index 5f5b3ed4d..4b4448211 100644 --- a/schemas/aquifer_system.py +++ b/schemas/aquifer_system.py @@ -5,6 +5,20 @@ from schemas import BaseResponseModel +# ------ CREATE ---------- +class CreateAquiferSystem(BaseModel): + """ + Schema for creating an aquifer system. + Used during data transfer and API creation. + """ + + name: str + description: str | None = None + primary_aquifer_type: str + geographic_scale: str + boundary: str | None = None + + # ------ RESPONSE ---------- class GeoJSONGeometry(BaseModel): """ @@ -27,6 +41,6 @@ class AquiferSystemResponse(BaseResponseModel): name: str description: str | None = None - aquifer_type: str + primary_aquifer_type: str geographic_scale: str boundary: GeoJSONGeometry | None = None From ba2b2964550ffc7def0d12dd5cd7fa495f0b5401 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Mon, 24 Nov 2025 11:13:21 -0700 Subject: [PATCH 32/33] feat(model): add eager loading Add eager loading to aquifer and geology relationships to the `thing` model. --- db/aquifer_system.py | 2 +- db/thing.py | 2 ++ db/thing_aquifer_association.py | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/db/aquifer_system.py b/db/aquifer_system.py index b72e91949..53086f983 100644 --- a/db/aquifer_system.py +++ b/db/aquifer_system.py @@ -37,7 +37,7 @@ class AquiferSystem(Base, AutoBaseMixin, ReleaseMixin): # Lexicon terms were retrieved from NMAquifer's 'LU_AquiferType' table. primary_aquifer_type: Mapped[str] = lexicon_term( nullable=False, - comment="A controlled vocabulary field to classify the aquifer's primary hydrologic properties (e.g., 'Unconfined', 'Confined', 'Perched').", + comment="A controlled vocabulary field to classify the aquifer system as a whole (e.g., 'Unconfined', 'Confined', 'Perched').", ) geographic_scale: Mapped[str] = lexicon_term( nullable=False, diff --git a/db/thing.py b/db/thing.py index 0bb361294..d2592f7ac 100644 --- a/db/thing.py +++ b/db/thing.py @@ -290,6 +290,7 @@ class Thing( back_populates="thing", cascade="all, delete-orphan", passive_deletes=True, + lazy="joined", ) # Many-To-Many: A Thing can penetrate many GeologicFormations. @@ -299,6 +300,7 @@ class Thing( back_populates="thing", cascade="all, delete-orphan", passive_deletes=True, + lazy="joined", ) ) diff --git a/db/thing_aquifer_association.py b/db/thing_aquifer_association.py index 414f09269..cca5758a9 100644 --- a/db/thing_aquifer_association.py +++ b/db/thing_aquifer_association.py @@ -47,4 +47,5 @@ class ThingAquiferAssociation(Base, AutoBaseMixin, ReleaseMixin): back_populates="thing_aquifer_association", cascade="all, delete-orphan", passive_deletes=True, + lazy="joined", ) From 910d5b31bbb942894a66236a09a97bdd0b8e2a8f Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Mon, 24 Nov 2025 11:45:51 -0700 Subject: [PATCH 33/33] feat(schema): refactor GeoJSON responses for `aquifer_system` and `geologic_formation` --- schemas/aquifer_system.py | 22 +++++++++++++--------- schemas/geologic_formation.py | 24 ++++++++++++++---------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/schemas/aquifer_system.py b/schemas/aquifer_system.py index 4b4448211..6c077aa90 100644 --- a/schemas/aquifer_system.py +++ b/schemas/aquifer_system.py @@ -25,16 +25,11 @@ class GeoJSONGeometry(BaseModel): Geometry schema for GeoJSON response. """ - type: str - coordinates: ( - List[float] - | List[List[float]] - | List[List[List[float]]] - | List[List[List[List[float]]]] - ) + type: str = "MULTIPOLYGON" + coordinates: List[List[List[float]]] -class AquiferSystemResponse(BaseResponseModel): +class GeoJSONProperties(BaseResponseModel): """ Response schema for aquifer system details. """ @@ -43,4 +38,13 @@ class AquiferSystemResponse(BaseResponseModel): description: str | None = None primary_aquifer_type: str geographic_scale: str - boundary: GeoJSONGeometry | None = None + + +class AquiferSystemGeoJSONResponse(BaseModel): + """ + Response schema for aquifer system details. + """ + + type: str = "Feature" + geometry: GeoJSONGeometry + properties: GeoJSONProperties diff --git a/schemas/geologic_formation.py b/schemas/geologic_formation.py index f6b3083d3..d42e48389 100644 --- a/schemas/geologic_formation.py +++ b/schemas/geologic_formation.py @@ -11,16 +11,11 @@ class GeoJSONGeometry(BaseModel): Geometry schema for GeoJSON response. """ - type: str - coordinates: ( - List[float] - | List[List[float]] - | List[List[List[float]]] - | List[List[List[List[float]]]] - ) + type: str = "MULTIPOLYGON" + coordinates: List[List[List[float]]] -class GeologicFormationResponse(BaseResponseModel): +class GeoJSONProperties(BaseResponseModel): """ Response schema for geologic formation details. """ @@ -28,7 +23,16 @@ class GeologicFormationResponse(BaseResponseModel): formation_code: str | None = None description: str | None = None lithology: str | None = None - boundary: GeoJSONGeometry | None = None + + +class GeologicFormationGeoJSONResponse(BaseModel): + """ + Response schema for geologic formation details. + """ + + type: str = "Feature" + geometry: GeoJSONGeometry + properties: GeoJSONProperties class ThingGeologicFormationAssociationResponse(BaseResponseModel): @@ -39,7 +43,7 @@ class ThingGeologicFormationAssociationResponse(BaseResponseModel): thing_id: int geologic_formation_id: int | None = None - geologic_formation: GeologicFormationResponse | None = None + geologic_formation: GeologicFormationGeoJSONResponse | None = None top_depth: float top_depth_unit: str = "ft" bottom_depth: float