Skip to content

Commit e24559f

Browse files
Implement Vetting Workflow (#292)
* Allow Users to Submit Annotations (#281) * Add settings for vetting * Model submission status of recording annotations * Add endpoint to get current user * Show recording submission status in table view * Don't filter out noise The model can predict noise, so sorting it out of the species list can lead to problems when loading/displaying that value in a list. * Add endpoint to submit file-level annotations * Allow submitting file annotations in interface * Squash migrations * Indicate when a file has been reviewed * Format * Show the current user's submitted label in sidebar * Disable deletion for non-admin vetters * Make 403 message more descriptive * Vetting progress UI (#293) * Add settings for vetting * Model submission status of recording annotations * Add endpoint to get current user * Show recording submission status in table view * Don't filter out noise The model can predict noise, so sorting it out of the species list can lead to problems when loading/displaying that value in a list. * Add endpoint to submit file-level annotations * Allow submitting file annotations in interface * Squash migrations * Indicate when a file has been reviewed * Format * Show the current user's submitted label in sidebar * Disable deletion for non-admin vetters * Make 403 message more descriptive * Show progress bar for submitted recordings * Enable showing/hiding submitted recordings * Toggle submitted recordings in sidebar * Fix submission bug * Navigate through unreviewed files (#294) * Add settings for vetting * Model submission status of recording annotations * Add endpoint to get current user * Show recording submission status in table view * Don't filter out noise The model can predict noise, so sorting it out of the species list can lead to problems when loading/displaying that value in a list. * Add endpoint to submit file-level annotations * Allow submitting file annotations in interface * Squash migrations * Indicate when a file has been reviewed * Format * Show the current user's submitted label in sidebar * Disable deletion for non-admin vetters * Make 403 message more descriptive * Show progress bar for submitted recordings * Enable showing/hiding submitted recordings * Toggle submitted recordings in sidebar * Fix submission bug * Add button to go to next unreviewed file * Add function to get next unreviewed recording * Navigate between unreviewed files * Display a message when there are no files to review * Reset selected annotation when recording changes * Show a user their submitted labels (#295) * Show submission status and label in sidebar * Show user submitted label in Recordings view * Hide detailed metadata in vetting mode (#296) * In vetting mode, hide detailed metadata fields * Hide annotation summary in vetting mode * Show GRTS cell bounding box for vetters * Indicate pending submissions * Update title for pulse annotation column * Hide "my recordings" if non-admins can't upload (#298) * Hide "my recordings" if non-admins can't upload * Add missing semicolon Co-authored-by: Bryon Lewis <61746913+BryonLewis@users.noreply.github.com> * Hide progress bar for my recordings when needed --------- Co-authored-by: Bryon Lewis <61746913+BryonLewis@users.noreply.github.com> * Allow users to add reference materials (#297) * Allow users to add reference materials * Model vetting details per user * Update vetting details from front end * Use separate component for reference materials * Add length check and test to API layer * Swap position of close/save for consistency * Use correct max length value for vuetify rule Co-authored-by: Bryon Lewis <61746913+BryonLewis@users.noreply.github.com> --------- Co-authored-by: Bryon Lewis <61746913+BryonLewis@users.noreply.github.com> * Vetting workflow feedback (#301) * Make return type compatable for column definition * Add scale widget to location map * Fix casing for kHz * Fix scrolling in spectrogram sidebar * Add tooltip to submission status * Ensure configuration is loaded before column check * Remove unused setting and remake migrations * Use tooltip in shared list --------- Co-authored-by: Bryon Lewis <61746913+BryonLewis@users.noreply.github.com>
1 parent ddddd1c commit e24559f

36 files changed

Lines changed: 1632 additions & 264 deletions

bats_ai/api.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
RecordingRouter,
1414
RecordingTagRouter,
1515
SpeciesRouter,
16+
VettingRouter,
1617
)
1718
from bats_ai.core.views.nabat import NABatConfigurationRouter, NABatRecordingRouter
1819

@@ -46,3 +47,4 @@ def global_auth(request):
4647
api.add_router('/recording-tag/', RecordingTagRouter)
4748
api.add_router('/nabat/recording/', NABatRecordingRouter)
4849
api.add_router('/nabat/configuration/', NABatConfigurationRouter)
50+
api.add_router('/vetting/', VettingRouter)

bats_ai/core/admin/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from .species import SpeciesAdmin
1818
from .spectrogram import SpectrogramAdmin
1919
from .spectrogram_image import SpectrogramImageAdmin
20+
from .vetting_details import VettingDetailsAdmin
2021

2122
__all__ = [
2223
'AnnotationsAdmin',
@@ -32,6 +33,7 @@
3233
'ConfigurationAdmin',
3334
'ExportedAnnotationFileAdmin',
3435
'SpectrogramImageAdmin',
36+
'VettingDetailsAdmin',
3537
# NABat Models
3638
'NABatRecordingAnnotationAdmin',
3739
'NABatCompressedSpectrogramAdmin',

bats_ai/core/admin/recording_annotations.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class RecordingAnnotationAdmin(admin.ModelAdmin):
1414
'additional_data',
1515
'comments',
1616
'model',
17+
'submitted',
1718
]
1819
list_select_related = True
1920
filter_horizontal = ('species',) # or filter_vertical
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from django.contrib import admin
2+
3+
from bats_ai.core.models import VettingDetails
4+
5+
6+
@admin.register(VettingDetails)
7+
class VettingDetailsAdmin(admin.ModelAdmin):
8+
list_display = [
9+
'pk',
10+
'user',
11+
# 'reference_materials',
12+
]
13+
search_fields = ('user',)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Generated by Django 4.2.23 on 2026-01-15 18:06
2+
3+
from django.conf import settings
4+
from django.db import migrations, models
5+
import django.db.models.deletion
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12+
('core', '0024_delete_image'),
13+
]
14+
15+
operations = [
16+
migrations.AddField(
17+
model_name='configuration',
18+
name='mark_annotations_completed_enabled',
19+
field=models.BooleanField(default=False),
20+
),
21+
migrations.AddField(
22+
model_name='configuration',
23+
name='non_admin_upload_enabled',
24+
field=models.BooleanField(default=True),
25+
),
26+
migrations.AddField(
27+
model_name='recordingannotation',
28+
name='submitted',
29+
field=models.BooleanField(default=False),
30+
),
31+
migrations.CreateModel(
32+
name='VettingDetails',
33+
fields=[
34+
(
35+
'id',
36+
models.BigAutoField(
37+
auto_created=True, primary_key=True, serialize=False, verbose_name='ID'
38+
),
39+
),
40+
('reference_materials', models.TextField(blank=True)),
41+
(
42+
'user',
43+
models.OneToOneField(
44+
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
45+
),
46+
),
47+
],
48+
),
49+
migrations.AddConstraint(
50+
model_name='vettingdetails',
51+
constraint=models.CheckConstraint(
52+
check=models.Q(('reference_materials__length__lte', 2000)),
53+
name='reference_materials_max_2000',
54+
),
55+
),
56+
]

bats_ai/core/models/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from .species import Species
1212
from .spectrogram import Spectrogram
1313
from .spectrogram_image import SpectrogramImage
14+
from .vetting_details import VettingDetails
1415

1516
__all__ = [
1617
'Annotations',
@@ -28,4 +29,5 @@
2829
'ProcessingTaskType',
2930
'ExportedAnnotationFile',
3031
'SpectrogramImage',
32+
'VettingDetails',
3133
]

bats_ai/core/models/configuration.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ class AvailableColorScheme(models.TextChoices):
3232
# 18 characters is just enough for "rgb(255, 255, 255)"
3333
default_spectrogram_background_color = models.CharField(max_length=18, default='rgb(0, 0, 0)')
3434

35+
# Fields used for community vetting focused deployment of BatAI
36+
non_admin_upload_enabled = models.BooleanField(default=True)
37+
mark_annotations_completed_enabled = models.BooleanField(default=False)
38+
3539
def save(self, *args, **kwargs):
3640
# Ensure only one instance of Configuration exists
3741
if not Configuration.objects.exists() and not self.pk:

bats_ai/core/models/recording_annotation.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ class RecordingAnnotation(TimeStampedModel, models.Model):
1212
owner = models.ForeignKey(User, on_delete=models.CASCADE)
1313
species = models.ManyToManyField(Species)
1414
comments = models.TextField(blank=True, null=True)
15-
model = models.TextField(blank=True, null=True) # AI Model information if inference used
15+
# AI Model information if inference used, else "User Defined"
16+
model = models.TextField(blank=True, null=True)
1617
confidence = models.FloatField(
1718
default=1.0,
1819
validators=[
@@ -24,3 +25,4 @@ class RecordingAnnotation(TimeStampedModel, models.Model):
2425
additional_data = models.JSONField(
2526
blank=True, null=True, help_text='Additional information about the models/data'
2627
)
28+
submitted = models.BooleanField(default=False)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from django.contrib.auth.models import User
2+
from django.db import models
3+
from django.db.models import Q
4+
from django.db.models.functions import Length
5+
6+
models.TextField.register_lookup(Length, 'length')
7+
8+
9+
class VettingDetails(models.Model):
10+
user = models.OneToOneField(User, on_delete=models.CASCADE)
11+
reference_materials = models.TextField(blank=True)
12+
13+
class Meta:
14+
constraints = [
15+
models.CheckConstraint(
16+
# TODO change to 'condition' in Django v6
17+
check=Q(reference_materials__length__lte=2000),
18+
name='reference_materials_max_2000',
19+
)
20+
]

bats_ai/core/tests/conftest.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
from django.test import Client
33
import pytest
44

5-
from .factories import SuperuserFactory, UserFactory
5+
from bats_ai.core.models import VettingDetails
6+
7+
from .factories import SuperuserFactory, UserFactory, VettingDetailsFactory
68

79

810
@pytest.fixture
@@ -32,3 +34,13 @@ def authorized_client(superuser: User) -> Client:
3234
client = Client()
3335
client.force_login(user=superuser)
3436
return client
37+
38+
39+
@pytest.fixture
40+
def vetting_details(user: User) -> VettingDetails:
41+
return VettingDetailsFactory(user=user)
42+
43+
44+
@pytest.fixture
45+
def random_user_vetting_details() -> VettingDetails:
46+
return VettingDetailsFactory(user=UserFactory())

0 commit comments

Comments
 (0)