Skip to content

Commit ed33aa2

Browse files
committed
feat: add thing_id association to AssociatedData and SoilRockResults models
1 parent 32d6aba commit ed33aa2

7 files changed

Lines changed: 48 additions & 18 deletions

alembic/versions/c2f4a9d0b1e2_create_nma_associated_data.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ def upgrade() -> None:
3737
sa.Column("Notes", sa.String(length=255), nullable=True),
3838
sa.Column("Formation", sa.String(length=15), nullable=True),
3939
sa.Column("OBJECTID", sa.Integer(), nullable=True, unique=True),
40+
sa.Column(
41+
"thing_id",
42+
sa.Integer(),
43+
sa.ForeignKey("thing.id", ondelete="CASCADE"),
44+
nullable=True,
45+
),
4046
sa.UniqueConstraint("LocationId", name="AssociatedData$LocationId"),
4147
)
4248
op.create_index("AssociatedData$PointID", "NMA_AssociatedData", ["PointID"])

alembic/versions/f5a6b7c8d9e0_create_nma_soil_rock_results.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ def upgrade() -> None:
3232
sa.Column("d13C", sa.Float(), nullable=True),
3333
sa.Column("d18O", sa.Float(), nullable=True),
3434
sa.Column("Sampled by", sa.String(length=255), nullable=True),
35+
sa.Column(
36+
"thing_id",
37+
sa.Integer(),
38+
sa.ForeignKey("thing.id", ondelete="CASCADE"),
39+
nullable=True,
40+
),
3541
)
3642
op.create_index(
3743
"Soil_Rock_Results$Point_ID", "NMA_Soil_Rock_Results", ["Point_ID"]

db/nma_legacy.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -296,22 +296,11 @@ class AssociatedData(Base):
296296
notes: Mapped[Optional[str]] = mapped_column("Notes", String(255))
297297
formation: Mapped[Optional[str]] = mapped_column("Formation", String(15))
298298
object_id: Mapped[Optional[int]] = mapped_column("OBJECTID", Integer, unique=True)
299-
300-
major_chemistries: Mapped[List["NMAMajorChemistry"]] = relationship(
301-
"NMAMajorChemistry",
302-
back_populates="chemistry_sample_info",
303-
cascade="all, delete-orphan",
304-
passive_deletes=True,
299+
thing_id: Mapped[Optional[int]] = mapped_column(
300+
Integer, ForeignKey("thing.id", ondelete="CASCADE")
305301
)
306302

307-
@validates("thing_id")
308-
def validate_thing_id(self, key, value):
309-
"""Prevent orphan ChemistrySampleInfo - must have a parent Thing."""
310-
if value is None:
311-
raise ValueError(
312-
"ChemistrySampleInfo requires a parent Thing (thing_id cannot be None)"
313-
)
314-
return value
303+
thing: Mapped["Thing"] = relationship("Thing")
315304

316305

317306
class SurfaceWaterData(Base):
@@ -414,6 +403,11 @@ class SoilRockResults(Base):
414403
d13c: Mapped[Optional[float]] = mapped_column("d13C", Float)
415404
d18o: Mapped[Optional[float]] = mapped_column("d18O", Float)
416405
sampled_by: Mapped[Optional[str]] = mapped_column("Sampled by", String(255))
406+
thing_id: Mapped[Optional[int]] = mapped_column(
407+
Integer, ForeignKey("thing.id", ondelete="CASCADE")
408+
)
409+
410+
thing: Mapped["Thing"] = relationship("Thing")
417411

418412

419413
class NMAMinorTraceChemistry(Base):

tests/test_associated_data_legacy.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
from db.nma_legacy import AssociatedData
3333

3434

35-
def test_create_associated_data_all_fields():
35+
def test_create_associated_data_all_fields(water_well_thing):
3636
"""Test creating an associated data record with all fields."""
3737
with session_ctx() as session:
3838
record = AssociatedData(
@@ -42,6 +42,7 @@ def test_create_associated_data_all_fields():
4242
notes="Legacy notes",
4343
formation="TEST",
4444
object_id=42,
45+
thing_id=water_well_thing.id,
4546
)
4647
session.add(record)
4748
session.commit()
@@ -53,6 +54,7 @@ def test_create_associated_data_all_fields():
5354
assert record.notes == "Legacy notes"
5455
assert record.formation == "TEST"
5556
assert record.object_id == 42
57+
assert record.thing_id == water_well_thing.id
5658

5759
session.delete(record)
5860
session.commit()

tests/test_soil_rock_results_legacy.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from db.nma_legacy import SoilRockResults
3232

3333

34-
def test_create_soil_rock_results_all_fields():
34+
def test_create_soil_rock_results_all_fields(water_well_thing):
3535
"""Test creating a soil/rock results record with all fields."""
3636
with session_ctx() as session:
3737
record = SoilRockResults(
@@ -41,6 +41,7 @@ def test_create_soil_rock_results_all_fields():
4141
d13c=-5.5,
4242
d18o=12.3,
4343
sampled_by="Tester",
44+
thing_id=water_well_thing.id,
4445
)
4546
session.add(record)
4647
session.commit()
@@ -53,6 +54,7 @@ def test_create_soil_rock_results_all_fields():
5354
assert record.d13c == -5.5
5455
assert record.d18o == 12.3
5556
assert record.sampled_by == "Tester"
57+
assert record.thing_id == water_well_thing.id
5658
session.delete(record)
5759
session.commit()
5860

transfers/associated_data.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
from sqlalchemy.dialects.postgresql import insert
2424
from sqlalchemy.orm import Session
2525

26-
from db import AssociatedData
26+
from db import AssociatedData, Thing
27+
from db.engine import session_ctx
2728
from transfers.logger import logger
2829
from transfers.transferer import Transferer
2930
from transfers.util import replace_nans
@@ -37,6 +38,14 @@ class AssociatedDataTransferer(Transferer):
3738
def __init__(self, *args, batch_size: int = 1000, **kwargs):
3839
super().__init__(*args, **kwargs)
3940
self.batch_size = batch_size
41+
self._thing_id_cache: dict[str, int] = {}
42+
self._build_thing_id_cache()
43+
44+
def _build_thing_id_cache(self) -> None:
45+
with session_ctx() as session:
46+
things = session.query(Thing.name, Thing.id).all()
47+
self._thing_id_cache = {name: thing_id for name, thing_id in things}
48+
logger.info(f"Built Thing ID cache with {len(self._thing_id_cache)} entries")
4049

4150
def _get_dfs(self) -> tuple[pd.DataFrame, pd.DataFrame]:
4251
df = self._read_csv(self.source_table)
@@ -83,6 +92,7 @@ def _row_dict(self, row: dict[str, Any]) -> dict[str, Any]:
8392
"Notes": row.get("Notes"),
8493
"Formation": row.get("Formation"),
8594
"OBJECTID": row.get("OBJECTID"),
95+
"thing_id": self._thing_id_cache.get(row.get("PointID")),
8696
}
8797

8898
def _dedupe_rows(

transfers/soil_rock_results.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
import pandas as pd
2222
from sqlalchemy.orm import Session
2323

24-
from db import SoilRockResults
24+
from db import SoilRockResults, Thing
25+
from db.engine import session_ctx
2526
from transfers.logger import logger
2627
from transfers.transferer import Transferer
2728
from transfers.util import replace_nans
@@ -35,6 +36,14 @@ class SoilRockResultsTransferer(Transferer):
3536
def __init__(self, *args, batch_size: int = 1000, **kwargs):
3637
super().__init__(*args, **kwargs)
3738
self.batch_size = batch_size
39+
self._thing_id_cache: dict[str, int] = {}
40+
self._build_thing_id_cache()
41+
42+
def _build_thing_id_cache(self) -> None:
43+
with session_ctx() as session:
44+
things = session.query(Thing.name, Thing.id).all()
45+
self._thing_id_cache = {name: thing_id for name, thing_id in things}
46+
logger.info(f"Built Thing ID cache with {len(self._thing_id_cache)} entries")
3847

3948
def _get_dfs(self) -> tuple[pd.DataFrame, pd.DataFrame]:
4049
df = self._read_csv(self.source_table)
@@ -67,6 +76,7 @@ def _row_dict(self, row: dict[str, Any]) -> dict[str, Any]:
6776
"d13C": self._float_val(row.get("d13C")),
6877
"d18O": self._float_val(row.get("d18O")),
6978
"Sampled by": row.get("Sampled by"),
79+
"thing_id": self._thing_id_cache.get(row.get("Point_ID")),
7080
}
7181

7282
def _float_val(self, value: Any) -> Optional[float]:

0 commit comments

Comments
 (0)