Conversation
* Initial plan * Add sorting, timestamps and taxa_count to TaxaList API Co-Authored-By: Claude <noreply@anthropic.com> Co-authored-by: mihow <158175+mihow@users.noreply.github.com> * Optimize taxa_count with query annotation Co-Authored-By: Claude <noreply@anthropic.com> Co-authored-by: mihow <158175+mihow@users.noreply.github.com> * Format lists with trailing commas per black style Co-Authored-By: Claude <noreply@anthropic.com> Co-authored-by: mihow <158175+mihow@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mihow <158175+mihow@users.noreply.github.com> Co-authored-by: Anna Viklund <annamariaviklund@gmail.com>
✅ Deploy Preview for antenna-preview ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for antenna-ssec ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds end-to-end taxa-list management: backend serializers, viewsets, permissions, nested routes, and tests; frontend routes, pages, hooks, components, models, and translations to list, add, and remove taxa from taxa lists. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as Client (UI)
participant Auth as Auth Service
participant API as Django API
participant DB as Database
UI->>Auth: include auth token
UI->>API: POST /taxa/lists/{taxaListId}/taxa/?project_id={pid} { taxon_id }
API->>Auth: validate token
Auth-->>API: token OK
API->>DB: fetch TaxaList scoped to project
DB-->>API: TaxaList record
API->>DB: fetch Taxon by id
DB-->>API: Taxon record / not found
alt taxon exists & not duplicate
API->>DB: create M2M association (taxalist.taxa.add)
DB-->>API: association persisted
API-->>UI: 201 Created + taxon payload
else duplicate or invalid
API-->>UI: 400 Bad Request / 404 Not Found
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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. Comment |
Co-Authored-By: Claude <noreply@anthropic.com>
|
Exciting! I will look into the missing BE parts. But would love to merge some version of this sooner and continue to improve |
|
Some observations when using the new endpoints to add and remove taxa from lists:
Just some thoughts, I'm fine leaving this as is for now! Thank you so much for the help @mohamedelabbas1996, it worked well to hook up with FE 🙏 |
|
Question: does it make sense to check "can update" permission for the taxa list, before rendering controls for "Add taxon" and "Remove taxon"? This is what I do know! :) |
… model)
Refactors taxa list endpoints to use nested routes and proper HTTP methods
while keeping the simple ManyToManyField (no through model).
**Backend changes:**
- Created TaxaListTaxonViewSet with nested routes under /taxa/lists/{id}/taxa/
- Simple serializers for input validation and output
- Removed deprecated add_taxon/remove_taxon actions from TaxaListViewSet
- Uses Django M2M .add() and .remove() methods directly
**Frontend changes:**
- Updated useAddTaxaListTaxon to POST to /taxa/ endpoint
- Updated useRemoveTaxaListTaxon to use DELETE method
**API changes:**
- POST /taxa/lists/{id}/taxa/ - Add taxon (returns 201, 400 for duplicates)
- DELETE /taxa/lists/{id}/taxa/by-taxon/{taxon_id}/ - Remove taxon (returns 204, 404 for non-existent)
- GET /taxa/lists/{id}/taxa/ - List taxa in list
- Removed POST /taxa/lists/{id}/add_taxon/ (deprecated)
- Removed POST /taxa/lists/{id}/remove_taxon/ (deprecated)
**Benefits:**
- Same API as project membership (consistency)
- No migration needed (keeps existing simple M2M)
- Proper HTTP semantics (POST=201, DELETE=204)
- RESTful nested resource design
**Tests:**
- Added comprehensive test suite (13 tests, all passing)
- Tests for CRUD operations, validation, and error cases
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
@annavik @mohamedelabbas1996 I updated the endpoints to use the same pattern as the project-user membership API, but no through-model to keep it a little simpler. I have in a separate PR here until I test it. Feel free to take a look before then. #1104 |
Match the permission pattern used by other project-scoped viewsets (ProjectViewSet, DeploymentViewSet, SiteViewSet, DeviceViewSet). Without this, non-staff project members cannot manage taxa lists. Co-Authored-By: Claude <noreply@anthropic.com>
The project write field was removed from the serializer in favour of server-side assignment in ProcessingServiceViewSet.perform_create(). Co-Authored-By: Claude <noreply@anthropic.com>
Tests were sending `project` as a POST field, but the write-only field was removed from ProcessingServiceSerializer in favor of server-side assignment via `project_id` query parameter. Also removes dead code in the serializer's create() override and fixes Prettier formatting. Co-Authored-By: Claude <noreply@anthropic.com>
Merge plan & interaction with #1110 and #1133Permission fix (latest commit)
Recommended merge order
Inline review commentsI've added inline comments on the two lines that will need updates when #1110 and #1133 merge. |
| "created_at", | ||
| "updated_at", | ||
| ] | ||
| permission_classes = [IsProjectMemberOrReadOnly] |
There was a problem hiding this comment.
After #1110 merges: Switch back to ObjectPermission. The new check_permission() routes M2M models (where get_project_accessor() returns "projects") through check_model_level_permission(), which uses global Django permissions instead of guardian object-level perms.
This will work correctly only if create_taxalist, update_taxalist, delete_taxalist are added to the AuthorizedUser role definition in #1110. Without those, taxa list creation breaks for all non-superusers.
| "updated_at", | ||
| ] | ||
| permission_classes = [IsProjectMemberOrReadOnly] | ||
| require_project = True |
There was a problem hiding this comment.
After #1133 merges: Change to require_project_for_list = False.
PR #1133 makes require_project_for_list = True the default on ProjectMixin, but opts out TaxaListViewSet because taxa lists are global M2M resources (same rationale as TaxonViewSet and TagViewSet). Keep require_project = True for write operations -- only listing should be allowed without a project filter.
ObjectPermission doesn't work for M2M-to-project models because BaseModel.get_project() returns None when get_project_accessor() returns "projects". This caused all write operations (create, update, delete) on taxa lists to be denied for every user. Switch to IsProjectMemberOrReadOnly which resolves the project via ProjectMixin.get_active_project() (from query param) instead of through the model instance. Add 10 permission tests covering member CRUD, anonymous read-only, non-member rejection, and owner access. Co-Authored-By: Claude <noreply@anthropic.com>
8990a33 to
4682933
Compare
Code reviewFound 3 issues:
antenna/ui/src/data-services/hooks/entities/utils.ts Lines 8 to 10 in 4682933 Affected serializers at
antenna/ami/main/api/serializers.py Lines 635 to 637 in 4682933 Root cause at Lines 142 to 144 in 4682933
Lines 1686 to 1691 in 4682933 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
Additional issues from code reviewTwo more issues to track: 4.
The frontend gates edit/delete buttons on 5. The Lines 1686 to 1691 in 4682933 Note: the frontend currently doesn't call this endpoint (it uses 🤖 Generated with Claude Code |
convertToServerFieldValues was sending `project_id` in the POST body, but the serializers for Sites, Devices, Storage Sources, and SourceImageCollections all declare `project` as the field name. Also pass project_id as a query param so IsProjectMemberOrReadOnly can resolve the active project for permission checks. Co-Authored-By: Claude <noreply@anthropic.com>
TaxaList had no permission entries in Project.Permissions, so non-superuser members could never receive create/update/delete permissions through the guardian role system. Adds create_taxalist, update_taxalist, and delete_taxalist to Project.Permissions, the Meta permissions list, and the ProjectManager role. Co-Authored-By: Claude <noreply@anthropic.com>
Models with an M2M relationship to Project (like TaxaList) return None from get_project(), causing the default permission resolver to return an empty list. This meant non-superuser members never saw edit/delete buttons despite having the correct role permissions. Adds add_m2m_object_permissions() to ami/base/permissions.py, which resolves permissions against the active project from the request context. TaxaListSerializer now calls this instead of the default path. The function is designed to be reused by other M2M-to-Project serializers (Taxon, Pipeline, ProcessingService) as part of #1120. Co-Authored-By: Claude <noreply@anthropic.com>
The UI fetches taxa for a list via the main /taxa/ endpoint with a
taxa_list_id filter, not the nested /taxa/lists/{id}/taxa/ list
endpoint. Remove the custom list() method and corresponding tests.
The viewset now only provides create (POST) and delete (DELETE).
Co-Authored-By: Claude <noreply@anthropic.com>
b21acfd to
d0c8c24
Compare
Merge main into feat/taxa-lists. The only conflict was in ui/src/app.tsx where the taxa-lists routes needed to coexist with the collections-to- capture-sets rename from main. Dropped a stale CollectionDetails route reference (component doesn't exist). Co-Authored-By: Claude <noreply@anthropic.com>
Use STRING.SELECT_TAXON_PLACEHOLDER and STRING.LOADING_DATA instead of hardcoded English strings in taxa list detail pages. Co-Authored-By: Claude <noreply@anthropic.com>
|
All 3 issues from this review are now resolved:
|
|
Both issues from this comment are also resolved: 4. 5. |
mihow
left a comment
There was a problem hiding this comment.
Tested once more with different user roles and I think it's ready to go!
The serializer had a write-only `project` PrimaryKeyRelatedField that was passed through to ProcessingService.objects.create(), but ProcessingService uses a M2M `projects` field, not a FK. This caused a TypeError on create. The field was left behind when #1094 moved project assignment to perform_create (which uses get_active_project from the query string). Remove the field entirely to match the TaxaList pattern: no writable project field on the serializer, M2M handled in perform_create. Also rename `project` to `project_id` in the frontend's generic convertToServerFieldValues to follow the codebase naming convention (ID values use _id suffix). Closes #1209 Co-Authored-By: Claude <noreply@anthropic.com>
…1210) * fix: remove stale project field from ProcessingServiceSerializer (#1209) The serializer had a write-only `project` PrimaryKeyRelatedField that was passed through to ProcessingService.objects.create(), but ProcessingService uses a M2M `projects` field, not a FK. This caused a TypeError on create. The field was left behind when #1094 moved project assignment to perform_create (which uses get_active_project from the query string). Remove the field entirely to match the TaxaList pattern: no writable project field on the serializer, M2M handled in perform_create. Also rename `project` to `project_id` in the frontend's generic convertToServerFieldValues to follow the codebase naming convention (ID values use _id suffix). Closes #1209 Co-Authored-By: Claude <noreply@anthropic.com> * fix: remove unnecessary project pop from ProcessingService create view DRF ignores unknown fields in request body, so no need to strip `project` before passing to the serializer. Co-Authored-By: Claude <noreply@anthropic.com> * fix: revert project_id rename in convertToServerFieldValues Site, Device, and StorageSource serializers still expect `project` (not `project_id`) as a writable PrimaryKeyRelatedField in the request body. The ProcessingService fix is backend-only — the serializer no longer declares a `project` field, so DRF silently ignores it. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>



Background
Will be a help for the upcoming class masking feature, but also something we have been wanting in general. This PR is still in draft mode.
Related to #997.
Missing BE stuff
taxalists/id/taxa/✅Missing FE stuff
Notes
Screenshots
List view:

Create view:

Edit view:

Delete view:

Detail view:

Summary by CodeRabbit
New Features
UI / Navigation
API / Backend
Hooks
Permissions
Tests