-
Notifications
You must be signed in to change notification settings - Fork 4
BDMS-227-231-aquifer-geology-models #248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
jacob-a-brown
merged 37 commits into
bdms-227
from
kas-227-231-additional-well-info-models
Nov 24, 2025
Merged
Changes from all commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
826a6b6
feat: pass bdd test for well completion date
jacob-a-brown b34f62e
feat: pass well driller name bdd test
jacob-a-brown 3d89a3e
feat: implement well construction method
jacob-a-brown ea00c9c
feat: implement well casing diameter in inches
jacob-a-brown cd4e174
feat: implement well pump type
jacob-a-brown ca6b6c4
feat: implement well pump depth
jacob-a-brown a0d939f
Merge branch 'bdms-227' into bdms-227-jab-updates-to-pass-tests
jacob-a-brown e1df131
fix: remove duplicate lexicon
jacob-a-brown 8e70c85
Merge branch 'bdms-227-jab-bdms-229' into bdms-227-jab-updates-to-pas…
jacob-a-brown 08d7aed
feat: update tests to include well casing materials
jacob-a-brown 3000e79
Merge branch 'kas-bdms-221-225-core-well-info-models-schemas' into ka…
ksmuczynski 7923551
feat: add 'AquiferSystem' model with relationships and controlled voc…
ksmuczynski 4ddec89
refactor: remove completed TODO about lexicon updates
ksmuczynski 29d7b91
refactor: clarify relationship name and add association proxy
ksmuczynski 0d3f807
refactor: clarify proxy purpose doc statement
ksmuczynski c3393d9
feat: add ThingAquiferAssociation model and update Thing relationships
ksmuczynski af092dd
feat: add `aqufiers` proxy to Thing model
ksmuczynski e330df4
feat: Link WellScreen to AquiferSystem
ksmuczynski 04a28be
feat: add GeologicFormation and ThingFormationAssociation models with…
ksmuczynski 477247a
feat(lexicon): add "formation_code" category and associated terms to …
ksmuczynski d359fa1
rafactor(model): Remove TODO about adding formation_code values to le…
ksmuczynski fc81327
refactor(lexicon): refine terms associated with "geographic_scale".
ksmuczynski b01d172
refactor(model): Replace hardcoded srid with SRID_WGS84 from constants
ksmuczynski f35e61d
refactor(model): Add geologic_formation foreign key to WellScreen model.
ksmuczynski 390d6f3
refactor(model): enhance relationships for `ThingAquiferAssociation` …
ksmuczynski c738afb
refactor(model): refactor `lithology` field to a lexicon_term
ksmuczynski 4d55177
feat(core): create placeholder `formations.json`
ksmuczynski 8362f9c
refactor: add aquifer and geology related models to `db/__init__.py`
ksmuczynski 39aeefa
feat: add aquifer and geology related enums.
ksmuczynski 47b8415
feat(schemas): add response schemas for aquifer systems and geologic …
ksmuczynski d1149f5
refactor: rename `ThingFormationAssociation` usages to `ThingGeologic…
ksmuczynski c85f971
refactor: rename `ThingFormationAssociation` file`ThingGeologicFormat…
ksmuczynski 853e450
refactor(schema): remove aquifer and formation field validators from …
ksmuczynski e9639bf
feat(model): Add `AquiferType` model and rename `aquifer_type` to `pr…
ksmuczynski 1de7f47
Merge branch 'bdms-227' into kas-227-231-additional-well-info-models
ksmuczynski ba2b296
feat(model): add eager loading
ksmuczynski 910d5b3
feat(schema): refactor GeoJSON responses for `aquifer_system` and `ge…
ksmuczynski File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| """ | ||
| 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 sqlalchemy.ext.associationproxy import association_proxy, AssociationProxy | ||
| from geoalchemy2 import Geometry | ||
|
|
||
| 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 | ||
| from db.aquifer_type import AquiferType | ||
|
|
||
|
|
||
| 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. | ||
| primary_aquifer_type: Mapped[str] = lexicon_term( | ||
| nullable=False, | ||
| 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, | ||
| 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=SRID_WGS84, 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. | ||
| thing_associations: 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, | ||
| ) | ||
|
|
||
| # --- Association Proxies --- | ||
| # Proxy to directly access Things (wells) associated with this AquiferSystem. | ||
| things: AssociationProxy[List["Thing"]] = association_proxy( | ||
| "thing_associations", "thing" | ||
| ) | ||
|
ksmuczynski marked this conversation as resolved.
|
||
| # 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") | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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" | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| """ | ||
| 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 | ||
|
|
||
| from constants import SRID_WGS84 | ||
|
|
||
| if TYPE_CHECKING: | ||
| from db.thing import Thing, WellScreen | ||
| from db.thing_geologic_formation_association import ( | ||
| ThingGeologicFormationAssociation, | ||
| ) | ||
|
|
||
|
|
||
| class GeologicFormation(Base, AutoBaseMixin, ReleaseMixin): | ||
| __versioned__ = {} | ||
|
|
||
| # 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, | ||
| 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. | ||
| # This should be implemented after AMMP reviews and cleans up their formation terms and codes. | ||
| 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=SRID_WGS84, 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 | ||
| # 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( | ||
| "WellScreen", back_populates="geologic_formation", 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"), | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.