Skip to content

Commit 3bb4097

Browse files
committed
Merge branch 'well-inventory-csv' into water-level-csv
2 parents 4c660b4 + 7e197c4 commit 3bb4097

File tree

4 files changed

+98
-164
lines changed

4 files changed

+98
-164
lines changed

tests/features/steps/nma-legacy-relationships.py

Lines changed: 28 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
- All models use `id` (Integer, autoincrement) as PK
2323
- Legacy UUID columns renamed with `nma_` prefix (e.g., `nma_global_id`)
2424
- Legacy string columns renamed with `nma_` prefix (e.g., `nma_point_id`)
25-
- Chemistry samples FK to Location (not Thing)
25+
- Chemistry samples FK to Thing
2626
- Other NMA models (hydraulics, stratigraphy, etc.) FK to Thing
2727
- Chemistry children use `chemistry_sample_info_id` (Integer FK)
2828
"""
@@ -34,7 +34,7 @@
3434
from behave.runner import Context
3535
from sqlalchemy.exc import IntegrityError, StatementError
3636

37-
from db import Location, Thing
37+
from db import Thing
3838
from db.engine import session_ctx
3939
from db.nma_legacy import (
4040
NMA_Chemistry_SampleInfo,
@@ -130,7 +130,7 @@ def step_then_find_by_locationid(context: Context):
130130

131131
@when("I try to save chemistry sample information")
132132
def step_when_save_chemistry(context: Context):
133-
"""Attempt to save chemistry sample info without a location."""
133+
"""Attempt to save chemistry sample info without a well."""
134134
context.orphan_error = None
135135
context.record_saved = False
136136

@@ -139,7 +139,7 @@ def step_when_save_chemistry(context: Context):
139139
chemistry = NMA_Chemistry_SampleInfo(
140140
nma_sample_pt_id=uuid.uuid4(),
141141
nma_sample_point_id="TEST001",
142-
location_id=None, # No parent location
142+
thing_id=None, # No parent well
143143
collection_date=datetime.now(),
144144
)
145145
session.add(chemistry)
@@ -159,11 +159,11 @@ def step_then_well_required(context: Context):
159159

160160
@then("orphaned chemistry records are not allowed")
161161
def step_then_no_orphan_chemistry(context: Context):
162-
"""Verify no orphan chemistry records exist (FK to Location)."""
162+
"""Verify no orphan chemistry records exist (FK to Thing)."""
163163
with session_ctx() as session:
164164
orphan_count = (
165165
session.query(NMA_Chemistry_SampleInfo)
166-
.filter(NMA_Chemistry_SampleInfo.location_id.is_(None))
166+
.filter(NMA_Chemistry_SampleInfo.thing_id.is_(None))
167167
.count()
168168
)
169169
assert orphan_count == 0, f"Found {orphan_count} orphan chemistry records"
@@ -256,38 +256,16 @@ def step_then_no_orphan_lithology(context: Context):
256256

257257
@when("I try to save radionuclide results")
258258
def step_when_save_radionuclides(context: Context):
259-
"""Attempt to save radionuclide results without a well."""
259+
"""Attempt to save radionuclide results without chemistry sample info."""
260260
context.orphan_error = None
261261
context.record_saved = False
262262

263263
try:
264264
with session_ctx() as session:
265-
# First create a Location for the chemistry sample (chemistry FKs to Location)
266-
location = Location(
267-
point="POINT(-107.949533 33.809665)",
268-
elevation=2464.9,
269-
release_status="draft",
270-
)
271-
session.add(location)
272-
session.commit()
273-
session.refresh(location)
274-
275-
# Create chemistry sample info for the radionuclide
276-
chemistry_sample = NMA_Chemistry_SampleInfo(
277-
nma_sample_pt_id=uuid.uuid4(),
278-
nma_sample_point_id="TEST001",
279-
location_id=location.id,
280-
collection_date=datetime.now(),
281-
)
282-
session.add(chemistry_sample)
283-
session.commit()
284-
session.refresh(chemistry_sample)
285-
286265
radionuclide = NMA_Radionuclides(
287266
nma_global_id=uuid.uuid4(),
288-
thing_id=None, # No parent well - this should fail
289-
chemistry_sample_info_id=chemistry_sample.id,
290-
nma_sample_pt_id=chemistry_sample.nma_sample_pt_id,
267+
chemistry_sample_info_id=None, # No parent sample info - should fail
268+
nma_sample_pt_id=uuid.uuid4(),
291269
analyte="U-238",
292270
)
293271
session.add(radionuclide)
@@ -304,7 +282,7 @@ def step_then_no_orphan_radionuclides(context: Context):
304282
with session_ctx() as session:
305283
orphan_count = (
306284
session.query(NMA_Radionuclides)
307-
.filter(NMA_Radionuclides.thing_id.is_(None))
285+
.filter(NMA_Radionuclides.chemistry_sample_info_id.is_(None))
308286
.count()
309287
)
310288
assert orphan_count == 0, f"Found {orphan_count} orphan radionuclide records"
@@ -397,26 +375,21 @@ def step_then_no_orphan_soil_rock(context: Context):
397375
def step_when_access_relationships(context: Context):
398376
"""Access the well's relationships.
399377
400-
Note: Chemistry samples now FK to Location, not Thing.
401-
Chemistry samples are accessed via Location.chemistry_sample_infos.
378+
Note: Chemistry samples FK to Thing.
379+
Chemistry samples are accessed via Thing.chemistry_sample_infos.
402380
"""
403381
with session_ctx() as session:
404382
well = session.query(Thing).filter(Thing.id == context.test_well_id).first()
405-
# Chemistry samples are now on Location, not Thing
406-
# Access via the test location created in step_given_well_has_chemistry
407-
location = None
408-
if hasattr(context, "test_location_id"):
409-
location = (
410-
session.query(Location)
411-
.filter(Location.id == context.test_location_id)
412-
.first()
413-
)
383+
chemistry_samples = well.chemistry_sample_infos if well else []
384+
radionuclides = [
385+
radio for sample in chemistry_samples for radio in sample.radionuclides
386+
]
414387

415388
context.well_relationships = {
416-
"chemistry_samples": location.chemistry_sample_infos if location else [],
389+
"chemistry_samples": chemistry_samples,
417390
"hydraulics_data": well.hydraulics_data,
418391
"lithology_logs": well.stratigraphy_logs,
419-
"radionuclides": well.radionuclides,
392+
"radionuclides": radionuclides,
420393
"associated_data": well.associated_data,
421394
"soil_rock_results": well.soil_rock_results,
422395
}
@@ -451,36 +424,21 @@ def step_then_relationships_correct(context: Context):
451424

452425
@given("a well has chemistry sample records")
453426
def step_given_well_has_chemistry(context: Context):
454-
"""Create chemistry samples for a location associated with a well.
455-
456-
Note: Chemistry samples now FK to Location (not Thing).
457-
This step creates a Location and associates chemistry samples with it.
458-
"""
427+
"""Create chemistry samples for a well."""
459428
if not hasattr(context, "test_well"):
460429
step_given_well_exists(context)
461430

462431
with session_ctx() as session:
463-
# Create a Location for chemistry samples
464-
location = Location(
465-
point="POINT(-107.949533 33.809665)",
466-
elevation=2464.9,
467-
release_status="draft",
468-
)
469-
session.add(location)
470-
session.commit()
471-
session.refresh(location)
472-
context.test_location_id = location.id
473-
474432
chemistry1 = NMA_Chemistry_SampleInfo(
475433
nma_sample_pt_id=uuid.uuid4(),
476434
nma_sample_point_id="TEST001",
477-
location_id=context.test_location_id,
435+
thing_id=context.test_well_id,
478436
collection_date=datetime.now(),
479437
)
480438
chemistry2 = NMA_Chemistry_SampleInfo(
481439
nma_sample_pt_id=uuid.uuid4(),
482440
nma_sample_point_id="TEST002",
483-
location_id=context.test_location_id,
441+
thing_id=context.test_well_id,
484442
collection_date=datetime.now(),
485443
)
486444
session.add_all([chemistry1, chemistry2])
@@ -537,26 +495,16 @@ def step_given_well_has_lithology(context: Context):
537495
def step_given_well_has_radionuclides(context: Context):
538496
"""Create radionuclide results for a well.
539497
540-
Note: Chemistry samples FK to Location, Radionuclides FK to both Thing and ChemistrySampleInfo.
498+
Note: Chemistry samples FK to Thing, Radionuclides FK to ChemistrySampleInfo.
541499
"""
542500
if not hasattr(context, "test_well"):
543501
step_given_well_exists(context)
544502

545503
with session_ctx() as session:
546-
# Create a Location for the chemistry sample (chemistry FKs to Location)
547-
location = Location(
548-
point="POINT(-107.949533 33.809665)",
549-
elevation=2464.9,
550-
release_status="draft",
551-
)
552-
session.add(location)
553-
session.commit()
554-
session.refresh(location)
555-
556504
chemistry_sample = NMA_Chemistry_SampleInfo(
557505
nma_sample_pt_id=uuid.uuid4(),
558506
nma_sample_point_id="TEST001",
559-
location_id=location.id,
507+
thing_id=context.test_well_id,
560508
collection_date=datetime.now(),
561509
)
562510
session.add(chemistry_sample)
@@ -565,14 +513,14 @@ def step_given_well_has_radionuclides(context: Context):
565513

566514
radionuclide = NMA_Radionuclides(
567515
nma_global_id=uuid.uuid4(),
568-
thing_id=context.test_well_id,
569516
chemistry_sample_info_id=chemistry_sample.id,
570517
nma_sample_pt_id=chemistry_sample.nma_sample_pt_id,
571518
analyte="U-238",
572519
)
573520
session.add(radionuclide)
574521
session.commit()
575522
context.radionuclide_results = radionuclide
523+
context.radionuclide_results_id = radionuclide.id
576524

577525

578526
@given("a well has associated data")
@@ -624,17 +572,11 @@ def step_when_well_deleted(context: Context):
624572

625573
@then("its chemistry samples are also deleted")
626574
def step_then_chemistry_deleted(context: Context):
627-
"""Verify chemistry samples are cascade deleted when Location is deleted.
628-
629-
Note: Chemistry samples now FK to Location (not Thing), so this step
630-
verifies no chemistry samples exist for the test location.
631-
"""
575+
"""Verify chemistry samples are cascade deleted when Thing is deleted."""
632576
with session_ctx() as session:
633-
# Chemistry samples FK to Location, not Thing
634-
# When a Location is deleted, its chemistry samples cascade delete
635577
remaining = (
636578
session.query(NMA_Chemistry_SampleInfo)
637-
.filter(NMA_Chemistry_SampleInfo.location_id == context.test_location_id)
579+
.filter(NMA_Chemistry_SampleInfo.thing_id == context.test_well_id)
638580
.count()
639581
)
640582
assert remaining == 0, f"Expected 0 chemistry samples, found {remaining}"
@@ -668,12 +610,8 @@ def step_then_lithology_deleted(context: Context):
668610
def step_then_radionuclides_deleted(context: Context):
669611
"""Verify radionuclide results are cascade deleted."""
670612
with session_ctx() as session:
671-
remaining = (
672-
session.query(NMA_Radionuclides)
673-
.filter(NMA_Radionuclides.thing_id == context.test_well_id)
674-
.count()
675-
)
676-
assert remaining == 0, f"Expected 0 radionuclide records, found {remaining}"
613+
orphan = session.get(NMA_Radionuclides, context.radionuclide_results_id)
614+
assert orphan is None, "Radionuclide record should be deleted with well"
677615

678616

679617
@then("its associated data is also deleted")

tests/integration/test_nma_legacy_relationships.py

Lines changed: 62 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -251,20 +251,6 @@ def test_stratigraphy_requires_well(self):
251251
session.add(record)
252252
session.flush()
253253

254-
def test_radionuclides_requires_well(self):
255-
"""
256-
@radionuclides
257-
Scenario: Radionuclide results require a well
258-
"""
259-
with session_ctx() as session:
260-
with pytest.raises(ValueError, match="requires a parent Thing"):
261-
record = NMA_Radionuclides(
262-
nma_sample_pt_id=uuid.uuid4(),
263-
thing_id=None, # This should raise ValueError
264-
)
265-
session.add(record)
266-
session.flush()
267-
268254
def test_associated_data_requires_well(self):
269255
"""
270256
@associated-data
@@ -375,36 +361,6 @@ def test_well_navigates_to_stratigraphy_logs(self, well_for_relationships):
375361
assert len(well.stratigraphy_logs) >= 1
376362
assert any(s.nma_point_id == "NAVSTRAT1" for s in well.stratigraphy_logs)
377363

378-
def test_well_navigates_to_radionuclides(self, well_for_relationships):
379-
"""Well can navigate to its radionuclide results."""
380-
with session_ctx() as session:
381-
well = session.merge(well_for_relationships)
382-
383-
# Create a chemistry sample for the thing (chemistry FKs to Thing)
384-
chem_sample = NMA_Chemistry_SampleInfo(
385-
nma_sample_pt_id=uuid.uuid4(),
386-
nma_sample_point_id="NAVRAD01", # Required, max 10 chars
387-
thing_id=well.id,
388-
)
389-
session.add(chem_sample)
390-
session.commit()
391-
session.refresh(chem_sample)
392-
393-
# Create radionuclide record for this well using the chemistry_sample_info_id
394-
radio = NMA_Radionuclides(
395-
nma_global_id=uuid.uuid4(),
396-
chemistry_sample_info_id=chem_sample.id,
397-
nma_sample_pt_id=chem_sample.nma_sample_pt_id,
398-
thing_id=well.id,
399-
)
400-
session.add(radio)
401-
session.commit()
402-
session.refresh(well)
403-
404-
# Navigate through relationship
405-
assert hasattr(well, "radionuclides")
406-
assert len(well.radionuclides) >= 1
407-
408364
def test_well_navigates_to_associated_data(self, well_for_relationships):
409365
"""Well can navigate to its associated data."""
410366
with session_ctx() as session:
@@ -445,6 +401,42 @@ def test_well_navigates_to_soil_rock_results(self, well_for_relationships):
445401
assert any(s.nma_point_id == "NAV-SOIL-01" for s in well.soil_rock_results)
446402

447403

404+
class TestChemistrySampleInfoNavigation:
405+
"""
406+
@relationships
407+
Scenario: Chemistry sample info can access its related records
408+
"""
409+
410+
def test_sample_info_navigates_to_radionuclides(self, well_for_relationships):
411+
"""Chemistry sample info can navigate to its radionuclide results."""
412+
with session_ctx() as session:
413+
well = session.merge(well_for_relationships)
414+
415+
# Create a chemistry sample for the thing (chemistry FKs to Thing)
416+
chem_sample = NMA_Chemistry_SampleInfo(
417+
nma_sample_pt_id=uuid.uuid4(),
418+
nma_sample_point_id="NAVRAD01", # Required, max 10 chars
419+
thing_id=well.id,
420+
)
421+
session.add(chem_sample)
422+
session.commit()
423+
session.refresh(chem_sample)
424+
425+
# Create radionuclide record using the chemistry_sample_info_id
426+
radio = NMA_Radionuclides(
427+
nma_global_id=uuid.uuid4(),
428+
chemistry_sample_info_id=chem_sample.id,
429+
nma_sample_pt_id=chem_sample.nma_sample_pt_id,
430+
)
431+
session.add(radio)
432+
session.commit()
433+
session.refresh(chem_sample)
434+
435+
# Navigate through relationship
436+
assert hasattr(chem_sample, "radionuclides")
437+
assert len(chem_sample.radionuclides) >= 1
438+
439+
448440
# =============================================================================
449441
# Deleting a Well Removes Related Records (Cascade Delete)
450442
# =============================================================================
@@ -597,7 +589,6 @@ def test_deleting_well_cascades_to_radionuclides(self):
597589
nma_global_id=uuid.uuid4(),
598590
chemistry_sample_info_id=chem_sample.id,
599591
nma_sample_pt_id=chem_sample.nma_sample_pt_id,
600-
thing_id=well.id,
601592
)
602593
session.add(radio)
603594
session.commit()
@@ -682,3 +673,29 @@ def test_deleting_well_cascades_to_soil_rock_results(self):
682673
# Verify soil/rock results were also deleted
683674
orphan = session.get(NMA_Soil_Rock_Results, soil_id)
684675
assert orphan is None, "Soil/rock results should be deleted with well"
676+
677+
678+
# =============================================================================
679+
# Chemistry Children Require Sample Info
680+
# =============================================================================
681+
682+
683+
class TestChemistryChildrenRequireSampleInfo:
684+
"""
685+
@radionuclides
686+
Scenario: Chemistry children require a parent sample info
687+
"""
688+
689+
def test_radionuclides_requires_sample_info(self):
690+
"""
691+
@radionuclides
692+
Scenario: Radionuclide results require chemistry sample info
693+
"""
694+
with session_ctx() as session:
695+
with pytest.raises(ValueError, match="requires a chemistry_sample_info_id"):
696+
record = NMA_Radionuclides(
697+
nma_sample_pt_id=uuid.uuid4(),
698+
chemistry_sample_info_id=None, # This should raise ValueError
699+
)
700+
session.add(record)
701+
session.flush()

0 commit comments

Comments
 (0)