diff --git a/bats_ai/core/views/recording_tag.py b/bats_ai/core/views/recording_tag.py index 3ab4a175..d9d512d1 100644 --- a/bats_ai/core/views/recording_tag.py +++ b/bats_ai/core/views/recording_tag.py @@ -1,8 +1,9 @@ from __future__ import annotations +from django.db.models import Q from django.http import Http404, HttpRequest from ninja import Schema -from ninja.pagination import RouterPaginated +from ninja.pagination import Router from bats_ai.core.models import RecordingTag @@ -12,12 +13,16 @@ class RecordingTagSchema(Schema): user: int -router = RouterPaginated() +router = Router() -@router.get("/") +@router.get("/", response=list[str]) def get_recording_tags(request: HttpRequest): - user = request.user - if not user: + if not request.user: return Http404() - return list(RecordingTag.objects.filter(user=request.user).values()) + # User's own tags + tags on public recordings + return list( + RecordingTag.objects.filter(Q(user=request.user) | Q(recording__public=True)) + .values_list("text", flat=True) + .distinct() + ) diff --git a/client/src/api/api.ts b/client/src/api/api.ts index 907df0ab..b479d2b5 100644 --- a/client/src/api/api.ts +++ b/client/src/api/api.ts @@ -273,11 +273,6 @@ interface GRTSCellBbox { }; } -export interface RecordingTag { - id: number; - text: string; - user_id: number; -} /** Params for paginated recording list (v-data-table-server compatible). */ export interface RecordingListParams { @@ -358,7 +353,7 @@ async function getUnsubmittedNeighbors( } async function getRecordingTags() { - return axiosInstance.get(`/recording-tag/`); + return axiosInstance.get(`/recording-tag/`); } async function deleteRecording(id: number) { diff --git a/client/src/components/BatchRecordingElement.vue b/client/src/components/BatchRecordingElement.vue index 8f8e1c0f..3ac9a8fb 100644 --- a/client/src/components/BatchRecordingElement.vue +++ b/client/src/components/BatchRecordingElement.vue @@ -36,7 +36,7 @@ export default defineComponent({ emits: ["done", "cancel", "update", "delete"], setup(props, { emit }) { const { recordingTagList } = useState(); - const tagOptions = computed(() => recordingTagList.value.map((tag) => tag.text)); + const tagOptions = computed(() => [...recordingTagList.value]); const initialTags = props.editing ? props.editing.tags : []; const currentTags: Ref = ref(initialTags); const dateAdapter = useDate(); diff --git a/client/src/components/BatchUploadRecording.vue b/client/src/components/BatchUploadRecording.vue index c92fe6c4..40c15e5b 100644 --- a/client/src/components/BatchUploadRecording.vue +++ b/client/src/components/BatchUploadRecording.vue @@ -2,7 +2,7 @@ import { computed, defineComponent, ref, Ref, watch } from 'vue'; import { RecordingMimeTypes } from '../constants'; import useRequest from '@use/useRequest'; -import { UploadLocation, uploadRecordingFile, getCellLocation, RecordingFileParameters, getGuanoMetadata, RecordingTag } from '../api/api'; +import { UploadLocation, uploadRecordingFile, getCellLocation, RecordingFileParameters, getGuanoMetadata } from '../api/api'; import BatchRecordingElement, { BatchRecording } from './BatchRecordingElement.vue'; import { cloneDeep } from 'lodash'; import { extractDateTimeComponents, getCurrentTime } from '@use/useUtils'; @@ -24,7 +24,7 @@ export default defineComponent({ emits: ['done', 'cancel'], setup(props, { emit }) { const { recordingTagList } = useState(); - const tagOptions = computed(() => recordingTagList.value.map((tag: RecordingTag) => tag.text)); + const tagOptions = computed(() => [...recordingTagList.value]); const fileInputEl: Ref = ref(null); const fileModel: Ref = ref(); diff --git a/client/src/components/RecordingTable.vue b/client/src/components/RecordingTable.vue new file mode 100644 index 00000000..56fa8e3d --- /dev/null +++ b/client/src/components/RecordingTable.vue @@ -0,0 +1,653 @@ + + + + + diff --git a/client/src/components/UploadRecording.vue b/client/src/components/UploadRecording.vue index 0082324c..a2ee4fb9 100644 --- a/client/src/components/UploadRecording.vue +++ b/client/src/components/UploadRecording.vue @@ -8,7 +8,7 @@ import { } from 'vue'; import { RecordingMimeTypes } from '../constants'; import useRequest from '@use/useRequest'; -import { UploadLocation, uploadRecordingFile, patchRecording, getCellLocation, getCellfromLocation, getGuanoMetadata, RecordingFileParameters, RecordingTag } from '../api/api'; +import { UploadLocation, uploadRecordingFile, patchRecording, getCellLocation, getCellfromLocation, getGuanoMetadata, RecordingFileParameters } from '../api/api'; import MapLocation from './MapLocation.vue'; import { useDate } from 'vuetify'; import { getCurrentTime, extractDateTimeComponents } from '@use/useUtils'; @@ -44,7 +44,7 @@ export default defineComponent({ emits: ['done', 'cancel'], setup(props, { emit }) { const { recordingTagList } = useState(); - const tagOptions = computed(() => recordingTagList.value.map((tag: RecordingTag) => tag.text)); + const tagOptions = computed(() => [...recordingTagList.value]); const initialTags = props.editing ? props.editing.tags : undefined; const currentTags: Ref = ref(initialTags || []); diff --git a/client/src/use/useState.ts b/client/src/use/useState.ts index 45425c00..7d100607 100644 --- a/client/src/use/useState.ts +++ b/client/src/use/useState.ts @@ -10,7 +10,6 @@ import { Recording, SpectrogramAnnotation, SpectrogramSequenceAnnotation, - RecordingTag, getComputedPulseContour, ComputedPulseContour, getVettingDetailsForUser, @@ -52,7 +51,7 @@ const otherUserAnnotations: Ref = ref({}); const sharedList: Ref = ref([]); const recordingList: Ref = ref([]); const currentRecordingId: Ref = ref(undefined); -const recordingTagList: Ref = ref([]); +const recordingTagList: Ref = ref([]); const nextShared: Ref = ref(false); const scaledVals: Ref<{ x: number; y: number }> = ref({ x: 1, y: 1 }); const viewCompressedOverlay = ref(false); @@ -116,6 +115,7 @@ const SHARED_FILTER_TAG_STORAGE_KEY = 'bataiSharedFilterTags'; const filterTags: Ref = ref([]); const sharedFilterTags: Ref = ref([]); +const showSubmittedRecordings = ref(false); /** Current spectrogram recording filename (for app bar display). Set by Spectrogram view, cleared when not on spectrogram. */ const spectrogramFilename: Ref = ref(""); @@ -196,8 +196,6 @@ export default function useState() { return router.currentRoute.value.fullPath.includes('nabat'); } - const showSubmittedRecordings = ref(false); - // Server filters by exclude_submitted when "Show submitted" is unchecked; we refetch on toggle. const myRecordingsDisplay = computed(() => recordingList.value); const sharedRecordingsDisplay = computed(() => sharedList.value); diff --git a/client/src/views/Recordings.vue b/client/src/views/Recordings.vue index aa499c41..3007cc48 100644 --- a/client/src/views/Recordings.vue +++ b/client/src/views/Recordings.vue @@ -1,290 +1,36 @@ @@ -406,13 +119,11 @@ export default defineComponent({ - + Upload Recording - - - Batch Upload - + + Batch Upload @@ -445,205 +156,13 @@ export default defineComponent({ - - - - - - - - - - - - - - - - - - - - -
- - Total: {{ totalMyCount }} recording{{ totalMyCount !== 1 ? 's' : '' }} - -
+ ref="myRecordingsTableRef" + variant="my" + :edit-recording="editRecording" + :open-delete-recording-dialog="openDeleteRecordingDialog" + /> -
- Shared -
+
Shared
- - - - - - - - - - - - - - - - - - -
- - Total: {{ totalSharedCount }} recording{{ totalSharedCount !== 1 ? 's' : '' }} - -
+