[codex] Reshape well details response around field events#660
Conversation
{
"well": {
"id": 6003,
"created_at": "2026-02-27T17:34:20Z",
"release_status": "public",
"name": "MG-030",
"site_name": null,
"thing_type": "water-well",
"current_location": {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-107.8512, 33.1584]
},
"properties": {
"county": "Sierra",
"state": "NM"
},
"release_status": "private"
},
"first_visit_date": "2024-05-10",
"groups": [],
"monitoring_status": "active",
"links": [
{
"thing_id": 6003,
"relation": "legacy",
"alternate_id": "MG-030",
"alternate_organization": "NMBGMR"
}
],
"monitoring_frequencies": [],
"general_notes": [],
"sampling_procedure_notes": [],
"site_notes": [],
"well_purposes": ["monitoring"],
"well_depth": 120.0,
"well_depth_unit": "ft",
"well_depth_source": "measured",
"historic_depth_to_water": [
"historic depth to water: 12 ft",
"historic depth to water: 18 ft"
],
"hole_depth": 130.0,
"hole_depth_unit": "ft",
"well_casing_diameter": 6.0,
"well_casing_diameter_unit": "in",
"well_casing_depth": 110.0,
"well_casing_depth_unit": "ft",
"well_casing_materials": ["PVC"],
"well_completion_date": "2020-06-01",
"well_completion_date_source": "driller log",
"well_driller_name": "Example Drilling",
"well_construction_method": "rotary",
"well_construction_method_source": "driller log",
"well_pump_type": "submersible",
"well_pump_depth": 95.0,
"well_pump_depth_unit": "ft",
"well_status": "active",
"open_status": true,
"datalogger_suitability_status": "suitable",
"measuring_point_height": 5.0,
"measuring_point_height_unit": "ft",
"measuring_point_description": "Top of casing",
"aquifers": [],
"water_notes": [],
"construction_notes": [],
"contacts": [],
"permissions": [],
"formation_completion_code": null,
"nma_formation_zone": null,
"well_location_note": [
"turn left at the cattle guard",
"use the south gate"
]
},
"contacts": [
{
"id": 101,
"created_at": "2026-02-27T17:40:00Z",
"release_status": "public",
"contact_name": "Jane Smith",
"contact_organization": null,
"emails": [
{
"id": 201,
"created_at": "2026-02-27T17:40:01Z",
"release_status": "public",
"email": "jane@example.org"
}
],
"phones": [
{
"id": 301,
"created_at": "2026-02-27T17:40:02Z",
"release_status": "public",
"phone_number": "555-123-4567"
}
],
"addresses": []
}
],
"sensors": [
{
"id": 401,
"created_at": "2026-02-27T17:41:00Z",
"release_status": "public",
"sensor_model": "Levelogger 5"
}
],
"deployments": [
{
"id": 501,
"created_at": "2026-02-27T17:42:00Z",
"release_status": "public",
"thing_id": 6003,
"sensor_id": 401,
"installation_date": "2026-02-01",
"removal_date": null,
"sensor": {
"id": 401,
"created_at": "2026-02-27T17:41:00Z",
"release_status": "public",
"sensor_model": "Levelogger 5"
}
}
],
"well_screens": [
{
"id": 701,
"created_at": "2026-02-27T17:43:00Z",
"release_status": "public",
"thing_id": 6003,
"aquifer_system_id": 10,
"aquifer_system": "Santa Fe Group",
"aquifer_type": "bedrock",
"geologic_formation_id": 22,
"geologic_formation": "QTsf",
"screen_depth_bottom": 110.0,
"screen_depth_bottom_unit": "ft",
"screen_depth_top": 90.0,
"screen_depth_top_unit": "ft",
"screen_type": "slotted pvc",
"screen_description": "primary production interval"
}
],
"field_events": [
{
"id": 801,
"created_at": "2026-02-27T17:45:00Z",
"release_status": "public",
"thing_id": 6003,
"event_date": "2026-02-27T17:45:00Z",
"notes": "Monthly groundwater check",
"field_event_participants": [
{
"id": 901,
"created_at": "2026-02-27T17:45:10Z",
"release_status": "public",
"field_event_id": 801,
"contact_id": 101,
"participant_role": "sampler",
"participant": {
"id": 101,
"created_at": "2026-02-27T17:40:00Z",
"release_status": "public",
"contact_name": "Jane Smith",
"contact_organization": null
}
}
],
"field_activities": [
{
"id": 1001,
"created_at": "2026-02-27T17:45:20Z",
"release_status": "public",
"field_event_id": 801,
"activity_type": "groundwater level",
"notes": "Steel tape measurement",
"samples": [
{
"id": 1101,
"created_at": "2026-02-27T17:45:30Z",
"release_status": "public",
"contact": {
"id": 101,
"created_at": "2026-02-27T17:40:00Z",
"release_status": "public",
"contact_name": "Jane Smith",
"contact_organization": null
},
"sample_date": "2026-02-27T17:45:30Z",
"sample_name": "MG-030-gwl-20260227",
"sample_matrix": "water",
"sample_method": "Steel-tape measurement",
"qc_type": "Normal",
"notes": "Clear access to casing",
"depth_top": null,
"depth_bottom": null,
"observations": [
{
"id": 1201,
"created_at": "2026-02-27T17:45:40Z",
"release_status": "public",
"sample_id": 1101,
"sensor_id": 401,
"observation_datetime": "2026-02-27T17:45:40Z",
"parameter": {
"id": 1,
"created_at": "2025-01-01T00:00:00Z",
"release_status": "public",
"parameter_name": "groundwater level"
},
"value": 14.2,
"unit": "ft",
"nma_data_quality": null,
"depth_to_water_bgs": 9.2,
"measuring_point_height": 5.0,
"groundwater_level_reason": "Water level not affected"
}
]
}
]
}
]
}
]
} |
There was a problem hiding this comment.
Pull request overview
This PR reshapes the /water-well/{thing_id}/details response to make field_events the primary nested structure, removing previously redundant top-level well-detail fields and tightening nested response payloads.
Changes:
- Replace top-level “latest sample” / “recent observations” / “field_event_participants” branches with a top-level
field_eventslist containing nested participants, activities, samples, and observations. - Remove embedded
thingdata fromwell_screensin the well details payload by introducingWellScreenBaseResponse. - Refactor payload-stage timing into a context manager and keep field events ordered newest-first.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
services/well_details_helper.py |
Reworks payload assembly to load and return field events (with nested data) and removes now-redundant “latest/recent” branches; introduces a context-managed timing helper. |
schemas/well_details.py |
Updates the well details response model to expose field_events and adjusts nested response shapes. |
schemas/thing.py |
Splits well screen response into a base schema (no thing) plus the full schema (includes thing). |
schemas/sample.py |
Adds a well-details-specific sample response model variant. |
tests/test_thing.py |
Updates well details tests to assert the new field_events-centric response shape and removal of thing from well screens. |
|
@copilot resolve the merge conflicts in this pull request |
1 similar comment
|
@copilot resolve the merge conflicts in this pull request |
| later_field_event = FieldEvent( | ||
| thing_id=water_well_thing.id, | ||
| event_date="2025-01-02T00:00:00Z", | ||
| notes="later field event", | ||
| release_status="draft", | ||
| ) |
There was a problem hiding this comment.
In this new test, FieldEvent.event_date is a timezone-aware datetime column; passing an ISO string ("2025-01-02T00:00:00Z") relies on DB/driver coercion and can break on some backends. Use an actual datetime (e.g., datetime(..., tzinfo=timezone.utc)) for event_date to keep the test portable and type-correct.
| session.delete(later_field_event) | ||
| session.commit() | ||
|
|
||
|
|
There was a problem hiding this comment.
There are whitespace-only lines after the finally block here. This can introduce new flake8 W293/W291 failures; please remove the trailing spaces/blank whitespace-only lines.
| def test_get_water_well_details_payload_limits_field_events( | ||
| water_well_thing, | ||
| field_event, | ||
| ): | ||
| from db import FieldEvent |
There was a problem hiding this comment.
The /details response no longer returns latest_field_event_sample or top-level field_event_participants (they’ve been moved under field_events). There is still a test later in this file (test_get_water_well_details_payload_includes_imported_water_level_staff) asserting those removed keys, which will fail with the new response shape. Update that test to assert against the new field_events[..].field_activities[..].samples[..].contact and field_events[..].field_event_participants structure.
… views and documentation
|
What needs to change on the front-end to make this possible? And will they need to change at the same time? |
|
@jeremyzilar this doesn't break anything on the front end, it just exposes all the field_events as a list of dicts. field events are sorted in desc order. the field_participants are included in the field_events payload |
Summary
field_eventsthingpayloadsResponse Shape Updates
field_event_participantslist with top-levelfield_eventsrecent_groundwater_level_observationsfrom the well details responselatest_field_event_samplefrom the well details responsethingobject in the well details payloadWhy
The old response split related field-event data across several top-level fields, which duplicated information and made the client stitch the event model back together. Returning field events directly makes the payload match the domain structure and removes redundant response branches.
Validation
pytest tests/test_thing.py -k "water_well_details_payload"black schemas/thing.py schemas/well_details.py services/well_details_helper.py tests/test_thing.pyflake8 schemas/thing.py schemas/well_details.py services/well_details_helper.py tests/test_thing.py(fails on pre-existingE501line-length violations in these files)