From 786785e650eda2ad7c8ccd20731d621eb05085e8 Mon Sep 17 00:00:00 2001 From: Bryon Lewis Date: Fri, 13 Mar 2026 11:32:52 -0400 Subject: [PATCH 1/4] allow deletion of blank species annotation, persistent clearing of species --- bats_ai/core/views/recording_annotation.py | 13 +++++++----- .../components/RecordingAnnotationEditor.vue | 13 ++++++------ client/src/components/SingleSpecieEditor.vue | 20 ++++++++++++++++++- client/src/components/SpeciesEditor.vue | 13 ++++++++++-- 4 files changed, 44 insertions(+), 15 deletions(-) diff --git a/bats_ai/core/views/recording_annotation.py b/bats_ai/core/views/recording_annotation.py index 3f6e9fa3..5be4e47f 100644 --- a/bats_ai/core/views/recording_annotation.py +++ b/bats_ai/core/views/recording_annotation.py @@ -206,17 +206,20 @@ def delete_recording_annotation(request: HttpRequest, pk: int): vetting_enabled = ( configuration.mark_annotations_completed_enabled if configuration else False ) - if vetting_enabled and not request.user.is_staff: - raise HttpError( - 403, "Permission denied. Annotations cannot be deleted while vetting is enabled" - ) - annotation = RecordingAnnotation.objects.get(pk=pk) # Check permission: only the annotation owner may delete their own if annotation.owner != request.user: raise HttpError(403, "Permission denied.") + # In vetting mode, non-staff may only delete blank annotations (no species) + if vetting_enabled and not request.user.is_staff and annotation.species.exists(): + raise HttpError( + 403, + "Permission denied. Only blank annotations can be deleted " + "while vetting is enabled.", + ) + annotation.delete() return "Recording annotation deleted successfully." except RecordingAnnotation.DoesNotExist as e: diff --git a/client/src/components/RecordingAnnotationEditor.vue b/client/src/components/RecordingAnnotationEditor.vue index 7f0b359c..e7cc5d91 100644 --- a/client/src/components/RecordingAnnotationEditor.vue +++ b/client/src/components/RecordingAnnotationEditor.vue @@ -170,13 +170,11 @@ export default defineComponent({ }); const deleteEnabled = computed(() => { - return ( - props.type !== 'nabat' - && ( - configuration.value.is_admin - || !configuration.value.mark_annotations_completed_enabled - ) - ); + if (props.type === 'nabat') return false; + if (configuration.value.is_admin) return true; + if (!configuration.value.mark_annotations_completed_enabled) return true; + // In vetting mode, non-admins may only delete blank annotations + return speciesEdit.value.length === 0; }); return { @@ -267,6 +265,7 @@ export default defineComponent({ :species-list="species" :disabled="annotation?.submitted || updatingAnnotation || deletingAnnotation" @update:model-value="onSpeciesModelValue" + @delete-blank-annotation="deleteAnnotation" /> diff --git a/client/src/components/SingleSpecieEditor.vue b/client/src/components/SingleSpecieEditor.vue index 321a29ad..529dd35e 100644 --- a/client/src/components/SingleSpecieEditor.vue +++ b/client/src/components/SingleSpecieEditor.vue @@ -99,6 +99,14 @@ export default defineComponent({ onMounted(() => window.addEventListener("keydown", speciesShortcut)); onUnmounted(() => window.removeEventListener("keydown", speciesShortcut)); + const onClearOrDeleteClick = () => { + if (selectedCode.value) { + selectedCode.value = null; + } else { + emit("delete"); + } + }; + return { search, selectedCode, @@ -106,6 +114,7 @@ export default defineComponent({ customFilter, categoryColors, speciesAutocomplete, + onClearOrDeleteClick, }; }, }); @@ -129,7 +138,6 @@ export default defineComponent({ item-value="species_code" :multiple="false" :custom-filter="customFilter" - clearable clear-on-select label="Select species" :menu-props="{ maxHeight: '300px', maxWidth: '400px' }" @@ -138,6 +146,16 @@ export default defineComponent({ density="compact" hide-details > + From 23486183c2fda43dde1ad750c0e3ff4e1ab7bd38 Mon Sep 17 00:00:00 2001 From: Bryon Lewis Date: Fri, 13 Mar 2026 11:51:38 -0400 Subject: [PATCH 3/4] formatting --- client/src/components/SpeciesEditor.vue | 55 ++++++++++++++----------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/client/src/components/SpeciesEditor.vue b/client/src/components/SpeciesEditor.vue index a622ccdf..8447b21c 100644 --- a/client/src/components/SpeciesEditor.vue +++ b/client/src/components/SpeciesEditor.vue @@ -150,34 +150,41 @@ export default defineComponent({ @delete="onSlotDelete(index)" /> - + - - mdi-plus - Add Bat - + + + mdi-plus + + Add Bat + - - {{ annotationComment ? 'mdi-pencil' : 'mdi-plus' }} - Comment - + + + {{ annotationComment ? 'mdi-pencil' : 'mdi-plus' }} + + Comment + Date: Fri, 13 Mar 2026 12:12:28 -0400 Subject: [PATCH 4/4] prevent adding multiple annotations in vetting mode --- .../src/components/RecordingAnnotations.vue | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/client/src/components/RecordingAnnotations.vue b/client/src/components/RecordingAnnotations.vue index 6fa5daff..31d7eae1 100644 --- a/client/src/components/RecordingAnnotations.vue +++ b/client/src/components/RecordingAnnotations.vue @@ -145,6 +145,26 @@ export default defineComponent({ return ( currentUserAnnotations.length > 0 && props.type === 'nabat'); }); + const vettingMode = computed(() => configuration.value.mark_annotations_completed_enabled); + // Count all annotations owned by current user (submitted and unsubmitted) for vetting one-annotation limit + const userAnnotationCount = computed(() => + annotations.value.filter( + (a: FileAnnotation) => a.owner === currentUser.value + ).length + ); + const vettingModeAddDisabled = computed(() => + vettingMode.value && userAnnotationCount.value > 0 + ); + const addButtonDisabled = computed(() => + addingAnnotation.value || disableNaBatAnnotations.value || vettingModeAddDisabled.value + ); + const addButtonTooltip = computed(() => { + if (vettingModeAddDisabled.value) { + return 'In vetting mode you may only add one annotation per recording.'; + } + return ''; + }); + function getConfidenceLabelText(confidence: number) { return `Confidence: ${confidence.toFixed(2)}`; } @@ -167,6 +187,11 @@ export default defineComponent({ userSubmittedAnnotationId, handleSubmitAnnotation, configuration, + addButtonDisabled, + addButtonTooltip, + userAnnotationCount, + vettingModeAddDisabled, + vettingMode, }; }, }); @@ -194,8 +219,26 @@ export default defineComponent({ + + + {{ addButtonTooltip }} +