Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions domain/water_levels.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,43 @@
from a per-row importer or from a single-record API call.
"""

from domain.wells import (
RELEASE_STATUS_DRAFT,
RELEASE_STATUS_PRIVATE,
RELEASE_STATUS_PUBLIC,
)

MEASUREMENT_UNIT = "ft"
SAMPLE_MATRIX = "groundwater"
SAMPLE_QC_TYPE = "Normal"
GROUNDWATER_LEVEL_ACTIVITY_TYPE = "groundwater level"

_COPIED_RELEASE_STATUSES = frozenset({RELEASE_STATUS_PUBLIC, RELEASE_STATUS_PRIVATE})


def observation_release_status(well_release_status: str | None) -> str:
"""
Choose the release status for a water level observation an importer creates.

The public water level views filter on the observation's status as well as the
well's, so an observation left at the ``draft`` default stays hidden even when
the owner agreed to publish the well. Copying the well's status applies that
decision to the observation.

Only ``public`` and ``private`` are copied, because they are the only statuses
that indicate whether the well's data may be published. For any other status
the function returns ``draft``.

The BDMS-1561 backfill
(``20261110_0001_backfill_imported_water_level_release_status``) applies the
same rule to observations imported before this function existed, so old and
new imported observations follow it. It keeps its own copy of the rule, so a
change here must be made there too.
"""
if well_release_status in _COPIED_RELEASE_STATUSES:
return well_release_status
return RELEASE_STATUS_DRAFT


def reconcile_measuring_point_height(
csv_mp_height: float | None,
Expand Down
5 changes: 5 additions & 0 deletions services/well_inventory_csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
GROUNDWATER_LEVEL_ACTIVITY_TYPE,
MEASUREMENT_UNIT,
SAMPLE_MATRIX,
observation_release_status,
)
from domain.wells import (
alternate_ids,
Expand Down Expand Up @@ -866,6 +867,10 @@ def _add_csv_row(session: Session, group: Group, model: WellInventoryRow, user)
else model.data_quality or None
),
notes=model.water_level_notes,
# The public water level views check the observation's status as
# well as the well's, so without this the views exclude the
# observation even after the owner agreed to public availability.
release_status=observation_release_status(well.release_status),
)
session.add(observation)

Expand Down
21 changes: 21 additions & 0 deletions tests/test_domain_water_levels.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@
from datetime import datetime
from decimal import Decimal

import pytest

from domain.samples import water_level_sample_name
from domain.water_levels import (
depth_to_water_error,
measuring_point_height_conflict_message,
observation_release_status,
reconcile_measuring_point_height,
)

Expand Down Expand Up @@ -135,4 +138,22 @@ def test_water_level_sample_name_ignores_sub_minute_precision():
)


# --------------------------------------------------------------------------
# observation_release_status
# --------------------------------------------------------------------------
@pytest.mark.parametrize("well_status", ["public", "private", "draft"])
def test_observation_release_status_copies_the_well_status(well_status):
assert observation_release_status(well_status) == well_status


@pytest.mark.parametrize(
"well_status",
# A well with no status, values no view treats as a release decision (the
# legacy review terms, and archived), and casing the views would not match.
[None, "", "provisional", "final", "published", "archived", "Public"],
)
def test_observation_release_status_holds_anything_else_in_draft(well_status):
assert observation_release_status(well_status) == "draft"


# ============= EOF =============================================
50 changes: 50 additions & 0 deletions tests/test_well_inventory.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from schemas.well_inventory import WellInventoryRow
from services.util import transform_srid, convert_ft_to_m
from shapely import Point
from sqlalchemy import text


def _minimal_valid_well_inventory_row():
Expand Down Expand Up @@ -756,6 +757,55 @@ def test_public_availability_sets_thing_and_location_release_status(
assert locations[0].release_status == expected


@pytest.mark.parametrize(
"acknowledgement, expected",
[("True", "public"), ("False", "private"), ("", "draft")],
)
def test_public_availability_sets_water_level_observation_release_status(
tmp_path, acknowledgement, expected
):
"""The public water level views need the observation public as well as the well."""
row = _minimal_valid_well_inventory_row()
row.update(
{
"public_availability_acknowledgement": acknowledgement,
"water_level_date_time": "2025-02-15T10:30:00",
"depth_to_water_ft": "8",
"sample_method": "Steel-tape measurement",
}
)

file_path = tmp_path / "well-inventory-observation-release-status.csv"
with file_path.open("w", encoding="utf-8", newline="") as f:
writer = csv.DictWriter(f, fieldnames=list(row.keys()))
writer.writeheader()
writer.writerow(row)

result = well_inventory_csv(file_path)
assert result.exit_code == 0, result.stderr

with session_ctx() as session:
thing = session.query(Thing).one()
observation = session.query(Observation).one()
sample = observation.sample

assert observation.release_status == expected
assert observation.release_status == thing.release_status
# Only the observation follows the well; the visit records stay draft.
assert sample.release_status == "draft"
assert sample.field_activity.release_status == "draft"
assert sample.field_activity.field_event.release_status == "draft"

# The purpose of the fix is that the public EDR collection includes
# the observation, so check the collection directly. It should
# include the observation only when the well is public.
published = session.execute(
text("SELECT count(*) FROM ogc_waterlevels WHERE thing_id = :id"),
{"id": thing.id},
).scalar_one()
assert published == (1 if expected == "public" else 0)


def test_measuring_point_height_ft_used_for_thing_and_observation(tmp_path):
"""When measuring_point_height_ft is provided it is used for the thing's (MeasuringPointHistory) and observation's measuring_point_height values."""
row = _minimal_valid_well_inventory_row()
Expand Down
Loading