Skip to content
Merged
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
7 changes: 7 additions & 0 deletions core/lexicon.json
Original file line number Diff line number Diff line change
Expand Up @@ -8206,6 +8206,13 @@
"term": "OwnerComment",
"definition": "Legacy owner comments field"
},
{
"categories": [
"note_type"
],
"term": "Site Notes (legacy)",
"definition": "Legacy site notes field from WaterLevels"
},
{
"categories": [
"well_pump_type"
Expand Down
4 changes: 4 additions & 0 deletions db/thing.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,10 @@ def sampling_procedure_notes(self):
def construction_notes(self):
return self._get_notes("Construction")

@property
def site_notes(self):
return self._get_notes("Site Notes (legacy)")

@property
def well_status(self) -> str | None:
"""
Expand Down
1 change: 1 addition & 0 deletions schemas/thing.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ class BaseThingResponse(BaseResponseModel):
monitoring_frequencies: list[MonitoringFrequencyResponse] = []
general_notes: list[NoteResponse] = []
sampling_procedure_notes: list[NoteResponse] = []
site_notes: list[NoteResponse] = []
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

site_notes was added to the response schema with an empty-list default, but there are existing API/BDD checks for other note arrays (e.g., general_notes, sampling_procedure_notes) and none validate that site_notes is always present and is [] when empty (per Issue #549 acceptance criteria). Please add/extend an API test to assert the field is included and defaults to an empty list when no notes exist.

Suggested change
site_notes: list[NoteResponse] = []
site_notes: list[NoteResponse] = Field(default_factory=list)

Copilot uses AI. Check for mistakes.

@field_validator("monitoring_frequencies", mode="before")
def remove_records_with_end_date(cls, monitoring_frequencies):
Expand Down
34 changes: 34 additions & 0 deletions transfers/waterlevels_transfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
Contact,
FieldEventParticipant,
Parameter,
Notes,
)
from db.engine import session_ctx
from transfers.transferer import Transferer
Expand Down Expand Up @@ -158,6 +159,7 @@ def _transfer_hook(self, session: Session) -> None:
"observations_created": 0,
"contacts_created": 0,
"contacts_reused": 0,
"notes_created": 0,
}

gwd = self.cleaned_df.groupby(["PointID"])
Expand Down Expand Up @@ -396,6 +398,38 @@ def _transfer_hook(self, session: Session) -> None:
session.execute(insert(Observation), observation_rows)
stats["observations_created"] += len(observation_rows)

# Site Notes (legacy)
# If there are duplicate notes for a single point ID, we only create one note.
# However, if some duplicates are "time stamped" (meaning they are attached to
# rows with different dates), we should ideally preserve that context.
# The current implementation prepends the date to the note content
# to ensure that duplicate content from different dates remains distinct.
unique_notes: dict[str, datetime] = {}
for prep in prepared_rows:
if hasattr(prep["row"], "SiteNotes") and prep["row"].SiteNotes:
content = str(prep["row"].SiteNotes).strip()
if content:
dt = prep["dt_utc"]
# We keep all notes that have different content OR different dates
# Actually, if content is same but date is different, we want to see it.
# So we key by (content, date)
key = (content, dt.date())
if key not in unique_notes:
unique_notes[key] = dt

for (content, _), dt in unique_notes.items():
date_prefix = dt.strftime("%Y-%m-%d")
session.add(
Notes(
target_table="thing",
target_id=thing_id,
note_type="Site Notes (legacy)",
content=f"{date_prefix}: {content}",
release_status="public",
)
)
stats["notes_created"] += 1
Comment on lines +401 to +431
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New behavior migrates legacy SiteNotes into the polymorphic Notes table, but there are existing unit tests for WaterLevelTransferer (e.g., in tests/test_transfer_legacy_dates.py) and none appear to cover this new note-migration path. Adding tests for: (1) skipping None/blank/whitespace-only notes and (2) deduping unique notes per PointID (including whitespace variants) would prevent regressions.

Copilot uses AI. Check for mistakes.

session.commit()
session.expunge_all()
stats["groups_processed"] += 1
Expand Down