Skip to content

Commit 2c4ff6b

Browse files
authored
Metadata updates (#397)
* add descriptions to DIVE Metadata fields * adding filtering visibility separate from default visibility to metadata * ordering DIVE Metadata Keys using vuedraggable * metadata key grouping * linting and disabling deletion/unlocking for system metadata fields * ordering and group displaying * metadata filter documentation * DIVE MEtadata update description field * increment version
1 parent a791477 commit 2c4ff6b

13 files changed

Lines changed: 1512 additions & 182 deletions

File tree

client/dive-common/components/Attributes/AttributeMetadataLink.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
getMetadataFilterValues,
1515
modifyDiveMetadataPermission,
1616
updateDiveMetadataDisplay,
17+
updateDiveMetadataFilterVisibility,
1718
} from 'platform/web-girder/api/divemetadata.service';
1819
import { AccessType, getFolder, getFolderAccess } from 'platform/web-girder/api/girder.service';
1920
import { useGirderRest } from 'platform/web-girder/plugins/girder';
@@ -403,6 +404,7 @@ export default defineComponent({
403404
}
404405
if (makeKeyVisible.value) {
405406
await updateDiveMetadataDisplay(rootId, key, 'display');
407+
await updateDiveMetadataFilterVisibility(rootId, key, 'display');
406408
}
407409
await loadKnownKeys();
408410
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -433,6 +435,7 @@ export default defineComponent({
433435
}
434436
if (makeKeyVisible.value) {
435437
await updateDiveMetadataDisplay(rootId, key, 'display');
438+
await updateDiveMetadataFilterVisibility(rootId, key, 'display');
436439
}
437440
}, Promise.resolve());
438441
await loadKnownKeys();

client/dive-common/components/DatasetInfo.vue

Lines changed: 178 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ import { getFolder } from 'platform/web-girder/api';
99
import { useStore } from 'platform/web-girder/store/types';
1010
import { useHandler } from 'vue-media-annotator/provides';
1111
import DIVEMetadataEditKey from 'platform/web-girder/views/DIVEMetadata/DIVEMetadataEditKey.vue';
12+
import MetadataKeyLabel from 'platform/web-girder/views/DIVEMetadata/MetadataKeyLabel.vue';
1213
import {
1314
FilterDisplayConfig,
1415
filterDiveMetadata,
1516
getMetadataFilterValues,
1617
MetadataFilterKeysItem,
18+
partitionMetadataKeys,
1719
setDiveDatasetMetadataKey,
1820
} from 'platform/web-girder/api/divemetadata.service';
1921
import { usePrompt } from 'dive-common/vue-utilities/prompt-service';
@@ -23,6 +25,7 @@ export default defineComponent({
2325
components: {
2426
StackedVirtualSidebarContainer,
2527
DIVEMetadataEditKey,
28+
MetadataKeyLabel,
2629
},
2730
2831
props: {
@@ -45,6 +48,7 @@ export default defineComponent({
4548
display: [], hide: [], categoricalLimit: 50, slicerCLI: 'Disabled',
4649
});
4750
const unlockedMap: Ref<Record<string, MetadataFilterKeysItem>> = ref({});
51+
const metadataKeysByName: Ref<Record<string, MetadataFilterKeysItem>> = ref({});
4852
const getMetadata = async () => {
4953
if (store.state.Dataset.meta) {
5054
const resp = await getFolder(store.state.Dataset.meta?.id);
@@ -74,6 +78,7 @@ export default defineComponent({
7478
},
7579
);
7680
const filterData = await getMetadataFilterValues(diveMetadataRootId.value);
81+
metadataKeysByName.value = filterData.data.metadataKeys || {};
7782
const { unlocked } = filterData.data;
7883
unlockedMap.value = {};
7984
if (unlocked) {
@@ -89,17 +94,52 @@ export default defineComponent({
8994
9095
const processedDatasetMetadata = computed(() => {
9196
if (!datasetMetadata.value || !diveMetadataFilter.value) return null;
92-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
93-
const display: { default: Record<string, any>, advanced: Record<string, any>} = { default: {}, advanced: {} };
94-
display.default.FileName = datasetMetadata.value.filename || 'N/A';
95-
Object.keys(datasetMetadata.value.metadata).forEach((field) => {
96-
if (datasetMetadata.value === null) return;
97-
if (diveMetadataFilter.value.display.includes(field)) {
98-
display.default[field] = datasetMetadata.value.metadata[field];
99-
} else if (!diveMetadataFilter.value.hide.includes(field)) {
100-
display.advanced[field] = datasetMetadata.value.metadata[field];
101-
}
102-
});
97+
type MetadataField = { name: string; value: unknown };
98+
type MetadataGroup = { id: string; name: string; description?: string; items: MetadataField[] };
99+
const display: {
100+
default: MetadataField[];
101+
defaultGroups: MetadataGroup[];
102+
advanced: MetadataField[];
103+
advancedGroups: MetadataGroup[];
104+
} = {
105+
default: [{ name: 'FileName', value: datasetMetadata.value.filename || 'N/A' }],
106+
defaultGroups: [],
107+
advanced: [],
108+
advancedGroups: [],
109+
};
110+
const allKeys = Object.keys(datasetMetadata.value.metadata);
111+
const defaultKeys = allKeys.filter((field) => diveMetadataFilter.value.display.includes(field));
112+
const advancedKeys = allKeys.filter((field) => !diveMetadataFilter.value.display.includes(field) && !diveMetadataFilter.value.hide.includes(field));
113+
114+
const defaultPartition = partitionMetadataKeys(defaultKeys, diveMetadataFilter.value);
115+
display.default.push(...defaultPartition.ungrouped.map((name) => ({
116+
name,
117+
value: datasetMetadata.value?.metadata[name],
118+
})));
119+
display.defaultGroups = defaultPartition.groups.map((group) => ({
120+
id: group.id,
121+
name: group.name,
122+
description: group.description,
123+
items: group.keys.map((name) => ({
124+
name,
125+
value: datasetMetadata.value?.metadata[name],
126+
})),
127+
}));
128+
129+
const advancedPartition = partitionMetadataKeys(advancedKeys, diveMetadataFilter.value);
130+
display.advanced = advancedPartition.ungrouped.map((name) => ({
131+
name,
132+
value: datasetMetadata.value?.metadata[name],
133+
}));
134+
display.advancedGroups = advancedPartition.groups.map((group) => ({
135+
id: group.id,
136+
name: group.name,
137+
description: group.description,
138+
items: group.keys.map((name) => ({
139+
name,
140+
value: datasetMetadata.value?.metadata[name],
141+
})),
142+
}));
103143
return display;
104144
});
105145
const datasetInfoLength = computed(() => Object.keys(datasetInfo.value || []).length);
@@ -119,6 +159,16 @@ export default defineComponent({
119159
});
120160
}
121161
};
162+
const getEditableValue = (value: unknown): string | number | boolean | null => {
163+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
164+
return value;
165+
}
166+
return null;
167+
};
168+
const getEditableSetValues = (key: string): string[] => {
169+
const values = unlockedMap.value[key]?.set || [];
170+
return values.filter((value): value is string => typeof value === 'string');
171+
};
122172
123173
return {
124174
datasetInfo,
@@ -127,7 +177,10 @@ export default defineComponent({
127177
processedDatasetMetadata,
128178
panels,
129179
unlockedMap,
180+
metadataKeysByName,
130181
updateDiveMetadataKeyVal,
182+
getEditableValue,
183+
getEditableSetValues,
131184
};
132185
},
133186
});
@@ -172,52 +225,150 @@ export default defineComponent({
172225
<v-expansion-panel v-if="processedDatasetMetadata" class="border">
173226
<v-expansion-panel-header>DIVE Metadata</v-expansion-panel-header>
174227
<v-expansion-panel-content class="pa-0">
175-
<v-list-item v-for="(value, name) in processedDatasetMetadata.default" :key="`datasetMetadata_${name}`" two-line dense>
228+
<v-list-item v-for="item in processedDatasetMetadata.default" :key="`datasetMetadata_${item.name}`" two-line dense>
176229
<v-list-item-content>
177230
<v-list-item-title>
178-
{{ name }}
231+
<MetadataKeyLabel
232+
:key-name="item.name"
233+
:description="metadataKeysByName[item.name] ? metadataKeysByName[item.name].description : undefined"
234+
/>
179235
</v-list-item-title>
180236
<v-list-item-subtitle class="wrap-text">
181-
<span v-if="unlockedMap[name] !== undefined">
237+
<span v-if="unlockedMap[item.name] !== undefined">
182238
<DIVEMetadataEditKey
183-
:category="unlockedMap[name].category"
184-
:value="value"
185-
:set-values="unlockedMap[name].set || []"
239+
:category="unlockedMap[item.name].category"
240+
:value="getEditableValue(item.value)"
241+
:set-values="getEditableSetValues(item.name)"
186242
class="pl-2"
187-
@update="updateDiveMetadataKeyVal(name, $event)"
243+
@update="updateDiveMetadataKeyVal(item.name, $event)"
188244
/>
189245
</span>
190246
<span v-else>
191-
{{ value !== undefined ? value.toString() : '' }}
247+
{{ item.value !== undefined ? item.value.toString() : '' }}
192248
</span>
193249
</v-list-item-subtitle>
194250
</v-list-item-content>
195251
</v-list-item>
252+
<v-expansion-panels>
253+
<v-expansion-panel
254+
v-for="group in processedDatasetMetadata.defaultGroups"
255+
:key="`datasetMetadata_default_group_${group.id}`"
256+
>
257+
<v-expansion-panel-header>
258+
<span class="d-inline-flex align-center">
259+
{{ group.name }}
260+
<v-tooltip v-if="group.description" bottom max-width="320" open-delay="200">
261+
<template #activator="{ on }">
262+
<v-icon small class="ml-1" color="grey lighten-1" v-on="on">
263+
mdi-information
264+
</v-icon>
265+
</template>
266+
<span>{{ group.description }}</span>
267+
</v-tooltip>
268+
</span>
269+
</v-expansion-panel-header>
270+
<v-expansion-panel-content class="pa-0">
271+
<v-list-item v-for="item in group.items" :key="`datasetMetadata_${group.id}_${item.name}`" two-line dense>
272+
<v-list-item-content>
273+
<v-list-item-title>
274+
<MetadataKeyLabel
275+
:key-name="item.name"
276+
:description="metadataKeysByName[item.name] ? metadataKeysByName[item.name].description : undefined"
277+
/>
278+
</v-list-item-title>
279+
<v-list-item-subtitle class="wrap-text">
280+
<span v-if="unlockedMap[item.name] !== undefined">
281+
<DIVEMetadataEditKey
282+
:category="unlockedMap[item.name].category"
283+
:value="getEditableValue(item.value)"
284+
:set-values="getEditableSetValues(item.name)"
285+
class="pl-2"
286+
@update="updateDiveMetadataKeyVal(item.name, $event)"
287+
/>
288+
</span>
289+
<span v-else>
290+
{{ item.value !== undefined ? item.value.toString() : '' }}
291+
</span>
292+
</v-list-item-subtitle>
293+
</v-list-item-content>
294+
</v-list-item>
295+
</v-expansion-panel-content>
296+
</v-expansion-panel>
297+
</v-expansion-panels>
196298
<v-expansion-panels>
197299
<v-expansion-panel>
198300
<v-expansion-panel-header>Advanced</v-expansion-panel-header>
199301
<v-expansion-panel-content class="pa-0">
200-
<v-list-item v-for="(value, name) in processedDatasetMetadata.advanced" :key="`datasetMetadata_${name}`" two-line dense>
302+
<v-list-item v-for="item in processedDatasetMetadata.advanced" :key="`datasetMetadata_${item.name}`" two-line dense>
201303
<v-list-item-content>
202304
<v-list-item-title>
203-
{{ name }}
305+
<MetadataKeyLabel
306+
:key-name="item.name"
307+
:description="metadataKeysByName[item.name] ? metadataKeysByName[item.name].description : undefined"
308+
/>
204309
</v-list-item-title>
205310
<v-list-item-subtitle class="wrap-text">
206-
<span v-if="unlockedMap[name] !== undefined">
311+
<span v-if="unlockedMap[item.name] !== undefined">
207312
<DIVEMetadataEditKey
208-
:category="unlockedMap[name].category"
209-
:value="value"
210-
:set-values="unlockedMap[name].set || []"
313+
:category="unlockedMap[item.name].category"
314+
:value="getEditableValue(item.value)"
315+
:set-values="getEditableSetValues(item.name)"
211316
class="pl-2"
212-
@update="updateDiveMetadataKeyVal(name, $event)"
317+
@update="updateDiveMetadataKeyVal(item.name, $event)"
213318
/>
214319
</span>
215320
<span v-else>
216-
{{ value !== undefined ? value.toString() : '' }}
321+
{{ item.value !== undefined ? item.value.toString() : '' }}
217322
</span>
218323
</v-list-item-subtitle>
219324
</v-list-item-content>
220325
</v-list-item>
326+
<v-expansion-panels>
327+
<v-expansion-panel
328+
v-for="group in processedDatasetMetadata.advancedGroups"
329+
:key="`datasetMetadata_advanced_group_${group.id}`"
330+
>
331+
<v-expansion-panel-header>
332+
<span class="d-inline-flex align-center">
333+
{{ group.name }}
334+
<v-tooltip v-if="group.description" bottom max-width="320" open-delay="200">
335+
<template #activator="{ on }">
336+
<v-icon small class="ml-1" color="grey lighten-1" v-on="on">
337+
mdi-information
338+
</v-icon>
339+
</template>
340+
<span>{{ group.description }}</span>
341+
</v-tooltip>
342+
</span>
343+
</v-expansion-panel-header>
344+
<v-expansion-panel-content class="pa-0">
345+
<v-list-item v-for="item in group.items" :key="`datasetMetadata_${group.id}_${item.name}`" two-line dense>
346+
<v-list-item-content>
347+
<v-list-item-title>
348+
<MetadataKeyLabel
349+
:key-name="item.name"
350+
:description="metadataKeysByName[item.name] ? metadataKeysByName[item.name].description : undefined"
351+
/>
352+
</v-list-item-title>
353+
<v-list-item-subtitle class="wrap-text">
354+
<span v-if="unlockedMap[item.name] !== undefined">
355+
<DIVEMetadataEditKey
356+
:category="unlockedMap[item.name].category"
357+
:value="getEditableValue(item.value)"
358+
:set-values="getEditableSetValues(item.name)"
359+
class="pl-2"
360+
@update="updateDiveMetadataKeyVal(item.name, $event)"
361+
/>
362+
</span>
363+
<span v-else>
364+
{{ item.value !== undefined ? item.value.toString() : '' }}
365+
</span>
366+
</v-list-item-subtitle>
367+
</v-list-item-content>
368+
</v-list-item>
369+
</v-expansion-panel-content>
370+
</v-expansion-panel>
371+
</v-expansion-panels>
221372
</v-expansion-panel-content>
222373
</v-expansion-panel>
223374
</v-expansion-panels>

client/package-lock.json

Lines changed: 18 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)