Skip to content

Commit fc5cdf5

Browse files
committed
refactor: update well inventory CSV files with corrected UTM coordinates and improved data validation
1 parent 368a91a commit fc5cdf5

24 files changed

Lines changed: 457 additions & 420 deletions

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ dependencies = [
9696
"typing-inspection==0.4.1",
9797
"tzdata==2025.2",
9898
"urllib3==2.5.0",
99+
"utm>=0.8.1",
99100
"uvicorn==0.38.0",
100101
"yarl==1.20.1",
101102
]

schemas/well_inventory.py

Lines changed: 70 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
from datetime import datetime
1818
from typing import Optional, Annotated, TypeAlias
1919

20-
from pydantic import BaseModel, model_validator, BeforeValidator
20+
import phonenumbers
21+
import utm
22+
from pydantic import BaseModel, model_validator, BeforeValidator, validate_email
2123

2224
from constants import STATE_CODES
2325
from core.enums import (
@@ -80,12 +82,34 @@ def state_validator(v):
8082
return v
8183

8284

85+
def phone_validator(phone_number_str):
86+
phone_number_str = phone_number_str.strip()
87+
if phone_number_str:
88+
parsed_number = phonenumbers.parse(phone_number_str, "US")
89+
if phonenumbers.is_valid_number(parsed_number):
90+
formatted_number = phonenumbers.format_number(
91+
parsed_number, phonenumbers.PhoneNumberFormat.E164
92+
)
93+
return formatted_number
94+
else:
95+
raise ValueError(f"Invalid phone number. {phone_number_str}")
96+
97+
98+
def email_validator_function(email_str):
99+
if email_str:
100+
try:
101+
validate_email(email_str)
102+
return email_str
103+
except ValueError as e:
104+
raise ValueError(f"Invalid email format. {email_str}") from e
105+
106+
83107
# Reusable type
84108
PhoneTypeField: TypeAlias = Annotated[
85109
Optional[PhoneType], BeforeValidator(blank_to_none)
86110
]
87111
ContactTypeField: TypeAlias = Annotated[
88-
Optional[ContactType], BeforeValidator(primary_default)
112+
Optional[ContactType], BeforeValidator(blank_to_none)
89113
]
90114
EmailTypeField: TypeAlias = Annotated[
91115
Optional[EmailType], BeforeValidator(blank_to_none)
@@ -102,6 +126,10 @@ def state_validator(v):
102126
Optional[str], BeforeValidator(postal_code_or_none)
103127
]
104128
StateField: TypeAlias = Annotated[Optional[str], BeforeValidator(state_validator)]
129+
PhoneField: TypeAlias = Annotated[Optional[str], BeforeValidator(phone_validator)]
130+
EmailField: TypeAlias = Annotated[
131+
Optional[str], BeforeValidator(email_validator_function)
132+
]
105133

106134

107135
# ============= EOF =============================================
@@ -126,14 +154,14 @@ class WellInventoryRow(BaseModel):
126154
contact_1_name: Optional[str] = None
127155
contact_1_organization: Optional[str] = None
128156
contact_1_role: ContactRoleField = None
129-
contact_1_type: ContactTypeField = "Primary"
130-
contact_1_phone_1: Optional[str] = None
157+
contact_1_type: ContactTypeField = None
158+
contact_1_phone_1: PhoneField = None
131159
contact_1_phone_1_type: PhoneTypeField = None
132-
contact_1_phone_2: Optional[str] = None
160+
contact_1_phone_2: PhoneField = None
133161
contact_1_phone_2_type: PhoneTypeField = None
134-
contact_1_email_1: Optional[str] = None
162+
contact_1_email_1: EmailField = None
135163
contact_1_email_1_type: EmailTypeField = None
136-
contact_1_email_2: Optional[str] = None
164+
contact_1_email_2: EmailField = None
137165
contact_1_email_2_type: EmailTypeField = None
138166
contact_1_address_1_line_1: Optional[str] = None
139167
contact_1_address_1_line_2: Optional[str] = None
@@ -151,14 +179,14 @@ class WellInventoryRow(BaseModel):
151179
contact_2_name: Optional[str] = None
152180
contact_2_organization: Optional[str] = None
153181
contact_2_role: ContactRoleField = None
154-
contact_2_type: ContactTypeField = "Primary"
155-
contact_2_phone_1: Optional[str] = None
182+
contact_2_type: ContactTypeField = None
183+
contact_2_phone_1: PhoneField = None
156184
contact_2_phone_1_type: PhoneTypeField = None
157-
contact_2_phone_2: Optional[str] = None
185+
contact_2_phone_2: PhoneField = None
158186
contact_2_phone_2_type: PhoneTypeField = None
159-
contact_2_email_1: Optional[str] = None
187+
contact_2_email_1: EmailField = None
160188
contact_2_email_1_type: EmailTypeField = None
161-
contact_2_email_2: Optional[str] = None
189+
contact_2_email_2: EmailField = None
162190
contact_2_email_2_type: EmailTypeField = None
163191
contact_2_address_1_line_1: Optional[str] = None
164192
contact_2_address_1_line_2: Optional[str] = None
@@ -220,6 +248,16 @@ class WellInventoryRow(BaseModel):
220248

221249
@model_validator(mode="after")
222250
def validate_model(self):
251+
# verify utm in NM
252+
zone = int(self.utm_zone[:-1])
253+
northern = self.utm_zone[-1] == "N"
254+
255+
lat, lon = utm.to_latlon(
256+
self.utm_easting, self.utm_northing, zone, northern=northern
257+
)
258+
if not ((31.33 <= lat <= 37.00) and (-109.05 <= lon <= -103.00)):
259+
raise ValueError("UTM coordinates are outside of the NM")
260+
223261
required_attrs = ("line_1", "type", "state", "city", "postal_code")
224262
all_attrs = ("line_1", "line_2", "type", "state", "city", "postal_code")
225263
for jdx in (1, 2):
@@ -234,19 +272,30 @@ def validate_model(self):
234272
raise ValueError("All contact address fields must be provided")
235273

236274
name = getattr(self, f"{key}_name")
237-
if name and not getattr(self, f"{key}_role"):
238-
raise ValueError("Role must be provided if name is provided")
239-
240-
phone = getattr(self, f"{key}_phone_1")
241-
phone_type = getattr(self, f"{key}_phone_1_type")
275+
if name:
276+
if not getattr(self, f"{key}_role"):
277+
raise ValueError(
278+
f"{key}_role must be provided if name is provided"
279+
)
280+
if not getattr(self, f"{key}_type"):
281+
raise ValueError(
282+
f"{key}_type must be provided if name is provided"
283+
)
284+
285+
phone = getattr(self, f"{key}_phone_{idx}")
286+
tag = f"{key}_phone_{idx}_type"
287+
phone_type = getattr(self, f"{key}_phone_{idx}_type")
242288
if phone and not phone_type:
243289
raise ValueError(
244-
"Phone type must be provided if phone number is provided"
290+
f"{tag} must be provided if phone number is provided"
245291
)
246292

247-
email = getattr(self, f"{key}_email_1")
248-
email_type = getattr(self, f"{key}_email_1_type")
293+
email = getattr(self, f"{key}_email_{idx}")
294+
tag = f"{key}_email_{idx}_type"
295+
email_type = getattr(self, tag)
249296
if email and not email_type:
250-
raise ValueError("Email type must be provided if email is provided")
297+
raise ValueError(
298+
f"{tag} type must be provided if email is provided"
299+
)
251300

252301
return self
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
project,measuring_point_height_ft,well_name_point_id,site_name,date_time,field_staff,contact_role,utm_easting,utm_northing,utm_zone,elevation_ft,elevation_method
2-
foo,10,WELL001,Site Alpha,2025-02-15T10:30:00-08:00,Jane Doe,Owner,345678.12,3987654.21,13,5120.5,LiDAR DEM
3-
foob,10,WELL001,Site Beta,2025-03-20T09:15:00-08:00,John Smith,Manager,346789.34,3987655.32,13,5130.7,LiDAR DEM
2+
foo,10,WELL001,Site Alpha,2025-02-15T10:30:00-08:00,Jane Doe,Owner,250000,4000000,13N,5120.5,LiDAR DEM
3+
foob,10,WELL001,Site Beta,2025-03-20T09:15:00-08:00,John Smith,Manager,250000,4000000,13N,5130.7,LiDAR DEM
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
project,well_name_point_id,site_name,date_time,field_staff,utm_easting,utm_northing,utm_zone,elevation_ft,elevation_method,measuring_point_height_ft,field_staff_2,field_staff_3,contact_1_name,contact_1_organization,contact_1_role,contact_1_type,contact_1_phone_1,contact_1_phone_1_type,contact_1_phone_2,contact_1_phone_2_type,contact_1_email_1,contact_1_email_1_type,contact_1_email_2,contact_1_email_2_type,contact_1_address_1_line_1,contact_1_address_1_line_2,contact_1_address_1_type,contact_1_address_1_state,contact_1_address_1_city,contact_1_address_1_postal_code,contact_1_address_2_line_1,contact_1_address_2_line_2,contact_1_address_2_type,contact_1_address_2_state,contact_1_address_2_city,contact_1_address_2_postal_code,contact_2_name,contact_2_organization,contact_2_role,contact_2_type,contact_2_phone_1,contact_2_phone_1_type,contact_2_phone_2,contact_2_phone_2_type,contact_2_email_1,contact_2_email_1_type,contact_2_email_2,contact_2_email_2_type,contact_2_address_1_line_1,contact_2_address_1_line_2,contact_2_address_1_type,contact_2_address_1_state,contact_2_address_1_city,contact_2_address_1_postal_code,contact_2_address_2_line_1,contact_2_address_2_line_2,contact_2_address_2_type,contact_2_address_2_state,contact_2_address_2_city,contact_2_address_2_postal_code,directions_to_site,specific_location_of_well,repeat_measurement_permission,sampling_permission,datalogger_installation_permission,public_availability_acknowledgement,result_communication_preference,contact_special_requests_notes,ose_well_record_id,date_drilled,completion_source,total_well_depth_ft,historic_depth_to_water_ft,depth_source,well_pump_type,well_pump_depth_ft,is_open,datalogger_possible,casing_diameter_ft,measuring_point_description,well_purpose,well_hole_status,monitoring_frequency,sampling_scenario_notes,well_measuring_notes,sample_possible
2+
Middle Rio Grande Groundwater Monitoring,MRG-001_MP1,Smith Farm Domestic Well,2025-02-15T10:30:00-07:00,A Lopez,250000,4000000,13N,5250,Survey-grade GPS,1.5,B Chen,,John Smith,NMBGMR,Owner,foo,505-555-0101,Primary,,,john.smith@example.com,Primary,,,123 County Rd 7,,Mailing,NM,Los Lunas,87031,,,,,,,Maria Garcia,NMBGMR,Principal Investigator,Secondary,505-555-0123,Home,,,maria.garcia@mrgcd.nm.gov,Work,,,1931 2nd St SW,Suite 200,Mailing,NM,Albuquerque,87102,,,,,,,Gate off County Rd 7 0.4 miles south of canal crossing,Domestic well in pump house east of residence,True,True,True,True,email,Call before visits during irrigation season,OSE-123456,2010-06-15,OSE well record,280,45,owner estimate,submersible,200,True,True,0.5,Top of steel casing inside pump house marked with orange paint,Domestic,active,Biannual,Sample only when pump has been off more than 12 hours,Measure before owner starts irrigation,True
3+
Middle Rio Grande Groundwater Monitoring,MRG-003_MP1,Old Orchard Well,2025-01-20T09:00:00-07:00,B Chen,250000,4000000,13N,5320,Global positioning system (GPS),1.8,,,Emily Davis,NMBGMR,Biologist,Primary,505-555-0303,Work,,,emily.davis@example.org,Work,,,78 Orchard Ln,,Mailing,NM,Los Lunas,87031,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,From Main St turn east on Orchard Ln well house at dead end,Abandoned irrigation well in small cinderblock building,False,False,False,True,phone,Owner prefers weekday visits,,1965-04-10,historic log scanned,350,60,historic log,vertical turbine inactive,280,False,False,0.75,Top of steel casing under removable hatch use fixed reference mark,Irrigation,abandoned,Annual,Sampling not permitted water level only when owner present,Well house can be locked coordinate ahead,False
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
project,well_name_point_id,site_name,date_time,field_staff,utm_easting,utm_northing,utm_zone,elevation_ft,elevation_method,measuring_point_height_ft,field_staff_2,field_staff_3,contact_1_name,contact_1_organization,contact_1_role,contact_1_type,contact_1_phone_1,contact_1_phone_1_type,contact_1_phone_2,contact_1_phone_2_type,contact_1_email_1,contact_1_email_1_type,contact_1_email_2,contact_1_email_2_type,contact_1_address_1_line_1,contact_1_address_1_line_2,contact_1_address_1_type,contact_1_address_1_state,contact_1_address_1_city,contact_1_address_1_postal_code,contact_1_address_2_line_1,contact_1_address_2_line_2,contact_1_address_2_type,contact_1_address_2_state,contact_1_address_2_city,contact_1_address_2_postal_code,contact_2_name,contact_2_organization,contact_2_role,contact_2_type,contact_2_phone_1,contact_2_phone_1_type,contact_2_phone_2,contact_2_phone_2_type,contact_2_email_1,contact_2_email_1_type,contact_2_email_2,contact_2_email_2_type,contact_2_address_1_line_1,contact_2_address_1_line_2,contact_2_address_1_type,contact_2_address_1_state,contact_2_address_1_city,contact_2_address_1_postal_code,contact_2_address_2_line_1,contact_2_address_2_line_2,contact_2_address_2_type,contact_2_address_2_state,contact_2_address_2_city,contact_2_address_2_postal_code,directions_to_site,specific_location_of_well,repeat_measurement_permission,sampling_permission,datalogger_installation_permission,public_availability_acknowledgement,result_communication_preference,contact_special_requests_notes,ose_well_record_id,date_drilled,completion_source,total_well_depth_ft,historic_depth_to_water_ft,depth_source,well_pump_type,well_pump_depth_ft,is_open,datalogger_possible,casing_diameter_ft,measuring_point_description,well_purpose,well_hole_status,monitoring_frequency,sampling_scenario_notes,well_measuring_notes,sample_possible
2+
Middle Rio Grande Groundwater Monitoring,MRG-001_MP1,Smith Farm Domestic Well,25-02-15T10:30:00-07:00,A Lopez,250000,4000000,13N,5250,Survey-grade GPS,1.5,B Chen,,John Smith,NMBGMR,Owner,Primary,505-555-0101,Primary,,,john.smith@example.com,Primary,,,123 County Rd 7,,Mailing,NM,Los Lunas,87031,,,,,,,Maria Garcia,NMBGMR,Principal Investigator,Secondary,505-555-0123,Home,,,maria.garcia@mrgcd.nm.gov,Work,,,1931 2nd St SW,Suite 200,Mailing,NM,Albuquerque,87102,,,,,,,Gate off County Rd 7 0.4 miles south of canal crossing,Domestic well in pump house east of residence,True,True,True,True,email,Call before visits during irrigation season,OSE-123456,2010-06-15,OSE well record,280,45,owner estimate,submersible,200,True,True,0.5,Top of steel casing inside pump house marked with orange paint,Domestic,active,Biannual,Sample only when pump has been off more than 12 hours,Measure before owner starts irrigation,True
3+
Middle Rio Grande Groundwater Monitoring,MRG-003_MP1,Old Orchard Well,2025-01-20T09:00:00-07:00,B Chen,250000,4000000,13N,5320,Global positioning system (GPS),1.8,,,Emily Davis,NMBGMR,Biologist,Primary,505-555-0303,Work,,,emily.davis@example.org,Work,,,78 Orchard Ln,,Mailing,NM,Los Lunas,87031,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,From Main St turn east on Orchard Ln well house at dead end,Abandoned irrigation well in small cinderblock building,False,False,False,True,phone,Owner prefers weekday visits,,1965-04-10,historic log scanned,350,60,historic log,vertical turbine inactive,280,False,False,0.75,Top of steel casing under removable hatch use fixed reference mark,Irrigation,abandoned,Annual,Sampling not permitted water level only when owner present,Well house can be locked coordinate ahead,False
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
well_name_point_id,site_name,date_time,field_staff,contact_role,utm_easting,utm_northing,utm_zone,elevation_ft,elevation_method
2-
WELL005,Site Alpha,2025-02-30T10:30:00-08:00,Jane Doe,Owner,345678.12,3987654.21,13,5120.5,GPS
3-
WELL006,Site Beta,2025-13-20T09:15:00-08:00,John Smith,Manager,346789.34,3987655.32,13,5130.7,Survey
4-
WELL007,Site Gamma,not-a-date,Emily Clark,Supervisor,347890.45,3987657.54,13,5150.3,Survey
5-
WELL008,Site Delta,2025-04-10 11:00:00,Michael Lee,Technician,348901.56,3987658.65,13,5160.4,GPS
2+
WELL005,Site Alpha,2025-02-30T10:30:00-08:00,Jane Doe,Owner,250000,4000000,13N,5120.5,GPS
3+
WELL006,Site Beta,2025-13-20T09:15:00-08:00,John Smith,Manager,250000,4000000,13N,5130.7,Survey
4+
WELL007,Site Gamma,not-a-date,Emily Clark,Supervisor,250000,4000000,13N,5150.3,Survey
5+
WELL008,Site Delta,2025-04-10 11:00:00,Michael Lee,Technician,250000,4000000,13N,5160.4,GPS
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
project,well_name_point_id,site_name,date_time,field_staff,utm_easting,utm_northing,utm_zone,elevation_ft,elevation_method,measuring_point_height_ft,field_staff_2,field_staff_3,contact_1_name,contact_1_organization,contact_1_role,contact_1_type,contact_1_phone_1,contact_1_phone_1_type,contact_1_phone_2,contact_1_phone_2_type,contact_1_email_1,contact_1_email_1_type,contact_1_email_2,contact_1_email_2_type,contact_1_address_1_line_1,contact_1_address_1_line_2,contact_1_address_1_type,contact_1_address_1_state,contact_1_address_1_city,contact_1_address_1_postal_code,contact_1_address_2_line_1,contact_1_address_2_line_2,contact_1_address_2_type,contact_1_address_2_state,contact_1_address_2_city,contact_1_address_2_postal_code,contact_2_name,contact_2_organization,contact_2_role,contact_2_type,contact_2_phone_1,contact_2_phone_1_type,contact_2_phone_2,contact_2_phone_2_type,contact_2_email_1,contact_2_email_1_type,contact_2_email_2,contact_2_email_2_type,contact_2_address_1_line_1,contact_2_address_1_line_2,contact_2_address_1_type,contact_2_address_1_state,contact_2_address_1_city,contact_2_address_1_postal_code,contact_2_address_2_line_1,contact_2_address_2_line_2,contact_2_address_2_type,contact_2_address_2_state,contact_2_address_2_city,contact_2_address_2_postal_code,directions_to_site,specific_location_of_well,repeat_measurement_permission,sampling_permission,datalogger_installation_permission,public_availability_acknowledgement,result_communication_preference,contact_special_requests_notes,ose_well_record_id,date_drilled,completion_source,total_well_depth_ft,historic_depth_to_water_ft,depth_source,well_pump_type,well_pump_depth_ft,is_open,datalogger_possible,casing_diameter_ft,measuring_point_description,well_purpose,well_hole_status,monitoring_frequency,sampling_scenario_notes,well_measuring_notes,sample_possible
2+
Middle Rio Grande Groundwater Monitoring,MRG-001_MP1,Smith Farm Domestic Well,2025-02-15T10:30:00-07:00,A Lopez,250000,4000000,13N,5250,Survey-grade GPS,1.5,B Chen,,John Smith,NMBGMR,Owner,Primary,505-555-0101,Primary,,,john.smithexample.com,Primary,,,123 County Rd 7,,Mailing,NM,Los Lunas,87031,,,,,,,Maria Garcia,NMBGMR,Principal Investigator,Secondary,505-555-0123,Home,,,maria.garcia@mrgcd.nm.gov,Work,,,1931 2nd St SW,Suite 200,Mailing,NM,Albuquerque,87102,,,,,,,Gate off County Rd 7 0.4 miles south of canal crossing,Domestic well in pump house east of residence,True,True,True,True,email,Call before visits during irrigation season,OSE-123456,2010-06-15,OSE well record,280,45,owner estimate,submersible,200,True,True,0.5,Top of steel casing inside pump house marked with orange paint,Domestic,active,Biannual,Sample only when pump has been off more than 12 hours,Measure before owner starts irrigation,True
3+
Middle Rio Grande Groundwater Monitoring,MRG-003_MP1,Old Orchard Well,2025-01-20T09:00:00-07:00,B Chen,250000,4000000,13N,5320,Global positioning system (GPS),1.8,,,Emily Davis,NMBGMR,Biologist,Primary,505-555-0303,Work,,,emily.davis@example.org,Work,,,78 Orchard Ln,,Mailing,NM,Los Lunas,87031,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,From Main St turn east on Orchard Ln well house at dead end,Abandoned irrigation well in small cinderblock building,False,False,False,True,phone,Owner prefers weekday visits,,1965-04-10,historic log scanned,350,60,historic log,vertical turbine inactive,280,False,False,0.75,Top of steel casing under removable hatch use fixed reference mark,Irrigation,abandoned,Annual,Sampling not permitted water level only when owner present,Well house can be locked coordinate ahead,False
Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
project,well_name_point_id,site_name,date_time,field_staff,utm_easting,utm_northing,utm_zone,elevation_ft,elevation_method,measuring_point_height_ft,contact_role,contact_type
2-
ProjectA,WELL001,Site1,2025-02-15T10:30:00-08:00,John Doe,345678,3987654,13,5000,Survey,2.5,INVALID_ROLE,owner
3-
ProjectB,WELL002,Site2,2025-02-16T11:00:00-08:00,Jane Smith,345679,3987655,13,5100,Survey,2.7,manager,INVALID_TYPE
4-
ProjectC,WELL003,Site3,2025-02-17T09:45:00-08:00,Jim Beam,345680,3987656,13,5200,INVALID_METHOD,2.6,manager,owner
5-
ProjectD,WELL004,Site4,2025-02-18T08:20:00-08:00,Jack Daniels,345681,3987657,13,5300,Survey,2.8,INVALID_ROLE,INVALID_TYPE
6-
2+
ProjectA,WELL001,Site1,2025-02-15T10:30:00-08:00,John Doe,250000,4000000,13N,5000,Survey,2.5,INVALID_ROLE,owner
3+
ProjectB,WELL002,Site2,2025-02-16T11:00:00-08:00,Jane Smith,250000,4000000,13N,5100,Survey,2.7,manager,INVALID_TYPE
4+
ProjectC,WELL003,Site3,2025-02-17T09:45:00-08:00,Jim Beam,250000,4000000,13N,5200,INVALID_METHOD,2.6,manager,owner
5+
ProjectD,WELL004,Site4,2025-02-18T08:20:00-08:00,Jack Daniels,250000,4000000,13N,5300,Survey,2.8,INVALID_ROLE,INVALID_TYPE

0 commit comments

Comments
 (0)