Repository navigation
Conversation
Someone reviewing an occurrence set needs to see what is in it, and the occurrence list is where that review starts. The filter panel now offers an occurrence set under More filters, beside the capture set, picked from the set choices endpoint in the same way. The section opens on arrival when a set is already applied, so a link carrying occurrence_set_id shows the filter where it can be cleared. The field is occurrence_set_id, the query parameter the occurrence list backend reads, and it is part of the filters other views may carry into the occurrence list. 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
Someone who has filtered the occurrence list down to the rows they care about had no way to keep that selection. While rows are selected, a "Create set" button now sits in the toolbar beside Export. It opens a small form that takes a name and saves the selected occurrences on the current page as a new set in the project. Selections do not carry across pages, so one set holds at most a page of occurrences; the help text says so. The button lives in the toolbar rather than in the bulk identification bar. Saving a set changes none of the occurrences, so it should not depend on update rights over them, and the selection bar stays reserved for identification actions. The button appears only for callers the server allows to create a set in this project, read from the user_permissions of the set choices response, so a basic member is not offered an action that would be refused. The request sends project_id and occurrence_ids as integers, matching the create contract of the occurrence set endpoint. When the server refuses the request, the form shows its reason: an error on the name appears under the name field, and errors on the occurrences or the project (for example an occurrence deleted since the page loaded) appear in the form's banner, instead of axios's "Request failed with status code 400". Save stays disabled until the name has a non-blank character, and the name is sent trimmed. 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 ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Contributor
|
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 |
This was referenced Oct 21, 2026
The create popover and the filter tooltip described every set as a list that never changes. They now say what a set is for (reviewing, exporting and scoring the same occurrences again) and that later filter changes do not change a saved set, which stays true when curated and saved-filter sets arrive (#1538, #1539). 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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
This is for people reviewing occurrences who want to come back to the same group of them later, such as a handful of tricky identifications to discuss or a small batch to check by hand. On the occurrence list they can now select rows and save them as a named occurrence set, and they can narrow the list to the occurrences in any set of the project.
Saving works on the selection, and a selection covers only the page being viewed (20 rows by default), so one set made this way holds at most a page of occurrences. That fits hand-picked lists. Sets for evaluation or retraining runs will usually need far more occurrences than that, and the way to make those is to save everything that matches the current filters, which is planned in #1539. Until then, larger sets can be created through the API from a list of occurrence ids.
It is the user-facing part of the occurrence sets epic #763. @mohamedelabbas1996 built it in #1492; this PR carries his UI over onto the smaller backend PR it is stacked on, with the contract changes from the review (the filter parameter is
occurrence_set_id, and only curators who may create a set are offered the button).List of Changes
OccurrenceSetFilter(anEntityPickeroveroccurrences/sets/choices) registered for theoccurrence_set_idfield; shown under "More filters" beside the capture set.occurrence_set_idpresent in another view's URL is carried along when following a link into the occurrence list.occurrence_set_idadded toFILTERS_TO_OCCURRENCES.CreateOccurrenceSetPopover+CreateOccurrenceSetform (name only) +useCreateOccurrenceSetmutation postingname,project_id,occurrence_idstooccurrences/sets/.getFormErrors(create-occurrence-set/form-errors.ts) puts anameerror under the name field and theoccurrence_idsorproject_iderrors in the form banner; Save stays disabled for a blank or whitespace-only name.form-errors.test.ts, using the error payloads the API returns.user_permissionsfrom the set choices response, which includescreatewhen the caller hascreate_occurrenceseton the project (or is a superuser).ServerOccurrenceSetinterface indata-services/models/occurrence-set.ts; new strings inutils/language.ts.Detailed Description
Filter. The field name is the query parameter,
occurrence_set_id, matchingtaxa_list_idand the backend filter added in the base PR. In #1492 the UI field wasoccurrence_set; the review settled onoccurrence_set_id. The picker uses the same choices endpoint pattern as the capture set filter (names only, most recently updated first, up to 100).The occurrence list still applies the project's default filters when a set is selected, so a set can show fewer rows than it holds. Turning off the default filters shows every occurrence in the set. That is the backend's documented behaviour in the base PR; this PR does not change it.
Creating a set. The form asks only for a name. The help text says how many of the selected occurrences on this page are being saved, and that changing the filters later does not change the set. It does not promise the contents never change: sets that people edit by hand are planned (#1538), and deleting or merging an occurrence removes it from every set (#1540). After a successful save, the popover closes and the set choices are refreshed, so the new set is immediately available in the filter.
When the server refuses the request, the reason is shown rather than axios's "Request failed with status code 400". The API reports validation failures per field: a blank name returns
{"name": ["This field may not be blank."]}, which appears under the name field, and an occurrence deleted since the page loaded returns{"occurrence_ids": ["Not occurrences in project N: [...]"]}, which appears in the form's banner. Adetailmessage (for example a 403) also goes in the banner. The sharedparseServerErrorhelper is unchanged; the mapping lives in a small module next to the form.Selection is per page. The occurrence list keeps only the selected ids that are on the current page, and the default page size is 20, so changing page drops the selection. This PR does not change that, because it is how the bulk identification actions already behave. Saving all occurrences that match the current filters, as a one-time snapshot or a live query, is #1539, and is what evaluation and retraining sets will need.
Placement. The button sits in the toolbar beside Export and appears only while rows are selected, since that is the only time it does anything. It is deliberately not part of the bulk identification bar at the bottom of the page, so it does not interfere with the agree / suggest ID actions there, and those actions keep their own update-permission gate.
Permission gate. The button reads
user_permissionsfromGET occurrences/sets/choices/?project_id=…. The base PR pins that response inTestListingSets.test_choices_offer_create_only_to_users_who_may_create_a_set(ami/main/test_occurrence_sets.py): it listscreatefor the project owner, an ML data manager and a superuser, and nothing for a basic member, a non-member or an anonymous visitor. A basic member's create request is refused with 403 (TestCreatingASet.test_only_users_with_the_create_permission_can_create_one).For downstream branches
@mohamedelabbas1996,
feat/evaluate-classifiers(#1493),feat/retrain-job-plumbing(#1501) andfeat/retrain-training-set(#1502) each carry #1492's ten UI files unchanged. Rebasing them onto this PR gives add/add conflicts in those files. The simplest route is to drop #1492's UI commits when rebasing rather than resolve the conflicts; this PR replaces them. The differences from #1492's UI:occurrence_set_id(it wasoccurrence_set).STRING.CREATE_OCCURRENCE_SETis removed; the button usesSTRING.CREATE_SET.MESSAGE_CREATE_OCCURRENCE_SET, and the tooltip describes what a set is for.STRING.FIELD_LABEL_OCCURRENCE_SET,API_ROUTES.OCCURRENCE_SETSandAPI_ROUTES.OCCURRENCE_SET_CHOICESkeep their names, so the training form onfeat/retrain-job-form(#1504), which uses them, should not need changes. Itsoccurrence_setparameter goes to that branch's training summary endpoint, not to the occurrence list, so the rename here does not touch it.How to Test
Checks run on this branch (measured):
yarn tsc --noEmit: cleanyarn lint: cleanprettier --checkon the changed files: cleanyarn test(jest): 15 suites, 77 tests passed (including the newform-errors.test.ts)python manage.py test ami.main.test_occurrence_setsran 31 tests, OKNot done for this PR: a manual run in the browser against the base branch's backend. To try it by hand:
?occurrence_set_id=<id>: the filter is shown, applied and clearable.Stacked on
This PR is stacked on
feat/occurrence-set-api(the occurrence set API and theoccurrence_set_idlist filter), which is itself stacked onfeat/occurrence-set-model. Retarget tomainonce those merge.Related Issues
Closes #1535
Part of #763
Split from #1492
Follow-ups: #1539 (save the current filters as a set), #1540 (membership when occurrences are deleted or merged)
🤖 Generated with Claude Code
https://claude.ai/code/session_01MeUoKCfkiZaZjhja7e6AGh