Repository navigation
Conversation
…h the API Adds /api/v2/occurrences/sets/ so curators can save a list of occurrences as a named set, list a project's sets with their size, and pick one from a choices endpoint that mirrors the capture set picker. The project and the occurrences are set once, on create: both are refused on PATCH and there is no PUT. Occurrence ids are checked against the project in one query and the error names up to ten that are missing or outside it. The project for the create permission is read from the body only, so the project checked is the one written to. Public sets are offered to every project but cannot be changed here. Row permissions are resolved against the active project, as for taxa lists, without a query per row. Split from #1492. Part of #763. Co-Authored-By: mohamedelabbas1996 <hack1996man@gmail.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MeUoKCfkiZaZjhja7e6AGh
Adds an occurrence_set_id query parameter to the occurrence list and the occurrence stats endpoints, named like collection_id and taxa_list_id. It is a filter backend rather than a FilterSet field so the export framework can run the same filter later. The value is parsed with SingleParamSerializer, so a non-integer returns 400. The filter only narrows: the active project and the project's default filters still apply, so a set never shows an occurrence the list would otherwise hide. Split from #1492. Part of #763. Co-Authored-By: mohamedelabbas1996 <hack1996man@gmail.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MeUoKCfkiZaZjhja7e6AGh
… set under the request's project A public set, one with no project, was listed with a details link that returned 403 for every caller, because the generic object check refuses anything without a project. The detail queryset already limits a caller to sets of projects they can see plus public sets, so retrieve now needs no further check. Editing a public set is still refused. A set shared by two projects (only possible through the Django admin) reported edit permissions on its row from the project in the request, while the edit itself was checked against the set's first project, so the offered buttons could fail and a curator of the first project could edit a set another project also used. An edit is now checked against the request's project when the set belongs to it, and against the set's first project otherwise. Tests cover public reads for every kind of caller and both directions of the shared-set case. Co-Authored-By: mohamedelabbas1996 <hack1996man@gmail.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MeUoKCfkiZaZjhja7e6AGh
…n to The occurrence list's "Create set" button reads the collection-level user_permissions from the set choices response, so a refactor of the paginator or the role grants could hide the button from curators or offer it to basic members without any test failing. This test fixes the answer for the owner, an ML data manager, a superuser, a basic member, a non-member and an anonymous visitor. Co-Authored-By: mohamedelabbas1996 <hack1996man@gmail.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MeUoKCfkiZaZjhja7e6AGh
✅ Deploy Preview for antenna-preview canceled.
|
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
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 |
…I behaviour The API sets a set's project and occurrences on create only. The docstrings, error text and test names now say that this is how the API creates sets today, with editing coming from curated sets (#1538) and saved-filter sets (#1539), instead of describing every set as fixed. Co-Authored-By: mohamedelabbas1996 <hack1996man@gmail.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MeUoKCfkiZaZjhja7e6AGh
Brings in the one-project model with the saved-set lifecycle (#1543). # Conflicts: # ami/main/models_future/occurrence_sets.py
The API follows the model from #1543: each set has one project, so the OccurrenceSetPermission class and the many-to-many and public-set handling are gone, and permissions come from the set's own project. /api/v2/occurrences/sets/ (now in ami/main/api/occurrence_set.py) has list, retrieve, create, update, delete, choices, and the lifecycle actions: add and remove (a list of ids, checked with one query, refused on a frozen set), freeze and copy. The occurrence list takes occurrence_set_id and not_occurrence_set_id, documented in the OpenAPI schema. Names the two "method" enums in the schema. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MeUoKCfkiZaZjhja7e6AGh
…trix Replaces the tests for the many-to-many API. Model tests stay in test_occurrence_set.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MeUoKCfkiZaZjhja7e6AGh
|
Claude says: A note on permissions, for when this PR is next picked up. There are now three ways to avoid the per-row permission cost:
#1430 and the cache in this PR solve the same problem. It may be simpler to adopt the #1430 approach here once it lands, instead of keeping a separate cache. The occurrence set |
✅ Deploy Preview for antenna-ssec ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Summary
#1543 adds occurrence sets: named groups of occurrences that one project keeps and uses again. This PR lets people and the UI work with them over the REST API. A user can create a set, add occurrences to it or remove them, freeze it so that results that refer to it stay true, and copy a frozen set to make a new version. The occurrence list can show only the members of a set, or leave them out.
The API follows the model in #1543 and the design on the saved-sets epic (#1548, design notes). Each set belongs to one project, and that project's roles decide who can change it. Any project member can read a project's sets. ML data managers and project managers create, edit, freeze and copy them, as they manage capture sets.
List of Changes
OccurrenceSetViewSetinami/main/api/occurrence_set.py, at/api/v2/occurrences/sets/project_idGET .../sets/choices/?project_id=returns names, methods and dates, up to 100, most recently updated firstPOST .../sets/{id}/add/and.../remove/with{"occurrence_ids": [...]}, 1 to 1,000 ids. The ids are checked with one query; any id outside the set's project refuses the whole requestPOST .../sets/{id}/freeze/recordsfrozen_atandfrozen_by. A frozen set refuses add, remove and a second freeze. It can still be renamedPOST .../sets/{id}/copy/with an optionalname. It returns the new set, not frozen, with the same members andcopied_fromsetmethod,kwargs,occurrences_count,populated_at,frozen_at,frozen_by(name and image only),copied_from, anduser_permissions(update,delete,freeze,copy)occurrence_set_idandnot_occurrence_set_id, asOccurrenceSetFilterinOCCURRENCE_FILTER_BACKENDS, documented in the OpenAPI schemaObjectPermissionwith the model's checks.add,removeandfreezeneed the update permission;copyneeds createOccurrenceSetPermissionand its shared-set rulesENUM_NAME_OVERRIDESnames the capture set and occurrence setmethodenumsEndpoints
GET /occurrences/sets/?project_id=GET /occurrences/sets/choices/?project_id=POST /occurrences/sets/{"name", "description", "project"}.methodismanualandkwargsmust be emptyGET,PATCH,DELETE /occurrences/sets/{id}/POST /occurrences/sets/{id}/add/,.../remove/{"occurrence_ids": [...]}POST /occurrences/sets/{id}/freeze/POST /occurrences/sets/{id}/copy/{"name"}GET /occurrences/?project_id=&occurrence_set_id=GET /occurrences/?project_id=¬_occurrence_set_id=Detailed Description
Errors.
kwargson a manual set, a change of project, anoccurrence_set_idthat is not a positive number.Query counts are measured in tests with strict counts:
choices: 10 queries.addwith 7 ids: 12 queries.Tests.
ami/main/models_future/tests/test_occurrence_set_api.pycovers:The model tests from #1543 stay in
test_occurrence_set.py. The earlier test file for the many-to-many API is removed.What comes next
This is the order of work from #1548:
filtersmethod, with the occurrence-list filters saved inkwargs;populated_at;FilterSetfor the occurrence list (Use one shared path from occurrence filters to results, for the occurrence list and for exports #1537) comes first, so saved filters mean the same thing everywhere.Note for the stacked branches (@mohamedelabbas1996)
occurrence_set_idandnot_occurrence_set_idon the occurrence list. They are also available to exports, throughOccurrenceSetFilterinami.main.api.views.project, notproject_id, and no occurrences. Create the set, thenPOST .../add/with the ids. Scoring and training should use a frozen set, or freeze a copy, and record the set's id andfrozen_at.for_project()and the shared-set rules are gone. Scope withproject=....Open questions
Jobor by a model reference in the job settings.apply_defaults=false, so the UI may need to show "N of M shown".occurrence_ids, so the UI can save a selection in one request?How to Test the Changes
docker compose run --rm django python manage.py test ami.main.models_future.tests.test_occurrence_set ami.main.models_future.tests.test_occurrence_set_api ami.users ami.exports docker compose run --rm django python manage.py makemigrations --check --dry-runDeployment Notes
There are no new migrations in this PR; it uses the ones in #1543.
Checklist
🤖 Generated with Claude Code
https://claude.ai/code/session_01MeUoKCfkiZaZjhja7e6AGh