Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/audio-views' into reference-photos
Browse files Browse the repository at this point in the history
  • Loading branch information
Zainrax committed Feb 11, 2025
2 parents d10360c + 750f431 commit 829510e
Show file tree
Hide file tree
Showing 32 changed files with 1,352 additions and 196 deletions.
12 changes: 9 additions & 3 deletions api/api/V1/recordingsBulkQueryUtil.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { RecordingProcessingState } from "@typedefs/api/consts.js";
import { RecordingType, TagMode } from "@typedefs/api/consts.js";
import { TagMode, RecordingType } from "@typedefs/api/consts.js";
import type {
DeviceId,
GroupId,
Expand Down Expand Up @@ -40,7 +40,11 @@ export const getFirstPass = (
const isHumanOnlyTagMode = [TagMode.HumanOnly].includes(tagMode);
return {
where: {
...(includeDeletedRecordings ? {} : { deletedAt: { [Op.eq]: null } }),
...(includeDeletedRecordings
? {}
: {
deletedAt: { [Op.eq]: null },
}),
...(types.length !== 0 ? { type: { [Op.in]: types } } : {}),
...(processingState !== undefined ? { processingState } : {}),
...(devices.length !== 0 ? { DeviceId: { [Op.in]: devices } } : {}),
Expand All @@ -57,7 +61,9 @@ export const getFirstPass = (
: { recordingDateTime: { [Op.lt]: until } }
: {}),
GroupId: projectId,
...(types.includes(RecordingType.Audio) ? { redacted: false } : {}),
...(types.includes(RecordingType.Audio) && !includeFilteredTracks
? { redacted: false }
: {}),
duration: statusRecordingsOnly
? { [Op.and]: [{ [Op.lt]: 2.5 }, { [Op.gt]: 0.0 }] }
: { [Op.gte]: minDuration },
Expand Down
4 changes: 2 additions & 2 deletions api/classifications/classification.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"label": "all",
"version": "adfe5a24-89fc-42ff-bc36-d8b799f5a5ea",
"version": "fdfe5a24-89fc-42ff-ac36-d8b799f5a5ea",
"children": [
{
"label": "mammal",
Expand Down Expand Up @@ -38,7 +38,7 @@
{
"label": "human",
"aliases": ["person"],
"display": "human speech"
"display": "human/human speech"
},
{
"label": "kangaroo",
Expand Down
6 changes: 6 additions & 0 deletions api/scripts/project-activity-digest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ const allVisitsForProjectInTimespan = async (
}
if (!alreadySentNoActivityEmail) {
for (const visit of visits) {
if (visit.classification === "false-trigger") {
continue;
}
if (visit.classification === "none") {
visit.classification = "unidentified";
}
recordingData[visit.classification] =
recordingData[visit.classification] || 0;
recordingData[visit.classification] += 1;
Expand Down
3 changes: 3 additions & 0 deletions browse-next/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ declare module 'vue' {
BForm: typeof import('bootstrap-vue-next')['BForm']
BFormCheckbox: typeof import('bootstrap-vue-next')['BFormCheckbox']
BFormCheckboxGroup: typeof import('bootstrap-vue-next')['BFormCheckboxGroup']
BFormFloatingLabel: typeof import('bootstrap-vue-next')['BFormFloatingLabel']
BFormGroup: typeof import('bootstrap-vue-next')['BFormGroup']
BFormInput: typeof import('bootstrap-vue-next')['BFormInput']
BFormInvalidFeedback: typeof import('bootstrap-vue-next')['BFormInvalidFeedback']
BFormRadio: typeof import('bootstrap-vue-next')['BFormRadio']
BFormRadioGroup: typeof import('bootstrap-vue-next')['BFormRadioGroup']
BFormSelect: typeof import('bootstrap-vue-next')['BFormSelect']
BFormTextarea: typeof import('bootstrap-vue-next')['BFormTextarea']
BimodalSwitch: typeof import('./src/components/BimodalSwitch.vue')['default']
BInput: typeof import('bootstrap-vue-next')['BInput']
BLink: typeof import('bootstrap-vue-next')['BLink']
Expand Down Expand Up @@ -67,6 +69,7 @@ declare module 'vue' {
RecordingsList: typeof import('./src/components/RecordingsList.vue')['default']
RecordingViewActionButtons: typeof import('./src/components/RecordingViewActionButtons.vue')['default']
RecordingViewLabels: typeof import('./src/components/RecordingViewLabels.vue')['default']
RecordingViewNotes: typeof import('./src/components/RecordingViewNotes.vue')['default']
RecordingViewTracks: typeof import('./src/components/RecordingViewTracks.vue')['default']
RenameableLocationName: typeof import('./src/components/RenameableLocationName.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
Expand Down
6 changes: 3 additions & 3 deletions browse-next/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions browse-next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,19 @@
"leaflet": "^1.9.3",
"luxon": "^3.3.0",
"scale-color-perceptual": "^1.1.2",
"spectastiq": "github:hardiesoft/spectastiq#v0.9.4",
"suncalc": "^1.9.0",
"tesseract-wasm": "^0.10.0",
"tz-lookup-oss": "^6.3.0",
"vue": "^3.5.13",
"vue-router": "^4.5.0"
"vue-router": "^4.5.0",
"spectastiq": "github:hardiesoft/spectastiq#v0.9.5"
},
"devDependencies": {
"@eslint/compat": "^1.2.0",
"@eslint/js": "^9.12.0",
"@rushstack/eslint-patch": "^1.2.0",
"@stylistic/eslint-plugin": "^2.9.0",
"@tsconfig/node16": "^1.0.4",
"@tsconfig/node18": "^2.0.1",
"@types/leaflet": "^1.9.3",
"@types/luxon": "^3.2.0",
Expand All @@ -61,7 +62,6 @@
"unplugin-vue-components": "^0.26.0",
"vite": "^6.0.11",
"vite-plugin-eslint": "^1.8.1",
"vue-tsc": "^1.6.5",
"@tsconfig/node16": "^1.0.4"
"vue-tsc": "^1.6.5"
}
}
10 changes: 10 additions & 0 deletions browse-next/src/api/Recording.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ export const addRecordingLabel = (id: RecordingId, label: string) =>
confidence: 0.9,
},
}) as Promise<FetchResult<{ tagId: TagId }>>;

export const addRecordingNoteLabel = (id: RecordingId, note: string) =>
CacophonyApi.post(`/api/v1/recordings/${id}/tags`, {
tag: {
detail: "note",
comment: note,
confidence: 0.9,
},
}) as Promise<FetchResult<{ tagId: TagId }>>;

export const removeRecordingLabel = (id: RecordingId, tagId: TagId) =>
CacophonyApi.delete(`/api/v1/recordings/${id}/tags/${tagId}`) as Promise<
FetchResult<void>
Expand Down
20 changes: 16 additions & 4 deletions browse-next/src/components/ActivitySearchParameters.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import type { ApiGroupResponse as ApiProjectResponse } from "@typedefs/api/group
import type { ApiStationResponse as ApiLocationResponse } from "@typedefs/api/station";
import { timezoneForLatLng } from "@models/visitsUtils.ts";
import { canonicalLatLngForLocations } from "@/helpers/Location.ts";
import { RecordingLabels } from "@/consts.ts";
import { TagMode } from "@typedefs/api/consts.ts";
import {
ActivitySearchDisplayMode,
Expand All @@ -36,6 +35,10 @@ import type { ActivitySearchParams } from "@views/ActivitySearchView.vue";
import { type LocationQuery, useRoute, useRouter } from "vue-router";
import { useElementBounding } from "@vueuse/core";
import { urlNormaliseName } from "@/utils.ts";
import {
CurrentProjectAudioLabels,
CurrentProjectCameraLabels,
} from "@/helpers/Project.ts";
const props = defineProps<{
locations: Ref<LoadedResource<ApiLocationResponse[]>>;
Expand All @@ -62,20 +65,27 @@ const currentProject = inject(currentActiveProject) as ComputedRef<
const availableProjects = inject(userProjects) as Ref<
LoadedResource<ApiProjectResponse[]>
>;
const availableLabels = computed(() => {
const labels = RecordingLabels.slice(2).map(({ text, value }) => ({
let labelSource;
if (recordingMode.value === ActivitySearchRecordingMode.Cameras) {
labelSource = CurrentProjectCameraLabels.value;
} else {
labelSource = CurrentProjectAudioLabels.value;
}
const labels = labelSource.slice(2).map(({ text, value }) => ({
label: text,
value: (value || text).toLowerCase(),
}));
if (selectedCoolLabel.value) {
const label = RecordingLabels[0];
const label = labelSource[0];
labels.push({
label: label.text,
value: (label.value || label.text).toLowerCase(),
});
}
if (selectedFlaggedLabel.value) {
const label = RecordingLabels[1];
const label = labelSource[1];
labels.push({
label: label.text,
value: (label.value || label.text).toLowerCase(),
Expand Down Expand Up @@ -197,6 +207,8 @@ const minDateForSelectedLocations = computed<Date>(() => {
const searchIsValid = computed<boolean>(() => {
const hasValidDateRange =
Array.isArray(selectedDateRange.value) ||
(selectedDateRange.value.value &&
Array.isArray(selectedDateRange.value.value)) ||
Array.isArray(customDateRange.value);
const hasAdvancedSettingsSelected =
showAdvanced.value &&
Expand Down
24 changes: 19 additions & 5 deletions browse-next/src/components/CardTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,15 @@
v-bind="{ cell, row: sortedItems[rowIndex] }"
>
<span
v-if="cell && cell.value"
class="text-nowrap"
v-if="cell && cell.value !== undefined"
:class="{ 'text-nowrap': !hasLineBreaks(cell) }"
v-html="cell.value"
/>
<span v-else-if="cell" class="text-nowrap" v-html="cell" />
<span
v-else-if="cell"
:class="{ 'text-nowrap': !hasLineBreaks(cell) }"
v-html="cell"
/>
</slot>
</td>
</tr>
Expand Down Expand Up @@ -85,10 +89,14 @@
>
<span
v-if="value.value"
class="text-nowrap"
:class="{ 'text-nowrap': !hasLineBreaks(value.value) }"
v-html="value.value"
/>
<span v-else-if="value" class="text-nowrap" v-html="value" />
<span
v-else-if="value"
:class="{ 'text-nowrap': !hasLineBreaks(value) }"
v-html="value"
/>
</div>
</div>
</slot>
Expand Down Expand Up @@ -139,6 +147,12 @@ const emit = defineEmits<{
const cardTableContainer = ref<HTMLDivElement>();
const hasLineBreaks = (value: any) => {
return (
typeof value === "string" && (value.length > 50 || value.includes("\n"))
);
};
const { width } = useElementSize(cardTableContainer);
const shouldRenderAsRows = computed(() => width.value >= props.maxCardWidth);
Expand Down
2 changes: 1 addition & 1 deletion browse-next/src/components/MapWithPoints.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { rafFps } from "@models/LoggedInUser";
import type { NamedPoint } from "@models/mapUtils";
import { BSpinner } from "bootstrap-vue-next";
import type { LatLng } from "@typedefs/api/common";
import { useElementSize, useResizeObserver } from "@vueuse/core";
import { useElementSize } from "@vueuse/core";
const attribution = control.attribution;
Expand Down
4 changes: 2 additions & 2 deletions browse-next/src/components/NetworkConnectionAlertModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ watch(
if (networkConnectionError.retryCount === MAX_RETRY_COUNT) {
show.value = false;
} else {
countDown.value = count / 1000;
countDown.value = Math.max(0, count / 1000);
countDownInterval.value = setInterval(() => {
countDown.value--;
countDown.value = Math.max(0, countDown.value - 1);
}, 1000) as unknown as number;
}
},
Expand Down
2 changes: 2 additions & 0 deletions browse-next/src/components/OverflowingTabList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ const findSelectedItemName = () => {
};
const activeItemTitle = ref<string>("");
// TODO: If overflowing items are selected, move the ellipsis around and have the selected item showing?
</script>

<template>
Expand Down
16 changes: 8 additions & 8 deletions browse-next/src/components/RecordingViewActionButtons.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { RecordingType } from "@typedefs/api/consts.ts";
import { currentSelectedProject } from "@models/provides.ts";
import type { ApiLoggedInUserResponse } from "@typedefs/api/user";
import type { LoadedResource } from "@api/types.ts";
import TwoStepActionButtonPopover from "@/components/TwoStepActionButtonPopover.vue";
const props = withDefaults(
defineProps<{
Expand Down Expand Up @@ -212,18 +213,17 @@ const notImplemented = () => {
>
<font-awesome-icon icon="download" color="#666" />
</button>
<two-step-action-button
:action="() => emit('delete-recording')"
icon="trash-can"
<two-step-action-button-popover
:icon="['fas', 'trash-can']"
:confirmation-label="'Delete recording'"
:classes="['btn-hi', 'btn', 'btn-square', 'p-0']"
confirmation-label="Delete Recording"
color="#666"
:action="() => emit('delete-recording')"
:placement="'top'"
v-if="userIsGroupAdmin"
:boundary-padding="false"
>
<template #button-content>
<font-awesome-icon icon="trash-can" color="#666" />
</template>
</two-step-action-button>
</two-step-action-button-popover>
<!-- <button-->
<!-- type="button"-->
<!-- class="btn btn-square btn-hi"-->
Expand Down
Loading

0 comments on commit 829510e

Please sign in to comment.