import { useState, useEffect, useContext, useMemo } from 'react'; // Cloudscape import { Button, Container, Header, Link, SpaceBetween, TagEditor } from '@cloudscape-design/components'; // Flatten/Unflatten JSON import { flatten, unflatten } from 'flat'; // App import { AppContext } from '../App'; import { INITIAL_DICOM_KEYS, METADATA_TAG_EDITOR_I18N } from './metadataConsts'; import { keyValueToObj } from '../../utils/Array'; // API import { updateImageSetMetadata } from '../../utils/AwsHealthImagingApi'; const flatOpts = { delimiter: '|' }; // Return initial tags for function getInitialTags(flatMetadata) { let initialTags = []; INITIAL_DICOM_KEYS.forEach((k) => { const keyValue = flatMetadata[k]; if (keyValue) { initialTags.push({ key: k, value: keyValue, existing: true, }); } }); return initialTags; } export function MetadataEditor({ imageSetMetadata, saveEnabled, maxVersion, metadataLoading, navigate }) { const { setToolsOpen } = useContext(AppContext); const [tags, setTags] = useState([]); const [saving, setSaving] = useState(false); // flatten metadata const flatMetadata = useMemo(() => { try { let tempMetadata = JSON.parse(imageSetMetadata); ['SchemaVersion', 'DatastoreID', 'ImageSetID'].forEach((k) => delete tempMetadata[k]); Object.keys(tempMetadata?.Study?.Series || {}).forEach((seriesUid) => { delete tempMetadata.Study.Series[seriesUid]?.Instances; }); return flatten(tempMetadata, flatOpts); } catch (error) { return {}; } }, [imageSetMetadata]); // set (initial) tags when metadata changes useEffect(() => { setTags(getInitialTags(flatMetadata)); }, [flatMetadata]); // return possible keys for // this is the keys from flatMetadata minus the keys in existing tags function keysRequest() { return new Promise(function (resolve, reject) { try { resolve(Object.keys(flatMetadata).filter((mk) => !tags.map((t) => t.key).includes(mk))); } catch (error) { resolve([]); } }); } // return possible values for // this is the vakue from flatMetadata for the given key, if it exists, otherwise return rejected promise function valueRequest(key) { return new Promise(function (resolve, reject) { const val = flatMetadata[key]; if (typeof val === 'undefined') { reject(); } else if (val == null) { resolve(['null']); } else { resolve([val]); } }); } async function handleSave() { setSaving(true); const { SchemaVersion, DatastoreID, ImageSetID } = JSON.parse(imageSetMetadata); // Get new or updated tags, save to updateData object let updateData = {}; const newUpdateTags = tags.filter((t) => { if (['', 'null'].includes(t.value)) { return null !== flatMetadata[t.key]; } else { return t.value !== flatMetadata[t.key]; } }); if (newUpdateTags.length > 0) { // convert array of objects with key/value keys into an object of key/value pairs const newUpdateTagsFlattenedData = keyValueToObj(newUpdateTags); const newUpdateTagsData = unflatten(newUpdateTagsFlattenedData, flatOpts); updateData = { SchemaVersion: SchemaVersion, ...newUpdateTagsData }; } // Get removed tags let removeData = {}; const removeTags = tags.filter((t) => t.markedForRemoval === true || t.value === 'REMOVE'); if (removeTags.length > 0) { const removedTagsFlattenedData = keyValueToObj(removeTags); const removedTagsData = unflatten(removedTagsFlattenedData, flatOpts); removeData = { SchemaVersion: SchemaVersion, ...removedTagsData }; } await updateImageSetMetadata({ datastoreId: DatastoreID, imageSetId: ImageSetID, latestVersionId: maxVersion, removableAttributes: removeData, updatableAttributes: updateData, }); navigate(-1); setSaving(false); } return ( DICOM tags can be updated or removed. This creates a new version of metadata for this image set.
Previous versions are still accessible.
A blank or null value is treated as null. A REMOVE value removes the tag. } actions={ } info={ setToolsOpen(true)}> Info } > Edit Metadata } > setTags(detail.tags)} keysRequest={() => keysRequest()} valuesRequest={(key) => valueRequest(key)} />
); }