Skip to content
Merged
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
57 changes: 30 additions & 27 deletions client/dive-common/use/useSave.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
import { ref, Ref } from '@vue/composition-api';

import Track, { TrackId } from 'vue-media-annotator/track';
import { Attribute } from 'vue-media-annotator/use/useAttributes';
import Track, { TrackId, isTrack } from 'vue-media-annotator/track';
import { Attribute, isAttribute } from 'vue-media-annotator/use/useAttributes';

import { useApi, DatasetMetaMutable } from 'dive-common/apispec';

function _updatePendingChangeMap<K, V>(
key: K, value: V,
action: 'upsert' | 'delete',
upsert: Map<K, V>,
del: Set<K>,
) {
if (action === 'delete') {
del.add(key);
upsert.delete(key);
} else if (action === 'upsert') {
del.delete(key);
upsert.set(key, value);
}
}

export default function useSave(datasetId: Ref<Readonly<string>>) {
const pendingSaveCount = ref(0);
const pendingChangeMap = {
Expand Down Expand Up @@ -49,37 +64,25 @@ export default function useSave(datasetId: Ref<Readonly<string>>) {

function markChangesPending(
{
type,
action,
data,
}: {
type: 'track' | 'attribute' | 'meta';
action?: 'upsert' | 'delete';
data?: Track | Attribute;
} = { type: 'meta' },
action: 'upsert' | 'delete' | 'meta';
data?: Track | Attribute;
} = { action: 'meta' },
) {
if (type === 'meta') {
if (action === 'meta') {
pendingChangeMap.meta += 1;
} else if (type === 'track' && data !== undefined && (data as Track).trackId !== undefined) {
const track = data as Track;
if (action === 'delete') {
pendingChangeMap.delete.add(track.trackId);
pendingChangeMap.upsert.delete(track.trackId);
} else if (action === 'upsert') {
pendingChangeMap.delete.delete(track.trackId);
pendingChangeMap.upsert.set(track.trackId, track);
}
} else if (type === 'attribute' && data !== undefined && (data as Attribute)._id !== undefined) {
const attribute = data as Attribute;
if (action === 'delete') {
pendingChangeMap.attributeDelete.add(attribute._id);
pendingChangeMap.attributeUpsert.delete(attribute._id);
} else if (action === 'upsert') {
pendingChangeMap.attributeDelete.delete(attribute._id);
pendingChangeMap.attributeUpsert.set(attribute._id, attribute);
}
} else if (isTrack(data)) {
_updatePendingChangeMap(
data.trackId, data, action, pendingChangeMap.upsert, pendingChangeMap.delete,
);
} else if (isAttribute(data)) {
_updatePendingChangeMap(
data._id, data, action, pendingChangeMap.attributeUpsert, pendingChangeMap.attributeDelete,
);
} else {
throw new Error('Arguments inconsistent with pending change type');
throw new Error(`Arguments inconsistent with pending change type: ${action} cannot be performed on ${data}`);
}
pendingSaveCount.value += 1;
}
Expand Down
11 changes: 11 additions & 0 deletions client/src/track.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@ interface TrackParams {
attributes?: StringKeyObject;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function isTrack(obj: any): obj is Track {
return (
(typeof obj === 'object')
&& (obj.trackId !== undefined)
&& (typeof obj.features === 'object')
&& (typeof obj.begin === 'number')
&& (typeof obj.end === 'number')
);
}

/**
* Track manages the state of a track, its
* frame data, and all metadata.
Expand Down
19 changes: 14 additions & 5 deletions client/src/use/useAttributes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ export interface Attribute {
_id: string;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function isAttribute(obj: any): obj is Attribute {
return (
(typeof obj === 'object')
&& (typeof obj.belongs === 'string')
&& (typeof obj.datatype === 'string')
&& (typeof obj._id === 'string')
&& (typeof obj.name === 'string')
);
}

export type Attributes = Record<string, Attribute>;

/**
Expand All @@ -19,11 +30,9 @@ export type Attributes = Record<string, Attribute>;
interface UseAttributesParams {
markChangesPending: (
{
type,
action,
data,
}: {
type: 'attribute';
action: 'upsert' | 'delete';
data: Attribute;
}
Expand Down Expand Up @@ -51,7 +60,7 @@ export default function UseAttributes({ markChangesPending }: UseAttributesParam
oldAttribute = attributes.value[data._id];
// Name change should delete the old attribute and create a new one with the updated id
VueDel(attributes.value, data._id);
markChangesPending({ type: 'attribute', action: 'delete', data: { ...attributes.value[data._id] } });
markChangesPending({ action: 'delete', data: { ...attributes.value[data._id] } });
// Create a new attribute to replace it
// eslint-disable-next-line no-param-reassign
data._id = `${data.belongs}_${data.name}`;
Expand All @@ -61,13 +70,13 @@ export default function UseAttributes({ markChangesPending }: UseAttributesParam
// TODO: Lengthy track/detection attribute updating function
}
VueSet(attributes.value, data._id, data);
markChangesPending({ type: 'attribute', action: 'upsert', data: attributes.value[data._id] });
markChangesPending({ action: 'upsert', data: attributes.value[data._id] });
}


function deleteAttribute(attributeId: string, removeFromTracks = false) {
if (attributes.value[attributeId] !== undefined) {
markChangesPending({ type: 'attribute', action: 'delete', data: { ...attributes.value[attributeId] } });
markChangesPending({ action: 'delete', data: { ...attributes.value[attributeId] } });
VueDel(attributes.value, attributeId);
}
if (removeFromTracks) {
Expand Down
8 changes: 3 additions & 5 deletions client/src/use/useTrackStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ import Track, { TrackId } from '../track';
interface UseTrackStoreParams {
markChangesPending: (
{
type,
action,
data,
}:
{
type: 'track';
action: 'upsert' | 'delete';
data: Track;
}) => void;
Expand Down Expand Up @@ -74,7 +72,7 @@ export default function useTrackStore({ markChangesPending }: UseTrackStoreParam
intervalTree.insert([track.begin, track.end], track.trackId.toString());
}
canary.value += 1;
markChangesPending({ type: 'track', action: 'upsert', data: track });
markChangesPending({ action: 'upsert', data: track });
}

function insertTrack(track: Track, afterId?: TrackId) {
Expand All @@ -97,7 +95,7 @@ export default function useTrackStore({ markChangesPending }: UseTrackStoreParam
confidencePairs: [[defaultType, 1]],
});
insertTrack(track, afterId);
markChangesPending({ type: 'track', action: 'upsert', data: track });
markChangesPending({ action: 'upsert', data: track });
return track;
}

Expand All @@ -117,7 +115,7 @@ export default function useTrackStore({ markChangesPending }: UseTrackStoreParam
throw new Error(`TrackId ${trackId} not found in trackIds.`);
}
trackIds.value.splice(listIndex, 1);
markChangesPending({ type: 'track', action: 'delete', data: track });
markChangesPending({ action: 'delete', data: track });
}

/*
Expand Down