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, }; }, });