Skip to content

Commit 6fe2bc1

Browse files
authored
Merge pull request #610 from DataIntegrationGroup/jab-bdms-626-mp-height
BDMS 626: mp height
2 parents 6fb61cf + 6df12f6 commit 6fe2bc1

2 files changed

Lines changed: 180 additions & 5 deletions

File tree

services/well_inventory_csv.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,22 @@ def _add_csv_row(session: Session, group: Group, model: WellInventoryRow, user)
681681
}
682682
)
683683

684+
if (
685+
model.mp_height is not None
686+
and model.measuring_point_height_ft is not None
687+
and model.mp_height != model.measuring_point_height_ft
688+
):
689+
raise ValueError(
690+
"Conflicting values for measuring point height: mp_height and measuring_point_height_ft"
691+
)
692+
693+
if model.measuring_point_height_ft is not None:
694+
universal_mp_height = model.measuring_point_height_ft
695+
elif model.mp_height is not None:
696+
universal_mp_height = model.mp_height
697+
else:
698+
universal_mp_height = None
699+
684700
data = CreateWell(
685701
location_id=loc.id,
686702
group_id=group.id,
@@ -689,7 +705,7 @@ def _add_csv_row(session: Session, group: Group, model: WellInventoryRow, user)
689705
well_depth=model.total_well_depth_ft,
690706
well_depth_source=model.depth_source,
691707
well_casing_diameter=model.casing_diameter_ft,
692-
measuring_point_height=model.measuring_point_height_ft,
708+
measuring_point_height=universal_mp_height,
693709
measuring_point_description=model.measuring_point_description,
694710
well_completion_date=model.date_drilled,
695711
well_completion_date_source=model.completion_source,
@@ -821,6 +837,11 @@ def _add_csv_row(session: Session, group: Group, model: WellInventoryRow, user)
821837
session.add(sample)
822838
session.flush()
823839

840+
if model.depth_to_water_ft is not None and universal_mp_height is None:
841+
raise ValueError(
842+
"measuring_point_height_ft or mp_height is required when depth_to_water_ft is provided for a non-null observation"
843+
)
844+
824845
# create Observation
825846
# TODO: groundwater_level_reason may be conditionally required for null depth_to_water_ft - handle accordingly
826847
observation = Observation(
@@ -829,7 +850,7 @@ def _add_csv_row(session: Session, group: Group, model: WellInventoryRow, user)
829850
value=model.depth_to_water_ft,
830851
unit="ft",
831852
observation_datetime=model.measurement_date_time,
832-
measuring_point_height=model.mp_height,
853+
measuring_point_height=universal_mp_height,
833854
groundwater_level_reason=(
834855
model.level_status.value
835856
if hasattr(model.level_status, "value")

tests/test_well_inventory.py

Lines changed: 157 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ def test_well_inventory_db_contents_with_waterlevels(tmp_path):
481481
"sample_method": "Steel-tape measurement",
482482
"data_quality": "Water level accurate to within two hundreths of a foot",
483483
"water_level_notes": "Attempted measurement",
484-
"mp_height_ft": 2.5,
484+
"mp_height_ft": 3.5,
485485
"level_status": "Water level not affected",
486486
}
487487
)
@@ -529,6 +529,160 @@ def test_well_inventory_db_contents_with_waterlevels(tmp_path):
529529
assert observation.sample == sample
530530

531531

532+
def test_measuring_point_height_ft_used_for_thing_and_observation(tmp_path):
533+
"""When measuring_point_height_ft is provided it is used for the thing's (MeasuringPointHistory) and observation's measuring_point_height values."""
534+
row = _minimal_valid_well_inventory_row()
535+
row.update(
536+
{
537+
"measuring_point_height_ft": 3.5,
538+
"water_level_date_time": "2025-02-15T10:30:00",
539+
"depth_to_water_ft": "8",
540+
"sample_method": "Steel-tape measurement",
541+
"data_quality": "Water level accurate to within two hundreths of a foot",
542+
"water_level_notes": "Attempted measurement",
543+
}
544+
)
545+
546+
file_path = tmp_path / "well-inventory-blank-depth.csv"
547+
with file_path.open("w", encoding="utf-8", newline="") as f:
548+
writer = csv.DictWriter(f, fieldnames=list(row.keys()))
549+
writer.writeheader()
550+
writer.writerow(row)
551+
552+
result = well_inventory_csv(file_path)
553+
assert result.exit_code == 0, result.stderr
554+
555+
with session_ctx() as session:
556+
things = session.query(Thing).all()
557+
observations = session.query(Observation).all()
558+
559+
assert len(things) == 1
560+
assert things[0].measuring_point_height == 3.5
561+
assert len(observations) == 1
562+
assert observations[0].measuring_point_height == 3.5
563+
564+
565+
def test_mp_height_used_for_thing_and_observation_when_measuring_point_height_ft_blank(
566+
tmp_path,
567+
):
568+
"""When depth to water is provided and measuring_point_height_ft is blank the mp_height value should be used for the thing's (MeasuringPointHistory) and observation's measuring_point_height."""
569+
row = _minimal_valid_well_inventory_row()
570+
row.update(
571+
{
572+
"measuring_point_height_ft": "",
573+
"water_level_date_time": "2025-02-15T10:30:00",
574+
"depth_to_water_ft": "8",
575+
"sample_method": "Steel-tape measurement",
576+
"data_quality": "Water level accurate to within two hundreths of a foot",
577+
"water_level_notes": "Attempted measurement",
578+
"mp_height": 4.0,
579+
}
580+
)
581+
582+
file_path = tmp_path / "well-inventory-blank-depth.csv"
583+
with file_path.open("w", encoding="utf-8", newline="") as f:
584+
writer = csv.DictWriter(f, fieldnames=list(row.keys()))
585+
writer.writeheader()
586+
writer.writerow(row)
587+
588+
result = well_inventory_csv(file_path)
589+
assert result.exit_code == 0, result.stderr
590+
591+
with session_ctx() as session:
592+
things = session.query(Thing).all()
593+
observations = session.query(Observation).all()
594+
595+
assert len(things) == 1
596+
assert things[0].measuring_point_height == 4.0
597+
assert len(observations) == 1
598+
assert observations[0].measuring_point_height == 4.0
599+
600+
601+
def test_null_observation_allows_blank_mp_height(tmp_path):
602+
"""When depth to water is not provided (ie null), blank measuring_point_height_ft and mp_height fields should be allowed and result in a null measuring_point_height for the observation and no associated measuring point height (MeasuringPointHistory) for the well."""
603+
row = _minimal_valid_well_inventory_row()
604+
row.update(
605+
{
606+
"measuring_point_height_ft": "",
607+
"water_level_date_time": "2025-02-15T10:30:00",
608+
"depth_to_water_ft": "",
609+
"sample_method": "Steel-tape measurement",
610+
"data_quality": "Water level accurate to within two hundreths of a foot",
611+
"water_level_notes": "Attempted measurement",
612+
}
613+
)
614+
615+
file_path = tmp_path / "well-inventory-blank-depth.csv"
616+
with file_path.open("w", encoding="utf-8", newline="") as f:
617+
writer = csv.DictWriter(f, fieldnames=list(row.keys()))
618+
writer.writeheader()
619+
writer.writerow(row)
620+
621+
result = well_inventory_csv(file_path)
622+
assert result.exit_code == 0, result.stderr
623+
624+
with session_ctx() as session:
625+
things = session.query(Thing).all()
626+
observations = session.query(Observation).all()
627+
628+
assert len(things) == 1
629+
assert things[0].measuring_point_height is None
630+
assert len(observations) == 1
631+
assert observations[0].measuring_point_height is None
632+
633+
634+
def test_conflicting_mp_heights_raises_error(tmp_path):
635+
"""
636+
When both measuring_point_height_ft and mp_height are provided, an inequality (conflict) should raise an error.
637+
"""
638+
row = _minimal_valid_well_inventory_row()
639+
640+
row.update(
641+
{
642+
"measuring_point_height_ft": 3.5,
643+
"mp_height": 4.0,
644+
}
645+
)
646+
647+
file_path = tmp_path / "well-inventory-blank-depth.csv"
648+
with file_path.open("w", encoding="utf-8", newline="") as f:
649+
writer = csv.DictWriter(f, fieldnames=list(row.keys()))
650+
writer.writeheader()
651+
writer.writerow(row)
652+
653+
result = well_inventory_csv(file_path)
654+
assert result.exit_code == 1, result.stderr
655+
assert (
656+
result.payload["validation_errors"][0]["error"]
657+
== "Conflicting values for measuring point height: mp_height and measuring_point_height_ft"
658+
)
659+
660+
661+
def test_no_mp_height_raises_error_when_depth_to_water_provided(tmp_path):
662+
row = _minimal_valid_well_inventory_row()
663+
row.update(
664+
{
665+
"water_level_date_time": "2025-02-15T10:30:00",
666+
"measuring_point_height_ft": "",
667+
"mp_height": "",
668+
"depth_to_water_ft": "8",
669+
}
670+
)
671+
672+
file_path = tmp_path / "well-inventory-no-mp-height.csv"
673+
with file_path.open("w", encoding="utf-8", newline="") as f:
674+
writer = csv.DictWriter(f, fieldnames=list(row.keys()))
675+
writer.writeheader()
676+
writer.writerow(row)
677+
678+
result = well_inventory_csv(file_path)
679+
assert result.exit_code == 1, result.stderr
680+
assert (
681+
result.payload["validation_errors"][0]["error"]
682+
== "measuring_point_height_ft or mp_height is required when depth_to_water_ft is provided for a non-null observation"
683+
)
684+
685+
532686
def test_blank_depth_to_water_still_creates_water_level_records(tmp_path):
533687
"""Blank depth-to-water is treated as missing while preserving the attempted measurement."""
534688
row = _minimal_valid_well_inventory_row()
@@ -539,7 +693,7 @@ def test_blank_depth_to_water_still_creates_water_level_records(tmp_path):
539693
"sample_method": "Steel-tape measurement",
540694
"data_quality": "Water level accurate to within two hundreths of a foot",
541695
"water_level_notes": "Attempted measurement",
542-
"mp_height_ft": 2.5,
696+
"mp_height_ft": 3.5,
543697
}
544698
)
545699

@@ -563,7 +717,7 @@ def test_blank_depth_to_water_still_creates_water_level_records(tmp_path):
563717
"2025-02-15T10:30:00Z"
564718
)
565719
assert observations[0].value is None
566-
assert observations[0].measuring_point_height == 2.5
720+
assert observations[0].measuring_point_height == 3.5
567721

568722

569723
def test_rerunning_same_well_inventory_csv_is_idempotent():

0 commit comments

Comments
 (0)