@@ -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+
532686def 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
569723def test_rerunning_same_well_inventory_csv_is_idempotent ():
0 commit comments