Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
152 commits
Select commit Hold shift + click to select a range
0ac45c1
WIP: first stab at well additional information
jacob-a-brown Nov 5, 2025
466055b
Merge branch 'staging' into bdms-227-jab-bdms-229
jacob-a-brown Nov 7, 2025
edf3c38
WIP: well additional information bdd tests
jacob-a-brown Nov 7, 2025
7197ede
feat: use function to retrieve polymoprhic records
jacob-a-brown Nov 10, 2025
826a6b6
feat: pass bdd test for well completion date
jacob-a-brown Nov 11, 2025
b34f62e
feat: pass well driller name bdd test
jacob-a-brown Nov 11, 2025
3d89a3e
feat: implement well construction method
jacob-a-brown Nov 11, 2025
ea00c9c
feat: implement well casing diameter in inches
jacob-a-brown Nov 11, 2025
cd4e174
feat: implement well pump type
jacob-a-brown Nov 11, 2025
ca6b6c4
feat: implement well pump depth
jacob-a-brown Nov 11, 2025
7e6f9a1
Merge branch 'bdms-227' into bdms-227-jab-bdms-229
jacob-a-brown Nov 12, 2025
a0d939f
Merge branch 'bdms-227' into bdms-227-jab-updates-to-pass-tests
jacob-a-brown Nov 12, 2025
e1df131
fix: remove duplicate lexicon
jacob-a-brown Nov 12, 2025
7d37154
refactor: test for casing materials
jacob-a-brown Nov 13, 2025
8e70c85
Merge branch 'bdms-227-jab-bdms-229' into bdms-227-jab-updates-to-pas…
jacob-a-brown Nov 13, 2025
08d7aed
feat: update tests to include well casing materials
jacob-a-brown Nov 13, 2025
c0b80fb
fix: fix casing materials test
jacob-a-brown Nov 13, 2025
3000e79
Merge branch 'kas-bdms-221-225-core-well-info-models-schemas' into ka…
ksmuczynski Nov 13, 2025
878ef3b
fix: uncomment pytest from pre commit
jacob-a-brown Nov 13, 2025
13f2a45
refactor: rename Permission -> PermissionHistory | move mixin to same…
jacob-a-brown Nov 13, 2025
6e036e5
feat: add permission types to lexicon
jacob-a-brown Nov 13, 2025
091637c
refactor: import PermissionHistoryMixin from correct dir
jacob-a-brown Nov 13, 2025
71d8534
fix: fix imports for newly renamed PermissionHistory
jacob-a-brown Nov 13, 2025
825ec4a
feat: add permission_type to lexicon
jacob-a-brown Nov 13, 2025
8ed0ce4
feat: update util to correspond with bdms 221
jacob-a-brown Nov 13, 2025
3b36c49
fix: sync bdms 221/227 for util functions
jacob-a-brown Nov 13, 2025
1657bb9
feat: implement permissions
jacob-a-brown Nov 13, 2025
307f47c
feat: add is_suitable_for_datalogger to thing
jacob-a-brown Nov 13, 2025
7923551
feat: add 'AquiferSystem' model with relationships and controlled voc…
ksmuczynski Nov 14, 2025
4ddec89
refactor: remove completed TODO about lexicon updates
ksmuczynski Nov 14, 2025
29d7b91
refactor: clarify relationship name and add association proxy
ksmuczynski Nov 14, 2025
0d3f807
refactor: clarify proxy purpose doc statement
ksmuczynski Nov 14, 2025
c3393d9
feat: add ThingAquiferAssociation model and update Thing relationships
ksmuczynski Nov 16, 2025
af092dd
feat: add `aqufiers` proxy to Thing model
ksmuczynski Nov 16, 2025
e330df4
feat: Link WellScreen to AquiferSystem
ksmuczynski Nov 16, 2025
04a28be
feat: add GeologicFormation and ThingFormationAssociation models with…
ksmuczynski Nov 17, 2025
477247a
feat(lexicon): add "formation_code" category and associated terms to …
ksmuczynski Nov 17, 2025
d359fa1
rafactor(model): Remove TODO about adding formation_code values to le…
ksmuczynski Nov 17, 2025
fc81327
refactor(lexicon): refine terms associated with "geographic_scale".
ksmuczynski Nov 17, 2025
b01d172
refactor(model): Replace hardcoded srid with SRID_WGS84 from constants
ksmuczynski Nov 18, 2025
f35e61d
refactor(model): Add geologic_formation foreign key to WellScreen model.
ksmuczynski Nov 18, 2025
390d6f3
refactor(model): enhance relationships for `ThingAquiferAssociation` …
ksmuczynski Nov 18, 2025
c738afb
refactor(model): refactor `lithology` field to a lexicon_term
ksmuczynski Nov 18, 2025
4d55177
feat(core): create placeholder `formations.json`
ksmuczynski Nov 18, 2025
8362f9c
refactor: add aquifer and geology related models to `db/__init__.py`
ksmuczynski Nov 19, 2025
39aeefa
feat: add aquifer and geology related enums.
ksmuczynski Nov 19, 2025
47b8415
feat(schemas): add response schemas for aquifer systems and geologic …
ksmuczynski Nov 19, 2025
1c4eda8
Merge pull request #237 from DataIntegrationGroup/bdms-227-jab-bdms-229
jacob-a-brown Nov 19, 2025
82e100f
Merge branch 'staging' into bdms-227
jacob-a-brown Nov 19, 2025
38e86b2
Merge branch 'bdms-227' into bdms-227-jab-updates-to-pass-tests
jacob-a-brown Nov 19, 2025
b4c8beb
fix: fix artifacts from merge conflicts
jacob-a-brown Nov 19, 2025
d1149f5
refactor: rename `ThingFormationAssociation` usages to `ThingGeologic…
ksmuczynski Nov 19, 2025
c85f971
refactor: rename `ThingFormationAssociation` file`ThingGeologicFormat…
ksmuczynski Nov 19, 2025
853e450
refactor(schema): remove aquifer and formation field validators from …
ksmuczynski Nov 19, 2025
2c33a79
feat: implement data source tests for well additional information
jacob-a-brown Nov 19, 2025
8ef2592
fix: remove outdated notes
jacob-a-brown Nov 19, 2025
ced10b9
feat: transfer water level sample permissions
jacob-a-brown Nov 19, 2025
e48b71e
feat: transfer permissions from legacy db
jacob-a-brown Nov 20, 2025
171b41b
feat: transfer well construction information
jacob-a-brown Nov 20, 2025
a9cec8a
WIP: transfers for well additional info
jacob-a-brown Nov 20, 2025
722425f
feat: implement origin_type
jacob-a-brown Nov 20, 2025
3c92552
Merge branch 'bdms-227-jab-updates-to-pass-tests' into bdms-227-trans…
jacob-a-brown Nov 20, 2025
75265f3
feat: transfer well source information
jacob-a-brown Nov 20, 2025
2b90f86
feat: add Quemado Mutual Water and Sewage Works Association to organi…
jacob-a-brown Nov 20, 2025
cf72461
fix: remove erroneous or check
jacob-a-brown Nov 20, 2025
2991591
feat(transfers): add `LU_AquiferClass` and `LU_AquiferType` lookup ta…
ksmuczynski Nov 20, 2025
00e7225
feat(schemas): implement Create schemas for `aquifer_system` and `geo…
ksmuczynski Nov 21, 2025
58b92f8
feat: transfer well pump type
jacob-a-brown Nov 21, 2025
4e9876e
fix: change Air-rotary to Air-Rotary to correspond with lu table
jacob-a-brown Nov 21, 2025
e9639bf
feat(model): Add `AquiferType` model and rename `aquifer_type` to `pr…
ksmuczynski Nov 23, 2025
9c924ae
Merge branch 'kas-227-231-additional-well-info-models' into kas-227-2…
ksmuczynski Nov 23, 2025
af07c3e
feat(transfer): WIP aquifer and geology transfers
ksmuczynski Nov 24, 2025
1de7f47
Merge branch 'bdms-227' into kas-227-231-additional-well-info-models
ksmuczynski Nov 24, 2025
ba2b296
feat(model): add eager loading
ksmuczynski Nov 24, 2025
910d5b3
feat(schema): refactor GeoJSON responses for `aquifer_system` and `ge…
ksmuczynski Nov 24, 2025
31b8602
Merge pull request #248 from DataIntegrationGroup/kas-227-231-additio…
jacob-a-brown Nov 24, 2025
827652a
Merge branch 'bdms-227' into bdms-227-jab-updates-to-pass-tests
jacob-a-brown Nov 24, 2025
1342b8d
fix: fix __table_args__ for aquifer and geology
jacob-a-brown Nov 24, 2025
88419b7
fix: add geologic_formation relation to WellScreen
jacob-a-brown Nov 24, 2025
1a23dff
fix: add missing comma
jacob-a-brown Nov 24, 2025
d82b163
fix: use BaseResponseModel for GeoJSONProperties in location schema
jacob-a-brown Nov 24, 2025
b1933e1
Merge branch 'bdms-227' into kas-227-231-additional-well-info-transfe…
ksmuczynski Nov 24, 2025
20af430
fix: include baseresponseinfo in location geojson properties
jacob-a-brown Nov 24, 2025
5778b74
feat: implement aquifer_systems in thing response
jacob-a-brown Nov 24, 2025
c4f9de0
feat: fix aquifer systems test
jacob-a-brown Nov 24, 2025
d3abd83
fix: return aquifer system and types in dict
jacob-a-brown Nov 24, 2025
b4cd84e
feat: implement geologic_formations from feature files
jacob-a-brown Nov 24, 2025
f0fe4c2
Merge branch 'bdms-227-jab-updates-to-pass-tests' into bdms-227-trans…
jacob-a-brown Nov 24, 2025
fbfa908
fix: remove duplicate well pump types in lexicon
jacob-a-brown Nov 24, 2025
748a57b
feat(transfer): add stratigraphy transfer script and update geologic …
ksmuczynski Nov 24, 2025
e6af5e6
fix: use OpenWellLoggerOK for is_suitable_for_datalogger field
jacob-a-brown Nov 24, 2025
d3c8101
feat: update CreateWell for transfers
jacob-a-brown Nov 25, 2025
2ea4cb1
fix: return aquifer/geology str instead of full response
jacob-a-brown Nov 25, 2025
0994bec
refactor: clean up permission history testing data
jacob-a-brown Nov 25, 2025
3041102
fix: use origin_type to retrieve well depth source
jacob-a-brown Nov 25, 2025
5e8fdad
refactor: use __tabename__ for polymorphic mixins
jacob-a-brown Nov 25, 2025
95ff8c0
fix: use __tablename__ for NotesMixin add_note
jacob-a-brown Nov 25, 2025
ca6d16d
Merge branch 'bdms-227-jab-updates-to-pass-tests' into bdms-227-trans…
jacob-a-brown Nov 25, 2025
b14e6d0
refactor(transfer): update log levels and error tracking in stratigra…
ksmuczynski Nov 25, 2025
48830b9
refactor(schemas): use enums in `geologic_formation` create schema.
ksmuczynski Nov 25, 2025
dc9b1ef
refactor(transfer): update log levels and error tracking in aquifer_s…
ksmuczynski Nov 25, 2025
ee787d1
feat(transfer): Add aquifer system and geologic formation association…
ksmuczynski Nov 25, 2025
81fed8a
refactor(transfer): Update logic related to creating an aquifer_system
ksmuczynski Nov 26, 2025
cd1d170
refactor(transfer): Update logic related to creating formation associ…
ksmuczynski Nov 26, 2025
e7f0aaa
refactor(transfer): add note to verify compound aquifer type codes wi…
ksmuczynski Nov 26, 2025
bdff12e
refactor: use sets in feature tests for comparison
jacob-a-brown Nov 26, 2025
8d055bd
Merge branch 'bdms-227-jab-updates-to-pass-tests' into bdms-227-trans…
jacob-a-brown Nov 26, 2025
19d736a
refactor: return permission history records for a well
jacob-a-brown Nov 26, 2025
ddef82d
Merge branch 'bdms-227-jab-updates-to-pass-tests' into bdms-227-trans…
jacob-a-brown Nov 26, 2025
eac24a2
Merge pull request #256 from DataIntegrationGroup/bdms-227-jab-update…
jacob-a-brown Nov 26, 2025
3ada894
Merge branch 'bdms-227' into bdms-227-transfer-updates
jacob-a-brown Nov 26, 2025
d81de21
Merge pull request #257 from DataIntegrationGroup/bdms-227-transfer-u…
jacob-a-brown Nov 26, 2025
595d1d9
refactor(transfer): removed unnecessary if statement re: creating aqu…
ksmuczynski Nov 26, 2025
091a77e
Merge branch 'bdms-227' into kas-227-231-additional-well-info-transfe…
ksmuczynski Nov 26, 2025
656e159
fix: use regex to check for pump type in well construction notes
jacob-a-brown Nov 26, 2025
183d275
fix: ensure permissions are not null during transfer
jacob-a-brown Nov 26, 2025
2d0f2b6
refactor: check for existence of permissions before converting
jacob-a-brown Nov 26, 2025
1147e77
Merge branch 'staging' into bdms-227
jacob-a-brown Dec 1, 2025
7ba6155
Merge branch 'bdms-227' into bdms-227-transfer-updates
jacob-a-brown Dec 1, 2025
9a2cdbc
refactor(transfer): remove section that creates Formation Association…
ksmuczynski Dec 1, 2025
f6dec15
refactor(transfer): add validation when assigning `formation_completi…
ksmuczynski Dec 1, 2025
64ecb18
refactor(schema): add `formation_completion_code` field to CreateWell…
ksmuczynski Dec 1, 2025
9a87a69
refactor(schema): use `FormationCode` enum for `formation_completion_…
ksmuczynski Dec 1, 2025
9588627
refactor(transfer): add missing transfers to `transfer.py`
ksmuczynski Dec 1, 2025
8a78f67
feat(schemas): add enum, depth and geometry validations and associati…
ksmuczynski Dec 1, 2025
12ebaac
Merge pull request #258 from DataIntegrationGroup/kas-227-231-additio…
jacob-a-brown Dec 1, 2025
20278af
Merge branch 'bdms-227' of https://github.com/DataIntegrationGroup/NM…
jacob-a-brown Dec 1, 2025
1e9b199
Merge branch 'bdms-227' into bdms-227-transfer-updates
jacob-a-brown Dec 1, 2025
64eb2b7
refactor: use formation_completion_code in well schema and tests
jacob-a-brown Dec 1, 2025
23c6ecc
fix: remove outdated geologic_formations field from ThingResponse
jacob-a-brown Dec 1, 2025
265a72a
fix: use formation_completion_code in well additional information
jacob-a-brown Dec 1, 2025
8064d74
Merge branch 'bdms-227' into bdms-227-transfer-updates
jacob-a-brown Dec 1, 2025
144b1b4
refactor: enumerate all errors as e.errors()
jacob-a-brown Dec 1, 2025
7345122
fix: use enums to restrict aquifer system fields
jacob-a-brown Dec 1, 2025
345e34f
feat: add Unknown to aquifer_type lexicon
jacob-a-brown Dec 1, 2025
1ded083
feat: make geographic_scale nullable
jacob-a-brown Dec 1, 2025
77e1923
fix: update aquifer types to correspond with NM_Aquifer
jacob-a-brown Dec 1, 2025
85f5211
fix: use edge case column names for LU_Lithology
jacob-a-brown Dec 1, 2025
93dc1e4
refactor: log errors as critical
jacob-a-brown Dec 2, 2025
893a527
fix: log well errors correctly
jacob-a-brown Dec 2, 2025
35b5f60
Merge branch 'bdms-227' into bdms-227-transfer-updates
jacob-a-brown Dec 2, 2025
d37e16d
Merge pull request #263 from DataIntegrationGroup/bdms-227-transfer-u…
jacob-a-brown Dec 2, 2025
a8c4afc
refactor(schemas): centralize validations and enforce strict enums
ksmuczynski Dec 2, 2025
33ea2e2
refactor(schemas): remove None check on validators.DepthIntervalMixin
ksmuczynski Dec 2, 2025
02107c3
refactor(schemas): enforce non-negative depths in DepthIntervalMixin
ksmuczynski Dec 2, 2025
3ed93b8
Merge remote-tracking branch 'origin/bdms-227' into kas-227-254-add-s…
ksmuczynski Dec 2, 2025
09b4b73
Merge pull request #266 from DataIntegrationGroup/kas-227-254-add-sch…
ksmuczynski Dec 2, 2025
6d3df5a
fix: make well_completion_date a date object, not str
jacob-a-brown Dec 2, 2025
f3be76a
Merge branch 'bdms-227' of https://github.com/DataIntegrationGroup/NM…
jacob-a-brown Dec 2, 2025
db3db66
fix: remove import of ValueError
jacob-a-brown Dec 2, 2025
cdd00f9
fix: only inherit from mixins for aquifer/geology
jacob-a-brown Dec 2, 2025
0605519
feat: skip test that needs to be updated
jacob-a-brown Dec 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion core/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -68,8 +70,14 @@
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")
PermissionType: type[Enum] = build_enum_from_lexicon_category("permission_type")
GroupType: type[Enum] = build_enum_from_lexicon_category("group_type")
MonitoringFrequency: type[Enum] = build_enum_from_lexicon_category(
"monitoring_frequency"
)
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")
FormationCode: type[Enum] = build_enum_from_lexicon_category("formation_code")
# ============= EOF =============================================
Empty file added core/formations.json
Empty file.
424 changes: 412 additions & 12 deletions core/lexicon.json

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion db/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from db.notes import *
from db.observation import *
from db.parameter import *
from db.permission import *
from db.permission_history import *
from db.publication import *
from db.regulatory_limit import *
from db.sample import *
Expand All @@ -43,6 +43,11 @@
from db.transducer import *
from db.measuring_point_history import *
from db.data_provenance import *
from db.aquifer_system import *
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,
Expand Down
84 changes: 84 additions & 0 deletions db/aquifer_system.py
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=True,
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"
)
# 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"),)
58 changes: 58 additions & 0 deletions db/aquifer_type.py
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"
)
20 changes: 0 additions & 20 deletions db/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
declared_attr,
Mapped,
mapped_column,
relationship,
)
from sqlalchemy_continuum import make_versioned
from sqlalchemy_searchable import make_searchable
Expand Down Expand Up @@ -179,25 +178,6 @@ def properties(self):
# ============= Polymorphic Helper Mixins =============================================


class PermissionMixin:
"""
Mixin for models that can have permissions (e.g., Thing, Location).
It automatically creates a polymorphic One-to-Many relationship to the
Permission table.
"""

@declared_attr
def permissions(self):
# One-to-Many polymorphic relationship
return relationship(
"Permission",
primaryjoin=f"and_({self.__name__}.id==foreign(Permission.permissible_id), "
f"Permission.permissible_type=='{self.__name__}')",
lazy="selectin",
viewonly=True,
)


class User(Base):
"""Represents a user in the system."""

Expand Down
8 changes: 5 additions & 3 deletions db/contact.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from db.field import FieldEventParticipant, FieldEvent
from db.thing import Thing
from db.publication import Author, AuthorContactAssociation
from db.permission import Permission
from db.permission_history import PermissionHistory


class ThingContactAssociation(Base, AutoBaseMixin):
Expand Down Expand Up @@ -74,8 +74,10 @@ class Contact(Base, AutoBaseMixin, ReleaseMixin):
)

# One-To-Many: A Contact can grant many Permissions.
permissions: Mapped[List["Permission"]] = relationship(
"Permission", back_populates="contact", cascade="all, delete, delete-orphan"
permissions: Mapped[List["PermissionHistory"]] = relationship(
"PermissionHistory",
back_populates="contact",
cascade="all, delete, delete-orphan",
)
# One-To-Many: A Contact can be associated with many Authors (in Publications).
author_associations: Mapped[List["AuthorContactAssociation"]] = relationship(
Expand Down
12 changes: 8 additions & 4 deletions db/data_provenance.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from sqlalchemy import Integer, Index, and_
from sqlalchemy.orm import relationship, Mapped, mapped_column, declared_attr, foreign

from db.base import Base, AutoBaseMixin, ReleaseMixin, pascal_to_snake
from db.base import Base, AutoBaseMixin, ReleaseMixin

from db import lexicon_term

Expand Down Expand Up @@ -53,9 +53,13 @@ class DataProvenance(AutoBaseMixin, ReleaseMixin, Base):
)
# Values from the following NMAquifer tables are included as `origin_source` terms in the lexicon:
# 'LU_DataSource', 'LU_Depth_CompletionSource'.
origin_source: Mapped[str] = lexicon_term(
origin_type: Mapped[str] = lexicon_term(
nullable=True,
comment="Indicates the origin source of the data (e.g'Driller's Log', 'Well Report'.",
comment="Indicates the type of origin the data (e.g'Driller's Log', 'Well Report'.",
)
origin_source: Mapped[str] = mapped_column(
nullable=True,
comment="The specific source of the data (e.g., 'J. Brown Thesis, \"I like APIs\", Pomona College, 1994').",
)
# Values from the following NMAquifer tables are included as `collection_method` terms in the lexicon:
# 'LU_AltitudeMethod','LU_CoordinateMethod'.
Expand Down Expand Up @@ -116,7 +120,7 @@ def data_provenance(cls):
"DataProvenance",
primaryjoin=and_(
cls.id == foreign(DataProvenance.target_id),
DataProvenance.target_table == pascal_to_snake(cls.__name__),
DataProvenance.target_table == cls.__tablename__,
),
lazy="selectin",
viewonly=True,
Expand Down
82 changes: 82 additions & 0 deletions db/geologic_formation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
"""
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.
well_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_formation_code", "formation_code"),)
4 changes: 2 additions & 2 deletions db/notes.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def notes(cls):
"Notes",
primaryjoin=and_(
cls.id == foreign(Notes.target_id),
Notes.target_table == cls.__name__,
Notes.target_table == cls.__tablename__,
),
cascade="all, delete-orphan",
lazy="selectin",
Expand All @@ -120,7 +120,7 @@ def add_note(
content=content,
note_type=note_type,
target_id=self.id,
target_table=self.__class__.__name__,
target_table=self.__class__.__tablename__,
release_status=release_status,
)

Expand Down
Loading
Loading