Skip to content

Commit

Permalink
- WIP adding per group custom labels
Browse files Browse the repository at this point in the history
  • Loading branch information
hardiesoft committed Feb 10, 2025
1 parent 7ed7d01 commit bf31c14
Show file tree
Hide file tree
Showing 9 changed files with 380 additions and 41 deletions.
2 changes: 1 addition & 1 deletion browse-next/src/components/CardTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
v-bind="{ cell, row: sortedItems[rowIndex] }"
>
<span
v-if="cell && cell.value"
v-if="cell && cell.value !== undefined"
class="text-nowrap"
v-html="cell.value"
/>
Expand Down
67 changes: 48 additions & 19 deletions browse-next/src/components/RecordingViewLabels.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ import { CurrentUser } from "@models/LoggedInUser";
import type { TagId } from "@typedefs/api/common";
import CardTable from "@/components/CardTable.vue";
import { DateTime } from "luxon";
import { RecordingLabels } from "@/consts";
import {
AudioRecordingLabels,
CameraRecordingLabels,
CommonRecordingLabels,
} from "@/consts";
import type { RecordingLabel } from "@typedefs/api/group";
import { RecordingType } from "@typedefs/api/consts.ts";
const props = withDefaults(
defineProps<{
Expand All @@ -28,7 +34,7 @@ const tableItems = computed<CardTableRows<ApiRecordingTagResponse | string>>(
return (props.recording?.tags || []).map(
(tag: ApiRecordingTagResponse) => ({
label:
labels.find((label) => label.value === tag.detail)?.text ||
labels.value.find((label) => label.value === tag.detail)?.text ||
tag.detail,
by: tag.taggerName || (tag.automatic ? "Cacophony AI" : "-"),
when: DateTime.fromJSDate(new Date(tag.createdAt)).toRelative({
Expand All @@ -41,31 +47,54 @@ const tableItems = computed<CardTableRows<ApiRecordingTagResponse | string>>(
}
);
interface Label {
text: string;
value: string;
description: string;
}
// TODO - Group-level defined labels created by group admin.
const labels: Label[] = RecordingLabels.map(({ text, description, value }) => ({
text,
description,
value: (value || text).toLowerCase(),
}));
const cameraLabels = computed<RecordingLabel[]>(() => {
return [...CommonRecordingLabels, ...CameraRecordingLabels].map(
({ text, description, value }) => ({
text: text,
description,
value: (value || text).toLowerCase(),
})
);
});
const audioLabels = computed<RecordingLabel[]>(() => {
return [...CommonRecordingLabels, ...AudioRecordingLabels].map(
({ text, description, value }) =>
({
text: text,
description,
value: (value || text).toLowerCase(),
} as RecordingLabel)
);
});
const labels = computed<RecordingLabel[]>(() => {
if (recordingTypeIsAudio.value) {
return audioLabels.value;
}
return cameraLabels.value;
});
const recordingTypeIsAudio = computed<boolean>(() => {
if (props.recording) {
return props.recording.type === RecordingType.Audio;
}
return false;
});
const unusedLabels = computed(() => {
// Filter out labels that have already been added
return labels.filter(
return labels.value.filter(
(label) => !props.recording?.tags.some((tag) => tag.detail === label.value)
);
});
const selectedLabel = ref<string>("");
const labelToAdd = computed<Label | null>(() => {
const labelToAdd = computed<RecordingLabel | null>(() => {
return (
(selectedLabel.value !== "" &&
labels.find((label) => label.value === selectedLabel.value)) ||
labels.value.find((label) => label.value === selectedLabel.value)) ||
null
);
});
Expand Down Expand Up @@ -97,14 +126,14 @@ const doAddLabel = async () => {
props.recording.id,
selectedLabel.value
);
if (addLabelResponse.success) {
if (addLabelResponse.success && CurrentUser.value) {
// Emit tag change event, patch upstream recording.
emit("added-recording-label", {
id: addLabelResponse.result.tagId,
detail: selectedLabel.value,
confidence: 0.9,
taggerName: CurrentUser.value?.userName,
taggerId: CurrentUser.value?.id,
taggerName: CurrentUser.value.userName,
taggerId: CurrentUser.value.id,
createdAt: new Date().toISOString(),
});
selectedLabel.value = "";
Expand Down
23 changes: 13 additions & 10 deletions browse-next/src/components/SpectrogramViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,8 @@ const selectTrackRegion = (track: ApiTrackResponse) => {
if (overlayCanvasContext.value) {
renderOverlay(overlayCanvasContext.value);
if (track.id !== -1) {
const trackStartZeroOne = track.start / audioDuration.value;
const trackEndZeroOne = track.end / audioDuration.value;
const trackStartZeroOne = Math.max(0, track.start / audioDuration.value);
const trackEndZeroOne = Math.min(1, track.end / audioDuration.value);
if (spectastiqEl.value && track.hasOwnProperty("maxFreq")) {
const minFreqZeroOne =
Math.max(0, track.minFreq || 0) / audioSampleRate.value;
Expand Down Expand Up @@ -492,19 +492,22 @@ const renderOverlay = (ctx: CanvasRenderingContext2D) => {
ctx.clearRect(0, 0, cWidth, cHeight);
if (props.recording) {
for (const track of tracksIntermediate.value) {
const minFreq =
const minFreqZeroOne =
1 - Math.max(0, track.minFreq || 0) / audioSampleRate.value;
const maxFreq =
const maxFreqZeroOne =
1 -
Math.min(track.maxFreq || 0, audioSampleRate.value) /
audioSampleRate.value;
const trackStart = track.start / audioDuration.value;
const trackEnd = track.end / audioDuration.value;
const trackStartZeroOne = Math.max(
0,
track.start / audioDuration.value
);
const trackEndZeroOne = Math.min(1, track.end / audioDuration.value);
const bounds = getTrackBounds(cWidth, cHeight, begin, end, [
trackStart,
maxFreq,
trackEnd,
minFreq,
trackStartZeroOne,
maxFreqZeroOne,
trackEndZeroOne,
minFreqZeroOne,
]);
drawRectWithText(
ctx,
Expand Down
21 changes: 12 additions & 9 deletions browse-next/src/consts.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { RecordingLabel } from "@typedefs/api/group";

export const TagColours = [
{ background: "#32ff7e", foreground: "dark" },
{ background: "#4bcffa", foreground: "dark" },
Expand Down Expand Up @@ -31,18 +33,20 @@ export const DEFAULT_AUDIO_TAGS = [
"unidentified",
];

export const RecordingLabels: {
text: string;
value?: string;
description: string;
}[] = [
{ text: "Cool", description: "Mark this as a cool or interesting recording" },
export const CommonRecordingLabels: RecordingLabel[] = [
{
text: "Cool",
description: "Mark this as a cool or interesting recording",
},
{
text: "Flagged for review",
value: "requires review",
description:
"Flag this recording for review due to low confidence IDing track(s)",
},
];

export const CameraRecordingLabels: RecordingLabel[] = [
{
text: "Animal in trap",
value: "trapped in trap",
Expand All @@ -69,14 +73,13 @@ export const RecordingLabels: {
},
{
text: "Outside",
description: "Outside",
},
{
text: "Inside",
description: "Inside",
},
{
text: "Incursion",
description: "Incursion",
},
];

export const AudioRecordingLabels: RecordingLabel[] = [];
9 changes: 8 additions & 1 deletion browse-next/src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,10 +314,17 @@ const router = createRouter({
{
name: "project-tag-settings",
path: "tag-settings",
meta: { title: "Tag preferences for :projectName" },
meta: { title: "Tag and label settings for :projectName" },
component: () =>
import("@/views/ManageProjectTagSettingsSubView.vue"),
},
{
name: "project-label-settings",
path: "label-settings",
meta: { title: "Label settings for :projectName" },
component: () =>
import("@/views/ManageProjectLabelSettingsSubView.vue"),
},
{
name: "project-misc-settings",
path: "project-settings",
Expand Down
Loading

0 comments on commit bf31c14

Please sign in to comment.