Skip to content

Keep the taxa-list filters when opening a taxon's occurrences, and add Device & Site filters#1347

Open
mihow wants to merge 7 commits into
mainfrom
feat/taxa-occurrence-filters
Open

Keep the taxa-list filters when opening a taxon's occurrences, and add Device & Site filters#1347
mihow wants to merge 7 commits into
mainfrom
feat/taxa-occurrence-filters

Conversation

@mihow

@mihow mihow commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

On the taxa list you can narrow the view to a station and a verification status, but until now clicking a row's occurrence count or verified count threw that context away: it opened the occurrence list showing every occurrence of the taxon, across all stations and both verified and unverified. This PR keeps the filters you had set, so drilling into a taxon lands you on exactly the occurrences you were looking at — the same station, device, site, session, verification status, taxa list, and default-filter settings carry over.

It also adds two filters that were missing from both the taxa and occurrence lists: Device and Site. A deployment records the camera/hardware device it runs and the research site it sits at, so you can now scope either list to one device or one site. Combined with the existing verification filter, this is the small, immediately useful piece of the presence-verification workflow in #1320 — for example, sweeping unverified taxa one station (or one site) at a time — ahead of the larger Example-column work in that issue.

image image

List of Changes

# Change (what it does for the user) How (implementation)
1 Following a taxon's occurrence count, verified count, or child-taxa link now keeps the filters that were active on the taxa list, instead of resetting. This works the same from the taxa table and from the taxon detail panel. Carry-over is keyed by destination: each list's carry contract lives next to its page (FILTERS_TO_OCCURRENCES in pages/occurrences/occurrence-filters.ts, FILTERS_TO_TAXA in pages/species/species-filters.ts), listing the fields that destination honors and can display. The generic useCarryOverFilters hook (useFilters.ts) plus a dependency-free buildCarryOverFilters helper carry whichever of those fields is currently active. Explicit allow-lists (not the whole query string) keep source-only state like sort order, page number, and "show unobserved taxa" out of the occurrence URL, while the child-taxa link still carries "show unobserved taxa" and the tag filters because those are taxa-list filters. The taxa table (species-columns.tsx) and detail panel (species-details.tsx) share the same helper.
2 The taxa filter panel is reordered to match the occurrence list: Taxon, Taxa in list / not in list, Verification status, Show unobserved taxa, Default filters in the main section; Station, Device, Site, and tags in a "More filters" section that opens automatically when one is set. species.tsx now renders two FilterSections mirroring occurrences.tsx.
3 The verified-count link still opens only verified occurrences The verified bubble overrides verified=true after the carried filters.
4 New Device and Site filters on the taxa list Two new EntityPicker filters (device-filter.tsx, site-filter.tsx) pointing at the existing deployments/devices and deployments/sites endpoints, wired into the filter registry and the taxa filter panel.
5 New Device and Site filters on the occurrence list Added to the "More filters" section; the section auto-opens when either is active.
6 Occurrence API accepts deployment__device and deployment__research_site Added to OCCURRENCE_FILTERSET_FIELDS.
7 Taxa API accepts the same two params, restricting which taxa are listed (not just their counts) and returning 404 for an unknown id TaxonViewSet.get_occurrence_filters reads and validates both params.
8 A malformed filter id (e.g. ?deployment__device=abc) now returns 400 on the taxa endpoint instead of a 500 get_occurrence_filters catches the non-integer case and raises a 400, matching what the occurrence endpoint already returns. This also fixes the same pre-existing 500 for deployment, event, and collection, which shared the lookup.
9 Regression coverage Backend: TestDeviceAndSiteFilters (6 tests) pins occurrence filtering, taxa membership restriction, the 404-on-unknown-id behaviour, and the 400-on-non-integer behaviour for both device and site. Frontend: carryOverFilters.test.ts (5 tests) pins the carry-over intersection, that no pagination/sort state leaks, and that "show unobserved taxa" stays a taxa-only carry.

Related Issues

Relates to #1320 (a preliminary step; does not close it).

Detailed Description

The filter labelled "Device" maps to the Device model (the camera/hardware configuration, verbose_name = "Device Configuration"); there is no separate device-type concept in the schema, so the filter is by device. "Site" maps to the deployment's research_site.

Both endpoints use the same query-param keys — deployment__device and deployment__research_site — so the frontend filter wiring is identical for the two lists. On the occurrence endpoint these are plain django-filter exact lookups. On the taxa endpoint they flow through get_occurrence_filters, which already special-cases deployment/event/collection; the new params are validated the same way (a missing Device/Site raises NotFound) and restrict taxa membership through the existing observation-count subquery and aggregation paths, so all dispatch paths (default, verified, event, deployment, collection) inherit them.

No model fields changed, so there is no migration.

Interaction with "show unobserved taxa". On the taxa list, the Device and Site filters restrict which taxa appear only when "show unobserved taxa" is off (the default). With it on, the list shows the full project checklist and the Device/Site/Station/Session filters scope each row's occurrence counts but not membership — the device/site filters ride the same direct_filters path as the existing Station and Session filters, which already behave this way (the membership restriction is intentionally bypassed when include_unobserved is set). This is existing behavior, not introduced here; making those filters also restrict membership in that mode would be a separate change touching all of them.

How to Test the Changes

Automated, backend: python manage.py test ami.main.tests.TestDeviceAndSiteFilters (6 tests) plus the existing TestTaxaVerification / TestTaxonomyViews suites. Frontend: cd ui && yarn test (includes src/utils/carryOverFilters.test.ts).

Manual (verified on a project with more than one device/site): on the taxa list, set the Device filter and the verification status, then click a row's occurrence count — the occurrence list opens with the same Device and verification filters applied and the result count matches the bubble. Repeat with the Site filter.

Deployment Notes

No migration and no configuration change. Deployed to a dev box for review.

Checklist

  • I have tested these changes appropriately.
  • I have added and/or modified relevant tests.
  • I updated relevant documentation or comments.
  • I have verified that this PR follows the project's coding standards.
  • Any dependent changes have already been merged to main.

Summary by CodeRabbit

  • New Features

    • Added device and research site filters to occurrences and taxa views.
    • Preserved applicable filters when navigating between species, taxa, and occurrences pages.
    • Added new filter controls for selecting a device or site.
  • Bug Fixes

    • Malformed filter IDs now return a clear validation error instead of a server error.
    • Filtered links now keep relevant active filters when opening related results.

On the taxa list, clicking a row's occurrence or verified count opened the
occurrence list showing every occurrence of that taxon, ignoring the station
and verification filters that were active on the taxa list. Drilling in now
carries those filters over so the occurrence list stays scoped to the same
selection (station, session, device, site, verification, taxa list, default
filters). The verified-count link still forces verified=true, since that is
what the column represents.

Both the taxa and occurrence lists also gain Device and Site filters, matching
the existing Station filter. A deployment records a device (camera/hardware
configuration) and a research site; users can now scope either list to one
device or one site, e.g. to build a per-site presence matrix. Combined with the
existing verification filter this supports the presence-verification workflow in
issue #1320 (a quick preliminary step ahead of the larger Example-column work).

Backend: deployment__device and deployment__research_site are added to the
occurrence filterset, and the taxa view's get_occurrence_filters reads the same
two params (restricting taxa membership, 404 on an unknown id). No model change,
so no migration.

Refs #1320

Co-Authored-By: Claude <noreply@anthropic.com>
@netlify

netlify Bot commented Jun 24, 2026

Copy link
Copy Markdown

Deploy Preview for antenna-ssec ready!

Name Link
🔨 Latest commit f631c66
🔍 Latest deploy log https://app.netlify.com/projects/antenna-ssec/deploys/6a3c6a732fd91e000859f9f7
😎 Deploy Preview https://deploy-preview-1347--antenna-ssec.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Jun 24, 2026

Copy link
Copy Markdown

Deploy Preview for antenna-preview ready!

Name Link
🔨 Latest commit f631c66
🔍 Latest deploy log https://app.netlify.com/projects/antenna-preview/deploys/6a3c6a715c73580008dc8901
😎 Deploy Preview https://deploy-preview-1347--antenna-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 60 (🔴 down 5 from production)
Accessibility: 81 (🔴 down 8 from production)
Best Practices: 92 (🔴 down 8 from production)
SEO: 92 (no change from production)
PWA: 80 (no change from production)
View the detailed breakdown and full score reports
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@mihow, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 29 minutes and 50 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c0a9bfd1-4a9b-420f-b2aa-c9d865ef251b

📥 Commits

Reviewing files that changed from the base of the PR and between 25d66a7 and f631c66.

📒 Files selected for processing (4)
  • ami/main/api/views.py
  • ui/src/pages/occurrences/occurrences.tsx
  • ui/src/pages/species/species.tsx
  • ui/src/utils/language.ts
📝 Walkthrough

Walkthrough

Adds backend support for deployment device/site occurrence filters, new UI filter options, carry-over filter contracts, and species/species-details navigation that preserves active filter state when linking to taxa and occurrences views.

Changes

Deployment device/site filtering

Layer / File(s) Summary
Backend occurrence filters
ami/main/api/views.py, ami/main/tests.py
Adds deployment device/site query params to occurrence filtering, resolves referenced records before constraining results, and tests device/site scoping plus 400 responses for malformed ids.
Carry-over contracts and helper
ui/src/pages/occurrences/occurrence-filters.ts, ui/src/pages/species/species-filters.ts, ui/src/utils/buildCarryOverFilters.ts, ui/src/utils/carryOverFilters.test.ts
Defines taxa/occurrence carry-over allowlists, adds the shared filter reduction helper, and tests supported-field and source-only exclusion behavior.
Filter catalog and route types
ui/src/utils/useFilters.ts, ui/src/utils/getAppRoute.ts
Adds deployment device/site entries to AVAILABLE_FILTERS, exposes useCarryOverFilters, and extends route filter typing for the new keys.
Deployment filter pickers
ui/src/components/filtering/filter-control.tsx, ui/src/components/filtering/filters/device-filter.tsx, ui/src/components/filtering/filters/site-filter.tsx
Registers DeviceFilter and SiteFilter in the filter control map and wires each picker to the matching API collection.
Species and occurrence wiring
ui/src/pages/species/species.tsx, ui/src/pages/species/species-columns.tsx, ui/src/pages/occurrences/occurrences.tsx
Species list now computes carry-over filters, splits the filter panel into default and more sections, passes carryFilters into the table, and includes deployment device/site in the occurrences panel's active-filter check; species-row occurrence links merge the carried filters into their routes.
Species details links
ui/src/pages/species-details/species-details.tsx
Species details derives taxa and occurrence carry-over filters and merges them into the Child taxa, Occurrences, and Verification links.

Sequence Diagram

sequenceDiagram
  participant User
  participant SpeciesPage
  participant useCarryOverFilters
  participant getAppRoute
  participant TaxonViewSet
  participant Device
  participant Site

  User->>SpeciesPage: open species view
  SpeciesPage->>useCarryOverFilters: derive carry-over filters
  SpeciesPage->>getAppRoute: build taxa and occurrence links
  User->>TaxonViewSet: follow filtered link
  TaxonViewSet->>Device: resolve deployment__device id
  TaxonViewSet->>Site: resolve deployment__research_site id
  TaxonViewSet-->>User: filtered taxa or occurrences
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

backend, PSv2

Suggested reviewers

  • annavik

Poem

(_/)
I hop with filters, light and neat,
Device and site now join the treat.
Through taxa trails I bounce and roam,
Carry-over keys lead rabbits home. 🐰

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: preserving taxa-list filters and adding Device/Site filters.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description matches the repository template and includes a clear summary, changes, issue reference, testing, deployment notes, and checklist.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/taxa-occurrence-filters

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Note

Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

✅ Created PR with unit tests: #1349

get_occurrence_filters validated related-object ids with Model.objects.get(id=...)
inside a try that only caught ObjectDoesNotExist. A non-integer value such as
?deployment__device=abc raised ValueError, which fell through as an unhandled 500.
The occurrence list returns a 400 for the same input (via django-filter), so the
two endpoints disagreed.

Catch the non-integer case and raise a 400 so both endpoints agree. This covers the
new deployment__device / deployment__research_site params and the pre-existing
deployment / event / collection / occurrence ids, which shared the same lookup.

Co-Authored-By: Claude <noreply@anthropic.com>
Comment thread ui/src/pages/species/species-columns.tsx
Comment thread ui/src/utils/getAppRoute.ts Outdated
| 'capture'
| 'collection'
| 'collections'
| 'date_end'

@mihow mihow Jun 24, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I see date params and not_taxa_list_id are being added in addition to the device & site params. are those additional ones missing/broken on live currently?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Claude says: These are type-only entries in the FilterType union — adding a key here changes no runtime behavior, it just lets a link reference that param in a URL. None of them are missing or broken on live: date_start/date_end are the existing date-range filter in the occurrence "More filters" section, and not_taxa_list_id is "Exclude taxa from list" — all already shipped and working.

They showed up here because I extended the union while wiring the filter carry-over, but they turned out unnecessary: carry-over spreads a string-keyed record into getAppRoute({ filters }), which is already assignable without per-field union entries — only fields used as explicit literal keys (e.g. taxon, verified) need to be in the union. So I've trimmed date_start / date_end / not_taxa_list_id in 25d66a7. deployment__device / deployment__research_site stay, since those are the new filter params this PR introduces.

mihow and others added 4 commits June 24, 2026 13:15
… on the detail panel

The taxa list and the taxon detail panel both link into the occurrence list from an
occurrence count and a verified count. The list rows carried the active taxa-list
filters (station, device, site, verification, ...) into that link, but the detail
panel did not — it reset to every occurrence of the taxon. The two now behave the
same.

The carry-over field list and the logic that builds the filter object move into
useFilters.ts as TAXA_OCCURRENCE_CARRY_OVER_FIELDS, buildCarryOverFilters, and the
useCarryOverFilters hook, so there is a single source of truth instead of a constant
living in one page component. The taxa table (via species.tsx) and the detail panel
both consume the hook.

No behavior change for the taxa table; the detail panel's two links now carry the
same filters.

Co-Authored-By: Claude <noreply@anthropic.com>
…ilters into the child-taxa link

Reorder the taxa-list filter panel to mirror the occurrence list: the primary section
now leads with Taxon, then Taxa in list / not in list, Verification status, Show
unobserved taxa, and Default filters; Station, Device, Site, and the tag filters move
into a "More filters" section that opens automatically when one of them is set. The two
list views now read the same way.

The taxon detail panel's "Child taxa" link also carries the active filters now, so
stepping from a taxon to its children keeps the same station / device / site /
verification scope, matching the occurrence and verified links next to it.

Rename TAXA_OCCURRENCE_CARRY_OVER_FIELDS to CARRY_OVER_FILTER_FIELDS since the same set
now feeds both the occurrence links and the child-taxa link.

Co-Authored-By: Claude <noreply@anthropic.com>
…S / FILTERS_TO_TAXA)

Replace the single carry-over field list with two destination-keyed sets in useFilters.ts:
FILTERS_TO_OCCURRENCES and FILTERS_TO_TAXA, each listing the filter fields its destination
list understands. Carry-over is now the intersection of the current view's active filters
with the destination's set, so the source is implicit and any view that links to a
destination reuses the same set — the behavior is consistent no matter where the link is.

This also corrects two cases for the taxon detail panel: "show unobserved taxa" and the
tag filters now carry along the child-taxa link (they are taxa-list filters) but still do
not leak into the occurrence list (which does not support them).

buildCarryOverFilters and the useCarryOverFilters hook take the destination field set as
an argument. Only the taxa views consume them so far; the sets are ready for other views'
links to reuse.

Co-Authored-By: Claude <noreply@anthropic.com>
Each list's carry-over contract now lives next to that list's page:
FILTERS_TO_OCCURRENCES in pages/occurrences/occurrence-filters.ts and FILTERS_TO_TAXA in
pages/species/species-filters.ts, each documenting the two bounds it must satisfy (the
backend honors the field, and the panel can display it so a carried filter is visible and
clearable). The carry-over helper stays generic and destination-agnostic.

Also:
- Extract the pure buildCarryOverFilters into its own dependency-free module so it can be
  unit-tested without loading the filter registry; the useCarryOverFilters hook keeps it
  in useFilters.ts.
- Add carryOverFilters.test.ts: the intersection behavior, no pagination/sort leak, and
  the "show unobserved taxa" taxa-only invariant.
- Drop date_start / date_end / not_taxa_list_id from getAppRoute's FilterType union — they
  were type-only additions that are not used as explicit link params (carry-over spreads a
  string-keyed record, which does not need them in the union).

Co-Authored-By: Claude <noreply@anthropic.com>
@mihow mihow marked this pull request as ready for review June 24, 2026 23:08
Copilot AI review requested due to automatic review settings June 24, 2026 23:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR improves taxa/occurrence list navigation by preserving the user’s active filter context when drilling down from taxa → occurrences (and taxa child links), and adds Device + Site filtering across both lists with backend support and regression tests.

Changes:

  • Add Device (deployment__device) and Site (deployment__research_site) filters to taxa and occurrence list UIs, plus carry-over support between list routes.
  • Introduce explicit “carry-over contracts” (FILTERS_TO_OCCURRENCES, FILTERS_TO_TAXA) and a shared buildCarryOverFilters helper/hook to avoid leaking source-only state (page/sort) into destination URLs.
  • Extend the occurrence/taxa backend filtering to accept the new parameters and add API regression coverage (including 400-on-non-integer behavior).

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
ui/src/utils/useFilters.ts Adds Device/Site to the filter registry; introduces useCarryOverFilters hook.
ui/src/utils/getAppRoute.ts Extends route filter typing to include new query params.
ui/src/utils/carryOverFilters.test.ts Unit tests for buildCarryOverFilters and carry-over contracts.
ui/src/utils/buildCarryOverFilters.ts New pure helper for carrying active filters into destination URLs.
ui/src/pages/species/species.tsx Reorders taxa filters into sections; wires carry-over into taxa table links.
ui/src/pages/species/species-filters.ts Defines taxa-list carry-over contract.
ui/src/pages/species/species-columns.tsx Applies carried filters when linking from taxa rows to occurrences.
ui/src/pages/species-details/species-details.tsx Applies carry-over filters for “view all” / child taxa / verified links.
ui/src/pages/occurrences/occurrences.tsx Adds Device/Site controls to “More filters” and auto-open logic.
ui/src/pages/occurrences/occurrence-filters.ts Defines occurrence-list carry-over contract.
ui/src/components/filtering/filters/device-filter.tsx New Device entity-picker filter control.
ui/src/components/filtering/filters/site-filter.tsx New Site entity-picker filter control.
ui/src/components/filtering/filter-control.tsx Registers device/site filter components.
ami/main/tests.py Adds API tests validating device/site filtering and error semantics.
ami/main/api/views.py Adds backend support for new filter params + 400-on-non-integer handling.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ami/main/api/views.py
Comment on lines 1765 to +1777
if occurrence_id:
Occurrence.objects.get(id=occurrence_id)
# This query does not need the same filtering as the others
filters &= models.Q(**{field("id"): occurrence_id})
if deployment_id:
Deployment.objects.get(id=deployment_id)
filters &= models.Q(**{field("deployment"): deployment_id})
if device_id:
Device.objects.get(id=device_id)
filters &= models.Q(**{field("deployment__device"): device_id})
if site_id:
Site.objects.get(id=site_id)
filters &= models.Q(**{field("deployment__research_site"): site_id})

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
ui/src/utils/useFilters.ts (2)

81-88: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Route the Device/Site labels through the translation layer.

Per the project guideline, user-facing strings should use translate(STRING.KEY) with keys added to the STRING enum and ENGLISH_STRINGS. The neighboring 'Station' entry predates this, but the new entries should ideally follow the rule.

As per coding guidelines: "All user-facing strings must go through the translation layer using translate(STRING.KEY)."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/src/utils/useFilters.ts` around lines 81 - 88, The Device and Site filter
labels are hardcoded user-facing strings and should be routed through the
translation layer like the other translated labels. Update the filter
definitions in useFilters so they use translate(STRING.KEY) instead of literal
text, and add the corresponding keys to STRING and ENGLISH_STRINGS to match the
existing localization pattern.

Source: Coding guidelines


292-300: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

useMemo here is effectively a no-op.

useFilters() returns a freshly-built filters array on every render, so the [filters, fields] dependency changes each render and the memo recomputes every time (also returning a new object identity). The computation is cheap so this is harmless today, but if a consumer feeds the returned record into effect deps or query keys it could trigger extra work. Memoizing on a stable serialization (or accepting the recompute and dropping useMemo) would be clearer.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/src/utils/useFilters.ts` around lines 292 - 300, The memoization in
useCarryOverFilters is ineffective because useFilters returns a new filters
array every render, so useMemo always recomputes and still produces a new object
identity. Either remove useMemo and return buildCarryOverFilters(filters,
fields) directly, or switch the memoization to a stable dependency strategy if
you truly need caching; keep the fix centered on useCarryOverFilters,
useFilters, and buildCarryOverFilters so the behavior is explicit.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ami/main/api/views.py`:
- Around line 1787-1791: The exception handling in the API view’s
ValueError/TypeError branch should explicitly preserve the original traceback
when raising api_exceptions.ValidationError. Update the re-raise in this
filter-id validation path to use exception chaining from the caught error so the
original failure context is not lost and the code stays compliant with the
project’s flake8 B904 rule. Locate the try/except block in the view logic that
validates integer filter ids and adjust the ValidationError raise accordingly.

In `@ui/src/pages/species/species.tsx`:
- Around line 94-95: The FilterSection title in species.tsx is hardcoded
user-facing copy and must be moved through the translation layer. Add a shared
STRING key and ENGLISH_STRINGS entry for this label, then update the
FilterSection usage in species.tsx to call translate(STRING.KEY) instead of the
literal text; also update occurrences.tsx to reuse the same shared key if it has
the same “More filters” label.

---

Nitpick comments:
In `@ui/src/utils/useFilters.ts`:
- Around line 81-88: The Device and Site filter labels are hardcoded user-facing
strings and should be routed through the translation layer like the other
translated labels. Update the filter definitions in useFilters so they use
translate(STRING.KEY) instead of literal text, and add the corresponding keys to
STRING and ENGLISH_STRINGS to match the existing localization pattern.
- Around line 292-300: The memoization in useCarryOverFilters is ineffective
because useFilters returns a new filters array every render, so useMemo always
recomputes and still produces a new object identity. Either remove useMemo and
return buildCarryOverFilters(filters, fields) directly, or switch the
memoization to a stable dependency strategy if you truly need caching; keep the
fix centered on useCarryOverFilters, useFilters, and buildCarryOverFilters so
the behavior is explicit.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c2349cd0-91fa-46f1-8001-73cc1a5937a4

📥 Commits

Reviewing files that changed from the base of the PR and between bfacd43 and 25d66a7.

📒 Files selected for processing (15)
  • ami/main/api/views.py
  • ami/main/tests.py
  • ui/src/components/filtering/filter-control.tsx
  • ui/src/components/filtering/filters/device-filter.tsx
  • ui/src/components/filtering/filters/site-filter.tsx
  • ui/src/pages/occurrences/occurrence-filters.ts
  • ui/src/pages/occurrences/occurrences.tsx
  • ui/src/pages/species-details/species-details.tsx
  • ui/src/pages/species/species-columns.tsx
  • ui/src/pages/species/species-filters.ts
  • ui/src/pages/species/species.tsx
  • ui/src/utils/buildCarryOverFilters.ts
  • ui/src/utils/carryOverFilters.test.ts
  • ui/src/utils/getAppRoute.ts
  • ui/src/utils/useFilters.ts

Comment thread ami/main/api/views.py Outdated
Comment thread ui/src/pages/species/species.tsx Outdated
@mihow mihow requested a review from annavik June 24, 2026 23:25
… label through i18n

Two review fixes:
- get_occurrence_filters now chains its re-raised exceptions (raise ... from e) for both
  the NotFound and the ValidationError paths, so the original ObjectDoesNotExist / ValueError
  traceback is preserved for debugging. Matches the existing pattern in ami/base/fields.py.
- The "More filters" section title now goes through translate(STRING.MORE_FILTERS) instead of
  a hardcoded literal, shared by the taxa and occurrence filter panels.

Co-Authored-By: Claude <noreply@anthropic.com>
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.

2 participants