Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions client/dive-common/components/GroupSidebar.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// @vitest-environment jsdom
/* eslint-disable vue/one-component-per-file -- harness components for shallow mounting */
/// <reference types="vitest" />
import {
defineComponent, h, reactive, ref,
} from 'vue';
import { shallowMount } from '@vue/test-utils';
import { clientSettings } from 'dive-common/store/settings';
import FilterList from 'vue-media-annotator/components/FilterList.vue';
import GroupSidebar from './GroupSidebar.vue';

const provideMocks = vi.hoisted(() => ({
groupFilterControls: undefined as unknown,
groupStyleManager: undefined as unknown,
}));

vi.mock('dive-common/vue-utilities/prompt-service', () => ({
usePrompt: () => ({ prompt: vi.fn(), visible: () => false }),
}));

vi.mock('vue-media-annotator/provides', () => ({
useCameraStore: () => ({ camMap: ref(new Map()) }),
useGroupFilterControls: () => provideMocks.groupFilterControls,
useGroupStyleManager: () => provideMocks.groupStyleManager,
useHandler: () => ({ seekFrame: vi.fn() }),
usePendingSaveCount: () => ref(0),
useReadOnlyMode: () => ref(false),
useSelectedCamera: () => ref(''),
useTime: () => ({ frame: ref(0) }),
}));

describe('GroupSidebar type filter', () => {
it('wires the group FilterList to flat show-empty behavior', () => {
clientSettings.typeSettings.trackSortDir = 'a-z';
clientSettings.typeSettings.filterTypesByFrame = false;
clientSettings.typeSettings.suppressionType = '';
const checkedTypes = ref(['school', 'pod']);
provideMocks.groupFilterControls = Object.freeze({
allTypes: ref(['school', 'pod']),
usedTypes: ref(['school']),
configuredTypes: ref(['pod']),
checkedTypes,
filteredAnnotations: ref([]),
confidenceFilters: ref({ default: 0 }),
disableAnnotationFilters: ref(false),
updateCheckedTypes: (types: string[]) => { checkedTypes.value = types; },
removeTypeAnnotations: vi.fn(),
});
provideMocks.groupStyleManager = Object.freeze({
typeStyling: ref({
color: () => '#fff',
strokeWidth: () => 1,
fill: () => false,
opacity: () => 1,
}),
});

const ContainerStub = defineComponent({
setup: (_props, { slots }) => () => h(
'div',
slots.default?.({ topHeight: 240, bottomHeight: 120 }),
),
});
const props = reactive({ width: 320 });
const Host = defineComponent({
setup: () => () => h(GroupSidebar, { props }),
});
const wrapper = shallowMount(Host, {
stubs: {
GroupSidebar: false,
FilterList: false,
StackedVirtualSidebarContainer: ContainerStub,
'v-divider': true,
},
});
const filterList = wrapper.findComponent(FilterList);

expect(filterList.exists()).toBe(true);
expect(filterList.props()).toEqual(expect.objectContaining({
filterControls: provideMocks.groupFilterControls,
group: true,
showEmptyTypes: true,
width: 320,
}));
});
});
11 changes: 1 addition & 10 deletions client/dive-common/components/Viewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -639,14 +639,6 @@ export default defineComponent({
const removeGroups = (id: AnnotationId) => {
cameraStore.removeGroups(id);
};
const setTrackType = (
id: AnnotationId,
newType: string,
confidenceVal?: number,
currentType?: string,
) => {
cameraStore.setTrackType(id, newType, confidenceVal, currentType);
};
const removeTypes = (id: AnnotationId, types: string[]) => cameraStore.removeTypes(id, types);
const setGroupType = (
id: AnnotationId,
Expand All @@ -664,7 +656,7 @@ export default defineComponent({
sorted: cameraStore.sortedGroups,
markChangesPending: (markChangesPending as MarkChangesPendingFilter),
remove: removeGroups,
setType: setGroupType,
setGroupType,
removeTypes: removeGroupTypes,
});

Expand All @@ -682,7 +674,6 @@ export default defineComponent({
cameraStore.renameTrackPair(id, currentType, newType)
),
groupFilterControls: groupFilters,
setType: setTrackType,
removeTypes,
});

Expand Down
2 changes: 1 addition & 1 deletion client/dive-common/typeHierarchy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ export function compileHierarchy(hierarchy: TypeHierarchy): TypeHierarchyIndex {
return { hierarchy: normalized, ancestors: Object.fromEntries(ancestorEntries) };
}

function ancestorsOf(index: TypeHierarchyIndex, type: string): readonly string[] {
export function ancestorsOf(index: TypeHierarchyIndex, type: string): readonly string[] {
return Object.prototype.hasOwnProperty.call(index.ancestors, type)
? index.ancestors[type]
: [];
Expand Down
6 changes: 2 additions & 4 deletions client/dive-common/use/useModeManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function makeHarness(markChangesPending: MarkChangesPending = () => undefined) {
sorted: cameraStore.sortedGroups,
remove: () => undefined,
markChangesPending: () => undefined,
setType: () => undefined,
setGroupType: () => undefined,
removeTypes: () => [],
});
const trackFilterControls = new TrackFilterControls({
Expand All @@ -64,7 +64,6 @@ function makeHarness(markChangesPending: MarkChangesPending = () => undefined) {
cameraStore.renameTrackPair(id, currentType, newType)
),
groupFilterControls,
setType: () => undefined,
removeTypes: () => [],
});

Expand Down Expand Up @@ -190,7 +189,7 @@ function makeSingleCamHarness() {
sorted: cameraStore.sortedGroups,
remove: () => undefined,
markChangesPending: () => undefined,
setType: () => undefined,
setGroupType: () => undefined,
removeTypes: () => [],
});
const trackFilterControls = new TrackFilterControls({
Expand All @@ -203,7 +202,6 @@ function makeSingleCamHarness() {
cameraStore.renameTrackPair(id, currentType, newType)
),
groupFilterControls,
setType: () => undefined,
removeTypes: () => [],
});
const modeManager = useModeManager({
Expand Down
40 changes: 14 additions & 26 deletions client/src/BaseFilterControls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ export interface FilterControlsParams<T extends Track | Group> {
sorted: Ref<SortedAnnotation<T>[]>;
markChangesPending: MarkChangesPendingFilter;
remove: (id: AnnotationId) => void;
setType: (id: AnnotationId, newType: string,
confidenceVal?: number, currentType?: string) => void;
removeTypes: (id: AnnotationId, types: string[]) => ConfidencePair[];
getTrack?: (trackId: Readonly<AnnotationId>, cameraName?: string) => T;
}
Expand Down Expand Up @@ -81,9 +79,6 @@ export default abstract class BaseFilterControls<T extends Track | Group> {

remove: (id: AnnotationId) => void;

setType: (id: AnnotationId, newType: string,
confidenceVal?: number, currentType?: string) => void;

removeTypes: (id: AnnotationId, types: string[]) => ConfidencePair[];

disableAnnotationFilters: Ref<boolean>;
Expand All @@ -101,8 +96,6 @@ export default abstract class BaseFilterControls<T extends Track | Group> {

this.remove = params.remove;

this.setType = params.setType;

this.removeTypes = params.removeTypes;

this.markChangesPending = params.markChangesPending;
Expand Down Expand Up @@ -174,6 +167,19 @@ export default abstract class BaseFilterControls<T extends Track | Group> {
}
}

/**
* Carry a renamed type's confidence threshold over to its new name, unless
* the new name already carries one of its own.
*/
protected carryConfidenceFilter(currentType: string, newType: string) {
if (!(newType in this.confidenceFilters.value) && currentType in this.confidenceFilters.value) {
this.setConfidenceFilters({
...this.confidenceFilters.value,
[newType]: this.confidenceFilters.value[currentType],
});
}
}

protected deleteTypeConfiguration(type: string) {
if (this.configuredTypes.value.includes(type)) {
this.configuredTypes.value.splice(this.configuredTypes.value.indexOf(type), 1);
Expand All @@ -197,25 +203,7 @@ export default abstract class BaseFilterControls<T extends Track | Group> {
this.timeFilters.value = val;
}

updateTypeName({ currentType, newType }: { currentType: string; newType: string }) {
//Go through the entire list and replace the oldType with the new Type
this.sorted.value.forEach((annotation) => {
for (let i = 0; i < annotation.confidencePairs.length; i += 1) {
const [name, confidenceVal] = annotation.confidencePairs[i];
if (name === currentType) {
this.setType(annotation.id, newType, confidenceVal, currentType);
break;
}
}
});
if (!(newType in this.confidenceFilters.value) && currentType in this.confidenceFilters.value) {
this.setConfidenceFilters({
...this.confidenceFilters.value,
[newType]: this.confidenceFilters.value[currentType],
});
}
this.deleteType(currentType);
}
abstract updateTypeName(params: { currentType: string; newType: string }): void;

removeTypeAnnotations(types: string[]) {
const processedIds = new Set<AnnotationId>();
Expand Down
10 changes: 0 additions & 10 deletions client/src/CameraStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -364,16 +364,6 @@ export default class CameraStore {
});
}

// Update all cameras to have the same track type
setTrackType(id: AnnotationId, newType: string, confidenceVal?: number, currentType?: string) {
this.camMap.value.forEach((camera) => {
const track = camera.trackStore.getPossible(id);
if (track !== undefined) {
track.setType(newType, confidenceVal, currentType);
}
});
}

setGroupType(id: AnnotationId, newType: string, confidenceVal?: number, currentType?: string) {
this.camMap.value.forEach((camera) => {
const group = camera.groupStore.getPossible(id);
Expand Down
30 changes: 29 additions & 1 deletion client/src/GroupFilterControls.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
import { computed, ref, Ref } from 'vue';
import { cloneDeep } from 'lodash';
import { AnnotationId } from './BaseAnnotation';
import BaseFilterControls, { AnnotationWithContext, FilterControlsParams } from './BaseFilterControls';
import type Group from './Group';

export interface GroupFilterControlsParams extends FilterControlsParams<Group> {
setGroupType: (
id: AnnotationId,
newType: string,
confidenceVal?: number,
currentType?: string,
) => void;
}

export default class GroupFilterControls extends BaseFilterControls<Group> {
filteredAnnotations: Ref<AnnotationWithContext<Group>[]>;

constructor(params: FilterControlsParams<Group>) {
private setGroupType: GroupFilterControlsParams['setGroupType'];

constructor(params: GroupFilterControlsParams) {
super(params);

this.setGroupType = params.setGroupType;

/**
* Override default confidence filters. There is no UI to adjust this,
* so filter nothing by default
Expand Down Expand Up @@ -46,4 +60,18 @@ export default class GroupFilterControls extends BaseFilterControls<Group> {
return resultsArr;
});
}

updateTypeName({ currentType, newType }: { currentType: string; newType: string }) {
this.sorted.value.forEach((annotation) => {
for (let i = 0; i < annotation.confidencePairs.length; i += 1) {
const [name, confidenceVal] = annotation.confidencePairs[i];
if (name === currentType) {
this.setGroupType(annotation.id, newType, confidenceVal, currentType);
break;
}
}
});
this.carryConfidenceFilter(currentType, newType);
this.deleteType(currentType);
}
}
Loading
Loading