Skip to content

Conversation

@mohamedelabbas1996
Copy link
Contributor

@mohamedelabbas1996 mohamedelabbas1996 commented Mar 13, 2025

Summary

This PR introduces a Taxa List filter for occurrences and taxa views, enabling filtering based on taxons in a Taxa List . The filter ensures that Occurrences and Taxa can be filtered by their presence in a Taxa List or if their ancestors belong to a specified Taxa List. Additionally, Taxa Lists can be managed from the admin dashboard, allowing users to Create new Taxa Lists, Add taxa to a Taxa List and Link Taxa Lists to projects.

List of Changes

  • Added OccurrenceTaxaListFilter: Enables filtering Occurrence objects where determination is in a TaxaList or has an ancestor in the list.
  • Added TaxonTaxaListFilter: Enables filtering Taxon objects where the taxon is in a TaxaList or has an ancestor in the list.
  • Added Taxa List filter UI for Occurrences and Taxa Pages:
    Users can now filter occurrences and taxa by selecting a Taxa List from a dropdown.

Related Issues

#745

How to Test the Changes

  1. Verify Taxa List Filtering in Occurrences and Taxa Pages
  2. Navigate to the Occurrences Page.
  3. Locate the Taxa List filter dropdown.
  4. Select a TaxaList.
  5. Verify that:
    Only Occurrences with a determination in the selected TaxaList or its ancestors are displayed.
    Changing the TaxaList filter updates the results accordingly.
  6. Repeat the same steps on the Taxa Page.

Screenshots

image image image

Deployment Notes

No database migrations required.

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.

@mohamedelabbas1996 mohamedelabbas1996 linked an issue Mar 13, 2025 that may be closed by this pull request
@netlify
Copy link

netlify bot commented Mar 13, 2025

Deploy Preview for antenna-preview ready!

Name Link
🔨 Latest commit 51c7b05
🔍 Latest deploy log https://app.netlify.com/sites/antenna-preview/deploys/67dd7916c20a3a00089d111f
😎 Deploy Preview https://deploy-preview-777--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: 71 (🔴 down 4 from production)
Accessibility: 89 (no change from production)
Best Practices: 92 (no change from production)
SEO: 100 (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 site configuration.

@mohamedelabbas1996 mohamedelabbas1996 self-assigned this Mar 13, 2025
@mohamedelabbas1996 mohamedelabbas1996 marked this pull request as draft March 13, 2025 14:28
@mohamedelabbas1996 mohamedelabbas1996 changed the title Species of Interest TaxaList Filter [Draft] Species of Interest TaxaList Filter Mar 13, 2025
logger.debug(f"Filtering by taxalist {taxalist_id} taxa_ids {taxa}")

# filter by the exact determination
query_filter = Q(determination__in=taxa)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you use a sub query instead? If you generate the list of taxa in python first and then pass it back, it may be too long. Some regional taxa lists will have over a few thousand items.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Example:

antenna/ami/main/models.py

Lines 2343 to 2346 in 1adb99d

Classification.objects.filter(detection__occurrence=self)
.filter(
score__in=models.Subquery(
Classification.objects.filter(detection__occurrence=self)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Actually I don't think a subquery is possible with our parents_json field. We can come back to that later. Also the first filter can occur on the Taxa view. Instead of the Occurrences view. Ideally both, but Taxa is the most important for the first demo.

router.register(r"detections", views.DetectionViewSet)
router.register(r"occurrences", views.OccurrenceViewSet)
router.register(r"taxa", views.TaxonViewSet)
router.register(r"taxalists", views.TaxaListViewSet)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Will you try mounting under /taxa? /taxa/lists? Like the ml prefix. We will eventually move all taxonomy related things into a taxa app. If it causes a headache then no worries.



class TaxaListSerializer(serializers.ModelSerializer):
taxa = serializers.PrimaryKeyRelatedField(queryset=Taxon.objects.all(), many=True)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think the TaxaListSerializer needs to return the actual taxa in the list. Try adding a link to the filtered taxa view instead like this example in SourceImageCollectionSerializer which links to a filtered SourceImages view:

def get_source_images(self, obj) -> str:
"""
Return URL to the captures endpoint filtered by this collection.
"""
return reverse_with_params(
"sourceimage-list",
request=self.context.get("request"),
params={"collections": obj.pk},
)

Perhaps in the detail view, but I don't think the taxa list is necessary in either right now and it will be long!

query_param = "taxalist"

def filter_queryset(self, request, queryset, view):
taxalist_id = request.query_params.get(self.query_param)
Copy link
Collaborator

@mihow mihow Mar 14, 2025

Choose a reason for hiding this comment

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

I'm glad you are using the IntegerField here for validating the query param! Here is how we are doing it in other filters. You don't need to check for the taxalist_id first if you set required to False.

def filter_queryset(self, request, queryset, view):
collection_id = IntegerField(required=False).clean(request.query_params.get(self.query_param))
if collection_id:
# Here the queryset is the Occurrence queryset
return queryset.filter(detections__source_image__collections=collection_id)

@mohamedelabbas1996 mohamedelabbas1996 changed the title [Draft] Species of Interest TaxaList Filter Species of Interest TaxaList Filter Mar 15, 2025
@mohamedelabbas1996 mohamedelabbas1996 marked this pull request as ready for review March 15, 2025 00:59
"""
Return URL to the taxa endpoint filtered by this taxalist.
"""
return reverse_with_params(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks for finding this!

return super().list(request, *args, **kwargs)


class TaxonTaxaListFilter(filters.BaseFilterBackend):
Copy link
Collaborator

@mihow mihow Mar 17, 2025

Choose a reason for hiding this comment

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

OccurrenceTaxaListFilter and TaxonTaxaListFilter are almost identical. If we can't reuse the filter, will you at least update the docstring in the filter classes to make it clear they are doing the same type of recursive search? Something like "Query for all taxa in the requested TaxaList, and any of their children that are in the the TaxaList, recursively"

Thank you!

@mihow
Copy link
Collaborator

mihow commented Mar 17, 2025

It works!
image

@mihow mihow requested a review from annavik March 17, 2025 21:59
@mihow
Copy link
Collaborator

mihow commented Mar 17, 2025

@annavik will you review the frontend changes on this one?

We will likely have a Taxonomy section soon in the new Project sub-menus as well!

@mihow
Copy link
Collaborator

mihow commented Mar 19, 2025

@annavik I pushed one simple method of hiding the Taxa List filter if there are no lists configured for the current project. I couldn't see how to add a showFilter() method to the TaxaList type because we don't have the current project in that context.

If we do more of these, we could add a hideIfDisabled attribute to the FormControl component? Because the Taxa List filter component has already checked the data and disabled the form field.

Anyway, let us know what you think of the other changes. We can improve the lists feature over time.

Copy link
Member

@annavik annavik left a comment

Choose a reason for hiding this comment

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

So glad you could include some FE updates @mohamedelabbas1996 ! 👏

Just let me know if you want to have a chat about anything. The multi fetch problem I want to think about a bit more, I think no show stopper though!

@annavik
Copy link
Member

annavik commented Mar 21, 2025

Thanks for the fixes @mohamedelabbas1996, this looks good to me now! :) I pushed an update to make it possible to pass custom data to filter controls. Thanks to that we can now skip the multi fetching. See commit 7cd7738 if you are curious about the solution.

I will approve this now!

@annavik
Copy link
Member

annavik commented Mar 21, 2025

One detail: do you think we should exclude "id" from the filter key everywhere, to follow same pattern we use for other filters? No blocker though.

Screenshot 2025-03-21 at 15 42 18

(Not just here, but overall)

@mohamedelabbas1996
Copy link
Contributor Author

mohamedelabbas1996 commented Mar 24, 2025

One detail: do you think we should exclude "id" from the filter key everywhere, to follow same pattern we use for other filters? No blocker though.

Thanks for pointing that out, Anna! I was actually following the pattern discussed in issue #660, where we're aiming to use object_id to refer to the actual ID and object for the object itself (e.g., project_id vs. project). Not sure if we’re still aligning with that convention, happy to adjust if needed!

@annavik
Copy link
Member

annavik commented Mar 24, 2025

One detail: do you think we should exclude "id" from the filter key everywhere, to follow same pattern we use for other filters? No blocker though.

Thanks for pointing that out, Anna! I was actually following the pattern discussed in issue #660, where we're aiming to use object_id to refer to the actual ID and object for the object itself (e.g., project_id vs. project). Not sure if we’re still aligning with that convention, happy to adjust if needed!

I see, I understand it's a bit confusing since we have been a bit inconsistent here! I do like having id as part of the key, I think it's more clear :) I also like to keep the filter feature consistent though...

Let's keep this filter key as is, if the plan is to update other filter keys in a near future? :) @mihow any thoughts?

@mihow
Copy link
Collaborator

mihow commented Mar 25, 2025

@annavik I'm glad you noticed this detail! And I am glad you like the _id version. This will help distinguish which properties are just IDs (or specific attributes) and which ones are nested json representations of related objects. For example:

project: { deployment_id: 2}
project: { deployment: {name: "Mothra", id: 2} }

I'm sorry we haven't updated them all at once! project => project_id was the first full-project change. And now taxa_list_id is the first new attribute it's being applied to.

@mihow
Copy link
Collaborator

mihow commented Mar 25, 2025

Thanks for the fixes @mohamedelabbas1996, this looks good to me now! :) I pushed an update to make it possible to pass custom data to filter controls. Thanks to that we can now skip the multi fetching. See commit 7cd7738 if you are curious about the solution.

I will approve this now!

Thanks for sharing the fix. It seems like that approach opens the door for a lot of flexibility.

@mihow mihow merged commit 815f952 into main Mar 25, 2025
6 checks passed
@mihow mihow deleted the feat/species-of-interest-filter branch March 25, 2025 01:34
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.

Add filtering options for Taxa Lists

4 participants