Skip to content

Conversation

@mohamedelabbas1996
Copy link
Contributor

@mohamedelabbas1996 mohamedelabbas1996 commented Nov 11, 2025

Summary

This PR continues the default filters follow-up work from #1029 by adding environment-variable–based default include and exclude taxa to projects.
It also ensures that all cached count fields (detections, occurrences, and taxa counts) consistently respect the project’s default filters across the Deployment, Event, and SourceImage models.

Finally, this PR fixes an inconsistency in the Taxon Detail view, where the project’s default score threshold was not being applied causing the direct occurrence count on the detail page to diverge from the count shown in the Taxon List view.

List of Changes

  • Added DEFAULT_INCLUDE_TAXA and DEFAULT_EXCLUDE_TAXA environment variables and updated get_or_create_default_project() to attach these default taxa to newly created default projects.

  • Added signals to refresh cached counts when:

    • project's default_filters_score_threshold changes
    • include taxa are added/removed/cleared
    • exclude taxa are added/removed/cleared
  • Applied project default filters to detection counting in:

    • Deployment.get_detections_count()

    • Event.get_detections_count()

    • SourceImage.get_detections_count()

  • Fixed get_taxa_observed() to allow separate application of score vs taxa filters

Related Issues

#994

Detailed Description

This PR completes the default taxa filter follow-up tasks by addressing three issues:

1. Environment-based default include/exclude taxa

Default include/exclude taxa can now be provided via environment variables (DEFAULT_INCLUDE_TAXA, DEFAULT_EXCLUDE_TAXA).
These are automatically assigned to default projects during creation.

2. Cached count updates more efficiently

Cached fields for occurrences, detections, and taxa counts in Deployment, Event, and SourceImage are now updated only when the project’s default filter values change.
Signals detect changes to: default score threshold, default include taxa and default exclude taxa and only then trigger recalculation of cached counts.

3. Taxon Detail view filter consistency fix

The Taxon Detail view previously skipped the default score threshold, causing mismatched occurrence counts compared to the Taxon List view.
This has now been corrected, ensuring consistent filtering across both views.

How to Test the Changes

  1. Set environment variables for default taxa
DEFAULT_INCLUDE_TAXA="TaxonA,TaxonB"
DEFAULT_EXCLUDE_TAXA="TaxonC"

call get_or_create_default_project(user)
Confirm include/exclude taxa are assigned
2.
a. Update the project’s default score threshold (default_filters_score_threshold) from the Project admin page antenna ui.
Verify that detections, occurrences, and taxa counts update consistently in:

  • Session (Event) list view

  • Session detail view

  • Stations (Deployment) list view

b. Modify the project’s default include or exclude taxa (add/remove taxa).

Again, verify that all cached counts update correctly across:

  • Event list & detail views

  • Deployment (stations) list view

  1. Visit Taxon List and Taxon Detail views and confirm direct occurrence counts now match.

Screenshots

N/A

Deployment Notes

Make sure to set the environment variables for default project taxa:
DEFAULT_INCLUDE_TAXA
DEFAULT_EXCLUDE_TAXA

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 configurable default taxa filtering through environment settings for include/exclude taxa lists
    • Detection counts now consistently apply project-specific filters across deployments, events, and source images
  • Improvements

    • Enhanced filter control with configurable options for taxa and score-based filtering
    • Implemented automatic cache refresh when project filters or taxa selections change

@netlify
Copy link

netlify bot commented Nov 11, 2025

Deploy Preview for antenna-preview ready!

Name Link
🔨 Latest commit 852a17a
🔍 Latest deploy log https://app.netlify.com/projects/antenna-preview/deploys/691dd166f367200008d5fa8d
😎 Deploy Preview https://deploy-preview-1045--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: 62 (🟢 up 24 from production)
Accessibility: 80 (no change from production)
Best Practices: 100 (no change from production)
SEO: 92 (no change from production)
PWA: 80 (no change from production)
View the detailed breakdown and full score reports

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 11, 2025

Walkthrough

This PR introduces configurable default filter application throughout the codebase. It adds environment-backed taxa inclusion/exclusion settings, makes default filters conditionally applicable in viewsets and query builders, implements per-model detection count methods that respect project filters, and establishes signal-driven cache invalidation when filters change.

Changes

Cohort / File(s) Summary
Configuration & Settings
config/settings/base.py
Added two new environment-backed taxa list settings: DEFAULT_INCLUDE_TAXA and DEFAULT_EXCLUDE_TAXA, both defaulting to empty lists.
Model Default Filters
ami/main/models.py
Added get_project_default_filters() function to load default taxa from settings. Updated get_or_create_default_project() to apply loaded taxa to new projects. Added three new methods for filtered detection counting: Deployment.get_detections_count(), Event.get_detections_count(), and SourceImage.get_detections_count(). Updated update_calculated_fields methods in Deployment and Event to use the new filtered count methods.
Query Filters
ami/main/models_future/filters.py
Extended build_occurrence_default_filters_q() signature with apply_default_score_filter and apply_default_taxa_filter boolean parameters (both defaulting to True). Made score threshold and taxa filtering conditional on these flags.
API Viewset
ami/main/api/views.py
Updated TaxonViewSet.get_taxa_observed() signature to accept apply_default_score_filter and apply_default_taxa_filter parameters. Modified call in get_queryset for retrieve action to explicitly pass apply_default_score_filter=True, apply_default_taxa_filter=False. Updated internal filter construction to pass flags to build_occurrence_default_filters_q(). Removed conditional logic that gated default filter application; now unconditionally applies constructed default filters.
Signal Handlers
ami/main/signals.py
Added refresh_cached_counts_for_project() helper function. Added four signal handlers: cache_old_threshold() (pre_save), threshold_updated() (post_save), include_taxa_updated() (m2m_changed), and exclude_taxa_updated() (m2m_changed) to detect changes and refresh project-related cached counts.

Sequence Diagram

sequenceDiagram
    actor Client
    participant ViewSet as TaxonViewSet
    participant Filters as build_occurrence_<br/>default_filters_q()
    participant Models as Deployment/<br/>Event Model
    participant Signals as Signal Handlers
    
    Client->>ViewSet: retrieve(project_id)
    ViewSet->>ViewSet: get_queryset()
    ViewSet->>ViewSet: get_taxa_observed(apply_default_score_filter=True,<br/>apply_default_taxa_filter=False)
    ViewSet->>Filters: build_occurrence_default_filters_q(<br/>apply_default_score_filter=True,<br/>apply_default_taxa_filter=False)
    Note over Filters: Apply score filter<br/>Skip taxa filter
    Filters-->>ViewSet: filtered Q object
    ViewSet-->>Client: taxa queryset
    
    Note over Signals: Separate Flow: When Filters Change
    Client->>Models: update default_filters_score_threshold
    Models->>Signals: pre_save signal (cache_old_threshold)
    Signals->>Signals: store old value
    Models->>Signals: post_save signal (threshold_updated)
    Signals->>Signals: compare old vs new
    alt threshold changed
        Signals->>Models: refresh_cached_counts_for_project()
        Models->>Models: update_related_calculated_fields()
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Areas requiring extra attention:
    • The signal-driven cache invalidation logic in ami/main/signals.py and corresponding cache refresh calls in ami/main/models.py to ensure all affected cached counts are properly invalidated
    • The conditional flag logic in build_occurrence_default_filters_q() to verify correct filter application across different code paths
    • The updated call site in TaxonViewSet.get_queryset() where apply_default_taxa_filter=False explicitly bypasses taxa filtering for the retrieve action—verify this behavior is intentional
    • Integration between environment settings (DEFAULT_INCLUDE_TAXA, DEFAULT_EXCLUDE_TAXA) and the get_project_default_filters() initialization

Possibly related issues

  • Follow-up fixes to default filters #994 — Implements the follow-up fixes to default filters by adding configurable filter flags, environment-backed taxa settings, signal-driven cache invalidation, and per-model detection count methods with filter awareness.

Possibly related PRs

Suggested reviewers

  • mihow

Poem

🐰 Hops with glee through filter paths,
Signals ring when settings change,
Cached counts refresh at last,
No more stale detections—how strange!
Default taxa dance in rows,
Fresh as lettuce, swift as prose. 🥬

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding default taxa to projects and handling cached counts with project default filters.
Docstring Coverage ✅ Passed Docstring coverage is 86.36% which is sufficient. The required threshold is 80.00%.
Description check ✅ Passed The pull request description comprehensively covers all required template sections including summary, list of changes, related issues, detailed description, testing instructions, deployment notes, and a completed checklist.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/default-filters-followup-default-taxa-and-cache

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


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 and usage tips.

@mohamedelabbas1996 mohamedelabbas1996 self-assigned this Nov 11, 2025
@mohamedelabbas1996 mohamedelabbas1996 marked this pull request as ready for review November 18, 2025 14:57
@mohamedelabbas1996 mohamedelabbas1996 changed the title [Draft] Default taxa filter follow-up: handle cached counts and add default taxa to projects Default taxa filter follow-up: handle cached counts and add default taxa to projects Nov 18, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (5)
ami/main/signals.py (1)

8-11: Default-filter signals correctly trigger cache refresh; consider linter and perf tweaks

The new refresh_cached_counts_for_project, threshold-change, and include/exclude-taxa m2m signals are wired correctly and line up with Project.update_related_calculated_fields(); behavior looks sound.

Two small follow-ups:

  • To satisfy Ruff’s ARG001 without changing behavior, you can rename unused args to underscore variants, e.g.:
    def cache_old_threshold(_sender, instance, **_kwargs): ...
    def threshold_updated(_sender, instance, **_kwargs): ...
    def include_taxa_updated(_sender, instance: Project, action, **_kwargs): ...
    def exclude_taxa_updated(_sender, instance: Project, action, **_kwargs): ...
  • project.update_related_calculated_fields() can be expensive for large projects (iterates all events/deployments and saves). If this becomes a bottleneck, consider deferring to a background job or at least wrapping with some timing/metrics to monitor impact.

Also applies to: 133-191

ami/main/models_future/filters.py (1)

128-135: New per-component default-filter flags are well structured; docstring could mention them

The addition of apply_default_score_filter and apply_default_taxa_filter preserves existing behavior when left at their defaults and cleanly allows callers to toggle score vs taxa components independently. Early returns for project is None and apply_defaults=false keep existing call sites safe.

Consider updating the function docstring/Examples section to briefly describe these two flags and show a usage where only score or only taxa defaults are applied, so future readers don’t have to infer the new behavior from the implementation.

Also applies to: 191-213

ami/main/models.py (2)

153-162: Default taxa application to new “Scratch Project” is straightforward; consider logging unknown taxa

get_project_default_filters() and the updated get_or_create_default_project() cleanly wire the env-configured taxa into a new default project via the include/exclude M2M fields; behavior on existing projects is unchanged.

If you want to make ops misconfigurations easier to detect, a small enhancement would be to log any names in settings.DEFAULT_INCLUDE_TAXA/DEFAULT_EXCLUDE_TAXA that don’t resolve to a Taxon, e.g. by comparing the configured name lists to the Taxon.objects.filter(...).values_list("name", flat=True) results before returning.

Also applies to: 164-186


704-715: Detection-count helpers correctly respect project defaults; align bulk updater semantics and return types

The new get_detections_count methods on Deployment, Event, and SourceImage correctly centralize counting through build_occurrence_default_filters_q, so cached detections_count now reflects project default filters when a project is present, and falls back to unfiltered counts when not.

Two follow-ups to consider:

  • The type hints for Deployment.get_detections_count and Event.get_detections_count are -> int | None, but the implementations always return an int (count()), even when there is no project. Tightening these to -> int would better match actual behavior.
  • SourceImage.update_calculated_fields() now sets detections_count using the default-filtered helper, while update_detection_counts() still bulk-updates detections_count as the raw number of detections (no default filters). Because Event.timeline() uses update_detection_counts(qs, null_only=True) before aggregating detections_count, older rows (with null counts) will be populated with unfiltered counts, while newer rows updated via get_detections_count() will be filtered. If the intent is that detections_count everywhere represents the default-filtered value, it would be worth updating update_detection_counts() to apply the same filtering (or documenting that it intentionally uses raw counts and only for legacy backfill).

Also applies to: 919-922, 1085-1095, 1799-1813, 1970-1993

ami/main/api/views.py (1)

1478-1483: Taxon detail/list default-filter handling is consistent; consider documenting new flags

The revised get_taxa_observed flow looks good:

  • List view: still filters taxa by filter_by_project_default_taxa(...) and calls get_taxa_observed with default flags, so both score and taxa defaults apply (unless apply_defaults=false is passed, in which case build_occurrence_default_filters_q returns Q() and base_filter is unchanged).
  • Detail view: calls get_taxa_observed(..., apply_default_score_filter=True, apply_default_taxa_filter=False), so the project’s score threshold now applies while taxa include/exclude are not re-applied inside the occurrence subqueries. That matches the intent of aligning detail counts with list behavior for included taxa while still allowing detail on excluded taxa.

The use of build_occurrence_default_filters_q with the new flags keeps the semantics consistent with other parts of the codebase. As a small improvement, you might extend the get_taxa_observed docstring to mention the two flags and how they’re used for list vs detail views, since the signature is now doing a bit more work than the current docstring reflects.

Also applies to: 1499-1505, 1523-1529, 1537-1538

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 562f734 and 7d99ebd.

📒 Files selected for processing (5)
  • ami/main/api/views.py (3 hunks)
  • ami/main/models.py (6 hunks)
  • ami/main/models_future/filters.py (2 hunks)
  • ami/main/signals.py (2 hunks)
  • config/settings/base.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
ami/main/models_future/filters.py (1)
ami/utils/requests.py (1)
  • get_default_classification_threshold (124-145)
ami/main/api/views.py (1)
ami/main/models_future/filters.py (1)
  • build_occurrence_default_filters_q (128-214)
ami/main/models.py (1)
ami/main/models_future/filters.py (1)
  • build_occurrence_default_filters_q (128-214)
ami/main/signals.py (1)
ami/main/models.py (2)
  • Project (249-489)
  • update_related_calculated_fields (331-341)
🪛 Ruff (0.14.5)
ami/main/signals.py

142-142: Unused function argument: sender

(ARG001)


142-142: Unused function argument: kwargs

(ARG001)


163-163: Unused function argument: sender

(ARG001)


163-163: Unused function argument: kwargs

(ARG001)


179-179: Unused function argument: sender

(ARG001)


179-179: Unused function argument: kwargs

(ARG001)


187-187: Unused function argument: sender

(ARG001)


187-187: Unused function argument: kwargs

(ARG001)

🔇 Additional comments (1)
config/settings/base.py (1)

451-453: Env-configured default taxa lists look correct

Reading DEFAULT_INCLUDE_TAXA/DEFAULT_EXCLUDE_TAXA via env.list with list defaults is consistent with the existing settings usage; no issues from a settings perspective.

@mihow mihow self-assigned this Nov 24, 2025
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