diff --git a/alembic/versions/f1a2b3c4d5e6_add_nma_formation_zone_to_thing.py b/alembic/versions/f1a2b3c4d5e6_add_nma_formation_zone_to_thing.py new file mode 100644 index 00000000..b9cce433 --- /dev/null +++ b/alembic/versions/f1a2b3c4d5e6_add_nma_formation_zone_to_thing.py @@ -0,0 +1,52 @@ +"""Add nma_formation_zone to Thing. + +Revision ID: f1a2b3c4d5e6 +Revises: f3b4c5d6e7f8 +Create Date: 2026-03-01 00:00:00.000000 +""" + +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +from sqlalchemy import inspect + +# revision identifiers, used by Alembic. +revision: str = "f1a2b3c4d5e6" +down_revision: Union[str, Sequence[str], None] = "f3b4c5d6e7f8" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + bind = op.get_bind() + inspector = inspect(bind) + if inspector.has_table("thing"): + columns = {col["name"] for col in inspector.get_columns("thing")} + if "nma_formation_zone" not in columns: + op.add_column( + "thing", + sa.Column("nma_formation_zone", sa.String(length=25), nullable=True), + ) + if inspector.has_table("thing_version"): + columns = {col["name"] for col in inspector.get_columns("thing_version")} + if "nma_formation_zone" not in columns: + op.add_column( + "thing_version", + sa.Column("nma_formation_zone", sa.String(length=25), nullable=True), + ) + + +def downgrade() -> None: + """Downgrade schema.""" + bind = op.get_bind() + inspector = inspect(bind) + if inspector.has_table("thing_version"): + columns = {col["name"] for col in inspector.get_columns("thing_version")} + if "nma_formation_zone" in columns: + op.drop_column("thing_version", "nma_formation_zone") + if inspector.has_table("thing"): + columns = {col["name"] for col in inspector.get_columns("thing")} + if "nma_formation_zone" in columns: + op.drop_column("thing", "nma_formation_zone") diff --git a/db/thing.py b/db/thing.py index 8340de81..8283bdc4 100644 --- a/db/thing.py +++ b/db/thing.py @@ -134,6 +134,11 @@ class Thing( "This indicates the target formation for the well, not the full stratigraphic column. " "For detailed depth-interval stratigraphy, see formation_associations.", ) + nma_formation_zone: Mapped[str] = mapped_column( + String(25), + nullable=True, + comment="Raw FormationZone value from legacy WellData (NM_Aquifer).", + ) # TODO: should this be required for every well in the database? AMMP review is_suitable_for_datalogger: Mapped[bool] = mapped_column( nullable=True, diff --git a/schemas/thing.py b/schemas/thing.py index 9f2a084e..7c3214dc 100644 --- a/schemas/thing.py +++ b/schemas/thing.py @@ -140,6 +140,7 @@ class CreateWell(CreateBaseThing, ValidateWell): well_pump_type: WellPumpType | None = None is_suitable_for_datalogger: bool | None formation_completion_code: FormationCode | None = None + nma_formation_zone: str | None = None class CreateSpring(CreateBaseThing): @@ -248,6 +249,7 @@ class WellResponse(BaseThingResponse): construction_notes: list[NoteResponse] = [] permissions: list[PermissionHistoryResponse] formation_completion_code: FormationCode | None + nma_formation_zone: str | None @field_validator("well_purposes", mode="before") def populate_well_purposes_with_strings(cls, well_purposes): @@ -409,6 +411,7 @@ class UpdateWell(UpdateThing, ValidateWell): well_casing_diameter: float | None = None # in inches well_casing_depth: float | None = None # in feet well_casing_materials: list[str] | None = None + nma_formation_zone: str | None = None class UpdateSpring(UpdateThing): diff --git a/tests/test_thing.py b/tests/test_thing.py index 87a016c0..f60a32f7 100644 --- a/tests/test_thing.py +++ b/tests/test_thing.py @@ -29,7 +29,7 @@ from main import app from schemas import DT_FMT from schemas.location import LocationResponse -from schemas.thing import ValidateWell +from schemas.thing import UpdateWell, ValidateWell from tests import ( client, override_authentication, @@ -78,6 +78,11 @@ def test_validate_hole_depth_casing_depth(): ValidateWell(hole_depth=100.0, well_casing_depth=110.0) +def test_update_well_allows_nma_formation_zone(): + payload = UpdateWell(nma_formation_zone="FZ-001") + assert payload.nma_formation_zone == "FZ-001" + + # this is not a valid test because measuring_point_height is not related to hole_depth # def test_validate_mp_height_hole_depth(): # with pytest.raises( diff --git a/transfers/well_transfer.py b/transfers/well_transfer.py index 48186f6e..17b98026 100644 --- a/transfers/well_transfer.py +++ b/transfers/well_transfer.py @@ -439,6 +439,7 @@ def _add_formation_zone(self, row, well, formations): formation_code = row.FormationZone if formation_code: formation_code = formation_code.strip() + well.nma_formation_zone = formation_code if formation_code in formations: # Formation exists: Set association well.formation_completion_code = formations[ @@ -1325,6 +1326,7 @@ def _step_parallel_complete( if formation_code: formation_code = formation_code.strip() if formation_code else None if formation_code: + well.nma_formation_zone = formation_code if formation_code in local_formations: well.formation_completion_code = local_formations[ formation_code