Skip to content

V1.0.0.2#666

Merged
jirhiker merged 15 commits into
productionfrom
staging
Apr 28, 2026
Merged

V1.0.0.2#666
jirhiker merged 15 commits into
productionfrom
staging

Conversation

@jirhiker
Copy link
Copy Markdown
Member

Final release before launch

jeremyzilar and others added 15 commits April 27, 2026 18:01
…n join

Document Thing virtual filter fields in query_helper. Add contacts filter
matching any linked Contact.name with contains, ncontains, eq, ne,
startswith, endswith.
- Accept repeated filter query params via order_sort_filter merge of filters list
- Contact virtual filter field things: EXISTS join ThingContactAssociation and
  Thing.name for contains/null operators (inverse of Thing contacts filter)
- Thing virtual sorts for properties and proxies that cannot use ORDER BY on ORM
  attributes: monitoring_status, well_status, datalogger_suitability_status,
  site_name (NMBGMR thing_id_link), contacts and aquifers (min lower name),
  open_status (CASE on Open Status history), measuring_point_height (latest MP row)
- Contact virtual sort for things: min lower Thing.name across associations
- Document dispatch order in _apply_json_filter_clause and module header
- Add sqlalchemy case and nulls_last imports
- Switch get_db_contacts to keyword-only filters argument matching
  order_sort_filter(filters=...)
- Document virtual things filter and get_db_contacts link to list endpoint
- Split add_contact signature across lines for line length
- Replace single filter string with Annotated list from Query(alias=filter)
  so Refine can send multiple JSON filter objects (AND semantics)
- Point get_contacts docstring at virtual things filter and get_db_contacts
- Add typing Annotated import for filter_params
- things contains, no match, and nnull filter cases
- Multiple filter parameters AND together (things plus name)
- Sort by things (min site name) returns 200 and includes linked contact
- Module-scoped auth override matching other API tests
- monitoring_status and well_status descending or ascending
- site_name, contacts, open_status, measuring_point_height, aquifers
  sort smoke tests (expect 200, SQL-backed ORDER BY)
- Covers Thing virtual sort keys used by the wells DataGrid
- Features bullet notes repeated filter params and virtual association fields
- Points readers to docs/refine-json-filters-and-virtual-fields.md for rationale
- New subsection under architecture for DataGrid filter and sort wiring
- Cross-link to refine-json-filters-and-virtual-fields.md for maintainers
- Explains repeated filter query params, virtual filter fields, EXISTS pattern,
  and inverse Thing contacts versus Contact things associations
- Documents sorting for non-column Thing and Contact fields including scalar
  summaries (min names, StatusHistory subqueries, site_name via thing_id_link)
- Tables map API behavior to query_helper helpers; notes tests to read

Note: docs/ is gitignored by default; this file is tracked via force-add so the
guide stays in version control alongside query_helper.
…st-filtering

BDMS 774 Filtering and Sorting Wells & Contacts
Copilot AI review requested due to automatic review settings April 28, 2026 16:27
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds Refine-compatible list filtering/sorting behavior (repeated JSON filter= params and “virtual” association/derived fields) and updates the Contacts endpoint plus tests/docs to match, as part of the final pre-launch release.

Changes:

  • Extend order_sort_filter / query filtering to support repeated JSON filters and virtual fields (Thing.contacts, Contact.things) plus multiple virtual sort keys.
  • Update GET /contact to accept filter as list[str] and plumb it through get_db_contacts.
  • Add/expand tests and documentation describing Refine filter semantics and virtual field handling.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/test_thing.py Adds tests for water-well list filtering on contacts and sorting on several virtual fields.
tests/test_contact_filters.py New tests for contacts list filtering/sorting using the things virtual field and multiple repeated filters.
services/query_helper.py Implements virtual filter/sort logic and supports repeated filter params in order_sort_filter.
services/contact_helper.py Updates contacts query helper to accept filters: list[str] and use order_sort_filter accordingly.
docs/refine-json-filters-and-virtual-fields.md New documentation describing repeated filter params, virtual fields, and virtual sorting semantics.
api/contact.py Changes filter query param to list[str] and passes through to get_db_contacts.
README.md Notes Refine repeated JSON filters and links to the new docs.
CLAUDE.md Adds repo guidance about Refine list filters and virtual sorting/filtering behavior.

Comment thread services/query_helper.py

Each call applies a **single** predicate. ``order_sort_filter`` chains
multiple JSON filters with AND semantics.
"""
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

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

_apply_json_filter_clause assumes f is a dict and calls f.keys(). If the filter query param contains valid JSON that is not an object (e.g. a list/string/number), this will raise an AttributeError and bubble up as a 500. Consider validating isinstance(f, dict) (and returning a 422/400 with a clear message) before accessing keys.

Suggested change
"""
"""
if not isinstance(f, dict):
raise HTTPException(
status_code=422,
detail="Filter must be a JSON object",
)

Copilot uses AI. Check for mistakes.
Comment thread services/query_helper.py
Comment on lines +441 to +462
def str_order(expr):
if ord_ == "asc":
return sql.order_by(
nulls_last(expr.asc()),
thing_table.id.asc(),
)
return sql.order_by(
nulls_last(expr.desc()),
thing_table.id.desc(),
)

def num_order(expr):
if ord_ == "asc":
return sql.order_by(
nulls_last(expr.asc()),
thing_table.id.asc(),
)
return sql.order_by(
nulls_last(expr.desc()),
thing_table.id.desc(),
)

Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

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

str_order and num_order are currently identical (same nulls_last(...), thing_table.id tie-break). This duplication makes future changes error-prone (easy to update one and forget the other). Consider collapsing to a single helper (or making the numeric vs string distinction explicit if different behavior is intended).

Copilot uses AI. Check for mistakes.
Comment thread tests/test_thing.py
"/thing/water-well",
params={
"sort": "measuring_point_height",
"order": "desc",
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

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

The test name says _asc, but the request uses order: "desc". This mismatch makes the test intent unclear; either rename the test to _desc or change the request to order: "asc".

Suggested change
"order": "desc",
"order": "asc",

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ce1e533aba

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread services/query_helper.py
Comment on lines +301 to +304
sh.start_date == max_start,
)
.correlate(thing_table)
.scalar_subquery()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Add deterministic tie-breaker to latest-status scalar subquery

The new latest-status sort subquery can return more than one row when a thing has multiple open StatusHistory entries with the same start_date (there is no uniqueness constraint in db/status_history.py to prevent this). In PostgreSQL, using that multi-row result as a scalar expression causes a cardinality error at runtime, so sorting by monitoring_status, well_status, datalogger_suitability_status, or open_status can fail for affected data. Add a deterministic tie-break (for example ORDER BY ... LIMIT 1) in this scalar subquery.

Useful? React with 👍 / 👎.

@jirhiker jirhiker merged commit 2178d0c into production Apr 28, 2026
13 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants