From 63c0dcdaaa05503002858ce4945a231862634db1 Mon Sep 17 00:00:00 2001 From: Bryon Lewis Date: Tue, 12 Nov 2024 12:54:29 -0500 Subject: [PATCH 01/11] adding recording annotation model --- bats_ai/core/models/recording_annotation.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 bats_ai/core/models/recording_annotation.py diff --git a/bats_ai/core/models/recording_annotation.py b/bats_ai/core/models/recording_annotation.py new file mode 100644 index 0000000..f458247 --- /dev/null +++ b/bats_ai/core/models/recording_annotation.py @@ -0,0 +1,12 @@ +from django.contrib.auth.models import User +from django.db import models + +from .recording import Recording +from .species import Species + + +class RecordingAnnotation(models.Model): + recording = models.ForeignKey(Recording, on_delete=models.CASCADE) + owner = models.ForeignKey(User, on_delete=models.CASCADE) + species = models.ManyToManyField(Species) + comments = models.TextField(blank=True, null=True) From c145a9ccb8fddc7a95a055fbf05d512a7d582ab1 Mon Sep 17 00:00:00 2001 From: Bryon Lewis Date: Mon, 2 Dec 2024 10:26:14 -0500 Subject: [PATCH 02/11] Add recording annnotation and migrations --- bats_ai/api.py | 9 +- ...options_annotations_confidence_and_more.py | 100 +++++++++++++ bats_ai/core/models/__init__.py | 2 + bats_ai/core/models/annotations.py | 13 +- bats_ai/core/models/recording_annotation.py | 13 +- bats_ai/core/views/__init__.py | 2 + bats_ai/core/views/recording.py | 5 + bats_ai/core/views/recording_annotation.py | 140 ++++++++++++++++++ 8 files changed, 281 insertions(+), 3 deletions(-) create mode 100644 bats_ai/core/migrations/0011_alter_annotations_options_annotations_confidence_and_more.py create mode 100644 bats_ai/core/views/recording_annotation.py diff --git a/bats_ai/api.py b/bats_ai/api.py index 88ab5cd..87a6469 100644 --- a/bats_ai/api.py +++ b/bats_ai/api.py @@ -3,7 +3,13 @@ from ninja import NinjaAPI from oauth2_provider.models import AccessToken -from bats_ai.core.views import GRTSCellsRouter, GuanoMetadataRouter, RecordingRouter, SpeciesRouter +from bats_ai.core.views import ( + GRTSCellsRouter, + GuanoMetadataRouter, + RecordingAnnotationRouter, + RecordingRouter, + SpeciesRouter, +) logger = logging.getLogger(__name__) @@ -28,3 +34,4 @@ def global_auth(request): api.add_router('/species/', SpeciesRouter) api.add_router('/grts/', GRTSCellsRouter) api.add_router('/guano/', GuanoMetadataRouter) +api.add_router('/recording-annotation/', RecordingAnnotationRouter) diff --git a/bats_ai/core/migrations/0011_alter_annotations_options_annotations_confidence_and_more.py b/bats_ai/core/migrations/0011_alter_annotations_options_annotations_confidence_and_more.py new file mode 100644 index 0000000..cfbcf66 --- /dev/null +++ b/bats_ai/core/migrations/0011_alter_annotations_options_annotations_confidence_and_more.py @@ -0,0 +1,100 @@ +# Generated by Django 4.1.13 on 2024-12-02 15:22 + +from django.conf import settings +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import django_extensions.db.fields + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('core', '0010_compressedspectrogram'), + ] + + operations = [ + migrations.AlterModelOptions( + name='annotations', + options={'get_latest_by': 'modified'}, + ), + migrations.AddField( + model_name='annotations', + name='confidence', + field=models.FloatField(blank=True, null=True), + ), + migrations.AddField( + model_name='annotations', + name='created', + field=django_extensions.db.fields.CreationDateTimeField( + auto_now_add=True, default=django.utils.timezone.now, verbose_name='created' + ), + preserve_default=False, + ), + migrations.AddField( + model_name='annotations', + name='model', + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name='annotations', + name='modified', + field=django_extensions.db.fields.ModificationDateTimeField( + auto_now=True, verbose_name='modified' + ), + ), + migrations.CreateModel( + name='RecordingAnnotation', + fields=[ + ( + 'id', + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name='ID' + ), + ), + ( + 'created', + django_extensions.db.fields.CreationDateTimeField( + auto_now_add=True, verbose_name='created' + ), + ), + ( + 'modified', + django_extensions.db.fields.ModificationDateTimeField( + auto_now=True, verbose_name='modified' + ), + ), + ('comments', models.TextField(blank=True, null=True)), + ('model', models.TextField(blank=True, null=True)), + ( + 'confidence', + models.FloatField( + default=1.0, + help_text='A confidence value between 0 and 1.0, default is 1.0.', + validators=[ + django.core.validators.MinValueValidator(0.0), + django.core.validators.MaxValueValidator(1.0), + ], + ), + ), + ( + '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')), + ], + options={ + 'get_latest_by': 'modified', + 'abstract': False, + }, + ), + ] diff --git a/bats_ai/core/models/__init__.py b/bats_ai/core/models/__init__.py index 36e2f0f..f53dc0b 100644 --- a/bats_ai/core/models/__init__.py +++ b/bats_ai/core/models/__init__.py @@ -3,6 +3,7 @@ from .grts_cells import GRTSCells from .image import Image from .recording import Recording, colormap +from .recording_annotation import RecordingAnnotation from .recording_annotation_status import RecordingAnnotationStatus from .species import Species from .spectrogram import Spectrogram @@ -19,4 +20,5 @@ 'GRTSCells', 'colormap', 'CompressedSpectrogram', + 'RecordingAnnotation', ] diff --git a/bats_ai/core/models/annotations.py b/bats_ai/core/models/annotations.py index 634c6b3..6028ffb 100644 --- a/bats_ai/core/models/annotations.py +++ b/bats_ai/core/models/annotations.py @@ -1,11 +1,13 @@ from django.contrib.auth.models import User +from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models +from django_extensions.db.models import TimeStampedModel from .recording import Recording from .species import Species -class Annotations(models.Model): +class Annotations(TimeStampedModel, 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) @@ -15,3 +17,12 @@ class Annotations(models.Model): type = models.TextField(blank=True, null=True) species = models.ManyToManyField(Species) comments = models.TextField(blank=True, null=True) + model = models.TextField(blank=True, null=True) # AI Model information if inference used + confidence = models.FloatField( + default=1.0, + validators=[ + MinValueValidator(0.0), + MaxValueValidator(1.0), + ], + help_text='A confidence value between 0 and 1.0, default is 1.0.', + ) diff --git a/bats_ai/core/models/recording_annotation.py b/bats_ai/core/models/recording_annotation.py index f458247..3429560 100644 --- a/bats_ai/core/models/recording_annotation.py +++ b/bats_ai/core/models/recording_annotation.py @@ -1,12 +1,23 @@ from django.contrib.auth.models import User +from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models +from django_extensions.db.models import TimeStampedModel from .recording import Recording from .species import Species -class RecordingAnnotation(models.Model): +class RecordingAnnotation(TimeStampedModel, models.Model): recording = models.ForeignKey(Recording, on_delete=models.CASCADE) owner = models.ForeignKey(User, on_delete=models.CASCADE) species = models.ManyToManyField(Species) comments = models.TextField(blank=True, null=True) + model = models.TextField(blank=True, null=True) # AI Model information if inference used + confidence = models.FloatField( + default=1.0, + validators=[ + MinValueValidator(0.0), + MaxValueValidator(1.0), + ], + help_text='A confidence value between 0 and 1.0, default is 1.0.', + ) diff --git a/bats_ai/core/views/__init__.py b/bats_ai/core/views/__init__.py index 90163e3..348849c 100644 --- a/bats_ai/core/views/__init__.py +++ b/bats_ai/core/views/__init__.py @@ -2,6 +2,7 @@ from .grts_cells import router as GRTSCellsRouter from .guanometadata import router as GuanoMetadataRouter from .recording import router as RecordingRouter +from .recording_annotation import router as RecordingAnnotationRouter from .species import router as SpeciesRouter from .temporal_annotations import router as TemporalAnnotationRouter @@ -12,4 +13,5 @@ 'TemporalAnnotationRouter', 'GRTSCellsRouter', 'GuanoMetadataRouter', + 'RecordingAnnotationRouter', ] diff --git a/bats_ai/core/views/recording.py b/bats_ai/core/views/recording.py index 7699cc8..e9a668a 100644 --- a/bats_ai/core/views/recording.py +++ b/bats_ai/core/views/recording.py @@ -15,6 +15,7 @@ Annotations, CompressedSpectrogram, Recording, + RecordingAnnotation, Species, TemporalAnnotations, colormap, @@ -215,6 +216,8 @@ def get_recordings(request: HttpRequest, public: bool | None = None): # TODO with larger dataset it may be better to do this in a queryset instead of python for recording in recordings: user = User.objects.get(id=recording['owner_id']) + fileAnnotations = RecordingAnnotation.objects.filter(recording=recording['id']) + recording['fileAnnotations'] = fileAnnotations recording['owner_username'] = user.username recording['audio_file_presigned_url'] = default_storage.url(recording['audio_file']) recording['hasSpectrogram'] = Recording.objects.get(id=recording['id']).has_spectrogram @@ -260,6 +263,8 @@ def get_recording(request: HttpRequest, id: int): recording_id=recording['id'], owner=request.user ).exists() recording['userMadeAnnotations'] = user_has_annotations + annotations = RecordingAnnotation.objects.filter(recording=id).annotate('confidence') + recording['fileAnnotations'] = annotations return recording else: diff --git a/bats_ai/core/views/recording_annotation.py b/bats_ai/core/views/recording_annotation.py new file mode 100644 index 0000000..3452e05 --- /dev/null +++ b/bats_ai/core/views/recording_annotation.py @@ -0,0 +1,140 @@ +import logging + +from django.http import HttpRequest +from ninja import Router, Schema +from ninja.errors import HttpError + +from bats_ai.core.models import Recording, RecordingAnnotation, Species + +logger = logging.getLogger(__name__) + +router = Router() + + +# Schemas for serialization +class RecordingAnnotationSchema(Schema): + id: int + recording: int + owner: str + species: list[int] + comments: str = None + model: str = None + confidence: float + + +class CreateRecordingAnnotationSchema(Schema): + recording: int + species: list[int] + comments: str = None + model: str = None + confidence: float + + +class UpdateRecordingAnnotationSchema(Schema): + species: list[int] = None + comments: str = None + model: str = None + confidence: float = None + + +# GET Endpoint +@router.get('/{id}', response=RecordingAnnotationSchema) +def get_recording_annotation(request: HttpRequest, id: int): + try: + annotation = RecordingAnnotation.objects.get(pk=id) + + # Check permission + if annotation.recording.owner != request.user and not annotation.recording.public: + raise HttpError(403, 'Permission denied.') + + return { + 'id': annotation.id, + 'recording': annotation.recording.id, + 'owner': annotation.owner.username, + 'species': [s.id for s in annotation.species.all()], + 'comments': annotation.comments, + 'model': annotation.model, + 'confidence': annotation.confidence, + } + except RecordingAnnotation.DoesNotExist: + raise HttpError(404, 'Recording annotation not found.') + + +# PUT Endpoint +@router.put('/', response={200: str}) +def create_recording_annotation(request: HttpRequest, data: CreateRecordingAnnotationSchema): + try: + recording = Recording.objects.get(pk=data.recording) + + # Check permission + if recording.owner != request.user and not recording.public: + raise HttpError(403, 'Permission denied.') + + # Create the recording annotation + annotation = RecordingAnnotation.objects.create( + recording=recording, + owner=request.user, + comments=data.comments, + model=data.model, + confidence=data.confidence, + ) + + # Add species + for species_id in data.species: + species = Species.objects.get(pk=species_id) + annotation.species.add(species) + + return 'Recording annotation created successfully.' + except Recording.DoesNotExist: + raise HttpError(404, 'Recording not found.') + except Species.DoesNotExist: + raise HttpError(404, 'One or more species IDs not found.') + + +# PATCH Endpoint +@router.patch('/{id}', response={200: str}) +def update_recording_annotation( + request: HttpRequest, id: int, data: UpdateRecordingAnnotationSchema +): + try: + annotation = RecordingAnnotation.objects.get(pk=id) + + # Check permission + if annotation.recording.owner != request.user: + raise HttpError(403, 'Permission denied.') + + # Update fields if provided + if data.comments is not None: + annotation.comments = data.comments + if data.model is not None: + annotation.model = data.model + if data.confidence is not None: + annotation.confidence = data.confidence + if data.species is not None: + annotation.species.clear() # Clear existing species + for species_id in data.species: + species = Species.objects.get(pk=species_id) + annotation.species.add(species) + + annotation.save() + return 'Recording annotation updated successfully.' + except RecordingAnnotation.DoesNotExist: + raise HttpError(404, 'Recording annotation not found.') + except Species.DoesNotExist: + raise HttpError(404, 'One or more species IDs not found.') + + +# DELETE Endpoint +@router.delete('/{id}', response={200: str}) +def delete_recording_annotation(request: HttpRequest, id: int): + try: + annotation = RecordingAnnotation.objects.get(pk=id) + + # Check permission + if annotation.recording.owner != request.user: + raise HttpError(403, 'Permission denied.') + + annotation.delete() + return 'Recording annotation deleted successfully.' + except RecordingAnnotation.DoesNotExist: + raise HttpError(404, 'Recording annotation not found.') From 1d4a0507cd9de9a3c7864fd952e9d7ac515f2f00 Mon Sep 17 00:00:00 2001 From: Bryon Lewis Date: Mon, 2 Dec 2024 15:22:57 -0500 Subject: [PATCH 03/11] client-side updates --- .../0012_alter_annotations_confidence.py | 25 ++ bats_ai/core/views/recording.py | 36 +- client/package.json | 2 +- client/src/App.vue | 4 +- client/src/components/AnnotationEditor.vue | 74 ++-- client/src/components/AnnotationList.vue | 317 +++++++++--------- client/src/components/RecordingAnnotation.vue | 0 client/src/use/useState.ts | 2 + client/src/views/Spectrogram.vue | 89 +++-- 9 files changed, 335 insertions(+), 214 deletions(-) create mode 100644 bats_ai/core/migrations/0012_alter_annotations_confidence.py create mode 100644 client/src/components/RecordingAnnotation.vue diff --git a/bats_ai/core/migrations/0012_alter_annotations_confidence.py b/bats_ai/core/migrations/0012_alter_annotations_confidence.py new file mode 100644 index 0000000..f9c75a0 --- /dev/null +++ b/bats_ai/core/migrations/0012_alter_annotations_confidence.py @@ -0,0 +1,25 @@ +# Generated by Django 4.1.13 on 2024-12-02 16:31 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('core', '0011_alter_annotations_options_annotations_confidence_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='annotations', + name='confidence', + field=models.FloatField( + default=1.0, + help_text='A confidence value between 0 and 1.0, default is 1.0.', + validators=[ + django.core.validators.MinValueValidator(0.0), + django.core.validators.MaxValueValidator(1.0), + ], + ), + ), + ] diff --git a/bats_ai/core/views/recording.py b/bats_ai/core/views/recording.py index e9a668a..71f559a 100644 --- a/bats_ai/core/views/recording.py +++ b/bats_ai/core/views/recording.py @@ -62,6 +62,26 @@ class RecordingUploadSchema(Schema): unusual_occurrences: str = None +class RecordingAnnotationSchema(Schema): + # species: list[SpeciesSchema] | None + comments: str | None = None + model: str | None = None + owner: str + confidence: float + id: int | None = None + + @classmethod + def from_orm(cls, obj: RecordingAnnotation, **kwargs): + return cls( + # species=[SpeciesSchema.from_orm(species) for species in obj.species.all()], + owner=obj.owner.username, + confidence=obj.confidence, + comments=obj.comments, + model=obj.model, + id=obj.pk, + ) + + class AnnotationSchema(Schema): start_time: int end_time: int @@ -74,7 +94,7 @@ class AnnotationSchema(Schema): owner_email: str = None @classmethod - def from_orm(cls, obj, owner_email=None, **kwargs): + def from_orm(cls, obj: Annotations, owner_email=None, **kwargs): return cls( start_time=obj.start_time, end_time=obj.end_time, @@ -217,7 +237,10 @@ def get_recordings(request: HttpRequest, public: bool | None = None): for recording in recordings: user = User.objects.get(id=recording['owner_id']) fileAnnotations = RecordingAnnotation.objects.filter(recording=recording['id']) - recording['fileAnnotations'] = fileAnnotations + recording['fileAnnotations'] = [ + RecordingAnnotationSchema.from_orm(fileAnnotation).dict() + for fileAnnotation in fileAnnotations + ] recording['owner_username'] = user.username recording['audio_file_presigned_url'] = default_storage.url(recording['audio_file']) recording['hasSpectrogram'] = Recording.objects.get(id=recording['id']).has_spectrogram @@ -263,8 +286,13 @@ def get_recording(request: HttpRequest, id: int): recording_id=recording['id'], owner=request.user ).exists() recording['userMadeAnnotations'] = user_has_annotations - annotations = RecordingAnnotation.objects.filter(recording=id).annotate('confidence') - recording['fileAnnotations'] = annotations + fileAnnotations = RecordingAnnotation.objects.filter(recording=id).order_by( + 'confidence' + ) + recording['fileAnnotations'] = [ + RecordingAnnotationSchema.from_orm(fileAnnotation).dict() + for fileAnnotation in fileAnnotations + ] return recording else: diff --git a/client/package.json b/client/package.json index 7237695..ef94d4f 100644 --- a/client/package.json +++ b/client/package.json @@ -1,5 +1,5 @@ { - "name": "vue-project-template", + "name": "bat-ai-client", "version": "0.1.0", "private": true, "scripts": { diff --git a/client/src/App.vue b/client/src/App.vue index c2fb841..df706fa 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -12,7 +12,7 @@ export default defineComponent({ const oauthClient = inject("oauthClient"); const router = useRouter(); const route = useRoute(); - const { nextShared, sharedList } = useState(); + const { nextShared, sharedList, sideTab, } = useState(); const getShared = async () => { sharedList.value = (await getRecordings(true)).data; }; @@ -57,7 +57,7 @@ export default defineComponent({ } }); - return { oauthClient, containsSpectro, loginText, logInOrOut, activeTab, nextShared }; + return { oauthClient, containsSpectro, loginText, logInOrOut, activeTab, nextShared, sideTab }; }, }); diff --git a/client/src/components/AnnotationEditor.vue b/client/src/components/AnnotationEditor.vue index 40cc2a3..f516fae 100644 --- a/client/src/components/AnnotationEditor.vue +++ b/client/src/components/AnnotationEditor.vue @@ -115,43 +115,45 @@ export default defineComponent({ - - - - - - - - - + + + + + + + + + + - - - + + + + diff --git a/client/src/components/AnnotationList.vue b/client/src/components/AnnotationList.vue index 6832295..cd4ac67 100644 --- a/client/src/components/AnnotationList.vue +++ b/client/src/components/AnnotationList.vue @@ -3,22 +3,34 @@ import { defineComponent, PropType } from "vue"; import { SpectroInfo } from './geoJS/geoJSUtils'; import useState from "../use/useState"; import { watch, ref } from "vue"; -import RecordingList from "./RecordingList.vue"; - +import AnnotationEditor from "./AnnotationEditor.vue"; +import { Species, SpectrogramAnnotation, SpectrogramTemporalAnnotation } from "../api/api"; export default defineComponent({ name: "AnnotationList", components: { - RecordingList, + AnnotationEditor, }, props: { spectroInfo: { type: Object as PropType, default: () => undefined, }, + selectedAnnotation: { + type: Object as PropType, + default: () => null, + }, + species: { + type: Array as PropType, + required: true, + }, + recordingId: { + type: String, + required: true, + } }, - emits: ['select'], + emits: ['select', 'update:annotation', 'delete:annotation'], setup() { - const { creationType, annotationState, setAnnotationState, annotations, temporalAnnotations, selectedId, selectedType, setSelectedId } = useState(); + const { creationType, annotationState, setAnnotationState, annotations, temporalAnnotations, selectedId, selectedType, setSelectedId, sideTab } = useState(); const tab = ref('pulse'); const scrollToId = (id: number) => { const el = document.getElementById(`annotation-${id}`); @@ -37,7 +49,7 @@ export default defineComponent({ }); // eslint-disable-next-line @typescript-eslint/no-explicit-any const tabSwitch = (event: any) => { - // On tab switches we want to deselect the curret anntation + // On tab switches we want to deselect the curret annotation if (['sequence', 'pulse'].includes(event)) { tab.value = event as 'sequence' | 'pulse'; selectedType.value = event as 'sequence' | 'pulse'; @@ -58,164 +70,167 @@ export default defineComponent({ setSelectedId, tabSwitch, tab, + sideTab, }; }, }); diff --git a/client/src/components/RecordingAnnotations.vue b/client/src/components/RecordingAnnotations.vue new file mode 100644 index 0000000..d229bce --- /dev/null +++ b/client/src/components/RecordingAnnotations.vue @@ -0,0 +1,187 @@ + + + + + diff --git a/client/src/views/Spectrogram.vue b/client/src/views/Spectrogram.vue index b1fda64..cda5543 100644 --- a/client/src/views/Spectrogram.vue +++ b/client/src/views/Spectrogram.vue @@ -558,6 +558,8 @@ export default defineComponent({ :annotations="annotations" :temporal-annotations="temporalAnnotations" :selected-annotation="selectedAnnotation" + :species="speciesList" + :recording-id="id" @select="processSelection($event)" @update:annotation="getAnnotationsList()" @delete:annotation=" From 847663d0eaa4b26a31fb0e30b76276ad03ed237f Mon Sep 17 00:00:00 2001 From: Bryon Lewis Date: Thu, 5 Dec 2024 10:41:27 -0500 Subject: [PATCH 05/11] basic adding/editing user based file level annotations --- client/src/api/api.ts | 6 +- client/src/components/AnnotationList.vue | 2 +- .../components/RecordingAnnotationEditor.vue | 29 +- .../src/components/RecordingAnnotations.vue | 72 +- client/yarn.lock | 4939 ----------------- 5 files changed, 66 insertions(+), 4982 deletions(-) delete mode 100644 client/yarn.lock diff --git a/client/src/api/api.ts b/client/src/api/api.ts index ff5b150..858f54d 100644 --- a/client/src/api/api.ts +++ b/client/src/api/api.ts @@ -119,7 +119,7 @@ export interface FileAnnotation { export interface UpdateFileAnnotation { recordingId?: number; - species_list: number[] | null; + species: number[] | null; comments?: string; model?: string; confidence: number; @@ -336,11 +336,11 @@ async function getFileAnnotations(recordingId: number) { async function putFileAnnotation(fileAnnotation: UpdateFileAnnotation) { - return axiosInstance.put<{message: string, id: number}>(`/recording-annotation`, { fileAnnotation }); + return axiosInstance.put<{message: string, id: number}>(`/recording-annotation/`, { ...fileAnnotation }); } async function patchFileAnnotation(fileAnnotationId: number, fileAnnotation: UpdateFileAnnotation) { - return axiosInstance.patch<{message: string, id: number}>(`/recording-annotation/${fileAnnotationId}`, { fileAnnotation }); + return axiosInstance.patch<{message: string, id: number}>(`/recording-annotation/${fileAnnotationId}`, { ...fileAnnotation }); } async function deleteFileAnnotation(fileAnnotationId: number) { diff --git a/client/src/components/AnnotationList.vue b/client/src/components/AnnotationList.vue index c1e648d..1f9b55f 100644 --- a/client/src/components/AnnotationList.vue +++ b/client/src/components/AnnotationList.vue @@ -33,7 +33,7 @@ export default defineComponent({ emits: ['select', 'update:annotation', 'delete:annotation'], setup() { const { creationType, annotationState, setAnnotationState, annotations, temporalAnnotations, selectedId, selectedType, setSelectedId, sideTab } = useState(); - const tab = ref('pulse'); + const tab = ref('recording'); const scrollToId = (id: number) => { const el = document.getElementById(`annotation-${id}`); if (el) { diff --git a/client/src/components/RecordingAnnotationEditor.vue b/client/src/components/RecordingAnnotationEditor.vue index 9dfd27d..bd1aea6 100644 --- a/client/src/components/RecordingAnnotationEditor.vue +++ b/client/src/components/RecordingAnnotationEditor.vue @@ -1,7 +1,7 @@ + + + + diff --git a/client/src/views/Recordings.vue b/client/src/views/Recordings.vue index 9275072..50fa104 100644 --- a/client/src/views/Recordings.vue +++ b/client/src/views/Recordings.vue @@ -6,12 +6,14 @@ import MapLocation from '../components/MapLocation.vue'; import useState from '../use/useState'; import BatchUploadRecording from '../components/BatchUploadRecording.vue'; import RecordingInfoDisplay from '../components/RecordingInfoDisplay.vue'; +import RecordingAnnotationSummary from '../components/RecordingAnnotationSummary.vue'; export default defineComponent({ components: { UploadRecording, MapLocation, BatchUploadRecording, - RecordingInfoDisplay + RecordingInfoDisplay, + RecordingAnnotationSummary, }, setup() { const itemsPerPage = ref(-1); @@ -26,11 +28,14 @@ export default defineComponent({ title:'Edit', key:'edit', }, - { title:'Name', key:'name', }, + { + title: 'Annotation', + key:'annotation' + }, { title:'Owner', key:'owner_username', @@ -39,10 +44,6 @@ export default defineComponent({ title:'Recorded Date', key:'recorded_date', }, - { - title:'Recorded Time', - key:'recorded_time', - }, { title:'Public', key:'public', @@ -71,6 +72,10 @@ export default defineComponent({ title:'Name', key:'name', }, + { + title: 'Annotation', + key:'annotation' + }, { title:'Owner', key:'owner_username', @@ -79,10 +84,6 @@ export default defineComponent({ title:'Recorded Date', key:'recorded_date', }, - { - title:'Recorded Time', - key:'recorded_time', - }, { title:'Public', key:'public', @@ -251,6 +252,14 @@ export default defineComponent({ + + + + + +