Skip to content

Commit

Permalink
Temporal Annotations (#30)
Browse files Browse the repository at this point in the history
* temporal annotation models and endpoints

* migrations for temporal endpoints

* updating migrations

* basics of rendering temporal annotations

* linting

* temporal creation/editing/deletion

* supporting multiple users with sequence and pulse annotations

* client cleanup of minor issues
  • Loading branch information
BryonLewis authored Feb 20, 2024
1 parent ce43eb8 commit ebcebcf
Show file tree
Hide file tree
Showing 30 changed files with 1,517 additions and 267 deletions.
4 changes: 4 additions & 0 deletions bats_ai/core/admin/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from .annotations import AnnotationsAdmin
from .image import ImageAdmin
from .recording import RecordingAdmin
from .species import SpeciesAdmin
from .spectrogram import SpectrogramAdmin
from .temporal_annotations import TemporalAnnotationsAdmin

__all__ = [
'AnnotationsAdmin',
'ImageAdmin',
'RecordingAdmin',
'SpectrogramAdmin',
'TemporalAnnotationsAdmin',
'SpeciesAdmin',
]
17 changes: 17 additions & 0 deletions bats_ai/core/admin/species.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from django.contrib import admin

from bats_ai.core.models import Species


@admin.register(Species)
class SpeciesAdmin(admin.ModelAdmin):
list_display = [
'pk',
'species_code',
'family',
'genus',
'species',
'common_name',
'species_code_6',
]
list_select_related = True
18 changes: 18 additions & 0 deletions bats_ai/core/admin/temporal_annotations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from django.contrib import admin

from bats_ai.core.models import TemporalAnnotations


@admin.register(TemporalAnnotations)
class TemporalAnnotationsAdmin(admin.ModelAdmin):
list_display = [
'pk',
'recording',
'owner',
'start_time',
'end_time',
'type',
'comments',
]
list_select_related = True
autocomplete_fields = ['owner']
43 changes: 43 additions & 0 deletions bats_ai/core/migrations/0007_temporalannotations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Generated by Django 4.1.13 on 2024-02-15 18:08

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('core', '0006_alter_recording_recording_location'),
]

operations = [
migrations.CreateModel(
name='TemporalAnnotations',
fields=[
(
'id',
models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name='ID'
),
),
('start_time', models.IntegerField(blank=True, null=True)),
('end_time', models.IntegerField(blank=True, null=True)),
('type', models.TextField(blank=True, null=True)),
('comments', models.TextField(blank=True, null=True)),
(
'owner',
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
),
),
(
'recording',
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to='core.recording'
),
),
('species', models.ManyToManyField(to='core.species')),
],
),
]
2 changes: 2 additions & 0 deletions bats_ai/core/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from .recording_annotation_status import RecordingAnnotationStatus
from .species import Species
from .spectrogram import Spectrogram
from .temporal_annotations import TemporalAnnotations

__all__ = [
'Annotations',
Expand All @@ -12,4 +13,5 @@
'RecordingAnnotationStatus',
'Species',
'Spectrogram',
'TemporalAnnotations',
]
15 changes: 15 additions & 0 deletions bats_ai/core/models/temporal_annotations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from django.contrib.auth.models import User
from django.db import models

from .recording import Recording
from .species import Species


class TemporalAnnotations(models.Model):
recording = models.ForeignKey(Recording, on_delete=models.CASCADE)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
start_time = models.IntegerField(blank=True, null=True)
end_time = models.IntegerField(blank=True, null=True)
type = models.TextField(blank=True, null=True)
comments = models.TextField(blank=True, null=True)
species = models.ManyToManyField(Species)
4 changes: 4 additions & 0 deletions bats_ai/core/views/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from .annotations import router as AnnotationRouter
from .recording import router as RecordingRouter
from .species import router as SpeciesRouter
from .temporal_annotations import router as TemporalAnnotationRouter

__all__ = [
'RecordingRouter',
'SpeciesRouter',
'AnnotationRouter',
'TemporalAnnotationRouter',
]
34 changes: 26 additions & 8 deletions bats_ai/core/views/annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

from django.http import HttpRequest
from ninja import Schema
from ninja.errors import HttpError
from ninja.pagination import RouterPaginated
from oauth2_provider.models import AccessToken

from bats_ai.core.models import Annotations, Recording

logger = logging.getLogger(__name__)

Expand All @@ -23,10 +23,28 @@ class AnnotationSchema(Schema):
comments: str


def get_owner_id(request: HttpRequest):
token = request.headers.get('Authorization').replace('Bearer ', '')
token_found = AccessToken.objects.get(token=token)
if not token_found:
raise HttpError(401, 'Authentication credentials were not provided.')
@router.get('/{id}')
def get_annotation(request: HttpRequest, id: int):
try:
annotation = Annotations.objects.get(pk=id)
recording = annotation.recording

# Check if the user owns the recording or if the recording is public
if recording.owner == request.user or recording.public:
# Query annotations associated with the recording that are owned by the current user
annotations_qs = Annotations.objects.filter(recording=recording, owner=request.user)

# Serialize the annotations using AnnotationSchema
annotations_data = [
AnnotationSchema.from_orm(annotation, owner_email=request.user.email).dict()
for annotation in annotations_qs
]

return annotations_data
else:
return {
'error': 'Permission denied. You do not own this annotation, or the associated recording is not public.'
}

return token_found.user.pk
except Recording.DoesNotExist:
return {'error': 'Recording not found'}
Loading

0 comments on commit ebcebcf

Please sign in to comment.