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
2 changes: 1 addition & 1 deletion src/actions/importDicomChunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ export async function importDicomChunks(chunks: Chunk[]) {
})
);

return Object.keys(chunksByVolume);
return chunksByVolume;
}
65 changes: 33 additions & 32 deletions src/actions/loadUserFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,24 @@ import { useDatasetStore } from '@/src/store/datasets';
import { useDICOMStore } from '@/src/store/datasets-dicom';
import { useLayersStore } from '@/src/store/datasets-layers';
import { useSegmentGroupStore } from '@/src/store/segmentGroups';
import { wrapInArray, nonNullable } from '@/src/utils';
import { wrapInArray, nonNullable, partition } from '@/src/utils';
import { basename } from '@/src/utils/path';
import { parseUrl } from '@/src/utils/url';
import { logError } from '@/src/utils/loggers';
import { PipelineResultSuccess, partitionResults } from '@/src/core/pipeline';
import {
ImportDataSourcesResult,
importDataSources,
toDataSelection,
} from '@/src/io/import/importDataSources';
import {
ErrorResult,
ImportResult,
LoadableResult,
VolumeResult,
LoadableVolumeResult,
isLoadableResult,
isVolumeResult,
ImportDataSourcesResult,
} from '@/src/io/import/common';
import { isDicomImage } from '@/src/utils/dataSelection';

// higher value priority is preferred for picking a primary selection
const BASE_MODALITY_TYPES = {
Expand All @@ -38,8 +39,8 @@ const BASE_MODALITY_TYPES = {

function findBaseDicom(loadableDataSources: Array<LoadableResult>) {
// find dicom dataset for primary selection if available
const dicoms = loadableDataSources.filter(
({ dataType }) => dataType === 'dicom'
const dicoms = loadableDataSources.filter(({ dataID }) =>
isDicomImage(dataID)
);
// prefer some modalities as base
const dicomStore = useDICOMStore();
Expand Down Expand Up @@ -97,19 +98,15 @@ function findBaseImage(
}

// returns image and dicom sources, no config files
function filterLoadableDataSources(
succeeded: Array<PipelineResultSuccess<ImportResult>>
) {
return succeeded.flatMap((result) => {
return result.data.filter(isLoadableResult);
});
function filterLoadableDataSources(succeeded: Array<ImportResult>) {
return succeeded.filter(isLoadableResult);
}

// Returns list of dataSources with file names where the name has the extension argument
// and the start of the file name matches the primary file name.
function filterMatchingNames(
primaryDataSource: VolumeResult,
succeeded: Array<PipelineResultSuccess<ImportResult>>,
primaryDataSource: LoadableVolumeResult,
succeeded: Array<ImportResult>,
extension: string
) {
const dicomStore = useDICOMStore();
Expand Down Expand Up @@ -141,7 +138,7 @@ function getStudyUID(volumeID: string) {
}

function findBaseDataSource(
succeeded: Array<PipelineResultSuccess<ImportResult>>,
succeeded: Array<ImportResult>,
segmentGroupExtension: string
) {
const loadableDataSources = filterLoadableDataSources(succeeded);
Expand All @@ -155,24 +152,24 @@ function findBaseDataSource(

function filterOtherVolumesInStudy(
volumeID: string,
succeeded: Array<PipelineResultSuccess<ImportResult>>
succeeded: Array<ImportResult>
) {
const targetStudyUID = getStudyUID(volumeID);
const dicomDataSources = filterLoadableDataSources(succeeded).filter(
({ dataType }) => dataType === 'dicom'
({ dataID }) => isDicomImage(dataID)
);
return dicomDataSources.filter((ds) => {
const sourceStudyUID = getStudyUID(ds.dataID);
return sourceStudyUID === targetStudyUID && ds.dataID !== volumeID;
}) as Array<VolumeResult>;
}) as Array<LoadableVolumeResult>;
}

// Layers a DICOM PET on a CT if found
function loadLayers(
primaryDataSource: VolumeResult,
succeeded: Array<PipelineResultSuccess<ImportResult>>
primaryDataSource: LoadableVolumeResult,
succeeded: Array<ImportResult>
) {
if (primaryDataSource.dataType !== 'dicom') return;
if (!isDicomImage(primaryDataSource.dataID)) return;
const otherVolumesInStudy = filterOtherVolumesInStudy(
primaryDataSource.dataID,
succeeded
Expand All @@ -198,8 +195,8 @@ function loadLayers(
// - DICOM SEG modalities with matching StudyUIDs.
// - DataSources that have a name like foo.segmentation.bar and the primary DataSource is named foo.baz
function loadSegmentations(
primaryDataSource: VolumeResult,
succeeded: Array<PipelineResultSuccess<ImportResult>>,
primaryDataSource: LoadableVolumeResult,
succeeded: Array<ImportResult>,
segmentGroupExtension: string
) {
const matchingNames = filterMatchingNames(
Expand Down Expand Up @@ -237,13 +234,19 @@ function loadDataSources(sources: DataSource[]) {

let results: ImportDataSourcesResult[];
try {
results = await importDataSources(sources);
results = (await importDataSources(sources)).filter((result) =>
// only look at data and error results
['data', 'error'].includes(result.type)
);
} catch (error) {
loadDataStore.setError(error as Error);
return;
}

const [succeeded, errored] = partitionResults(results);
const [succeeded, errored] = partition(
(result) => result.type !== 'error',
results
);

if (!dataStore.primarySelection && succeeded.length) {
const primaryDataSource = findBaseDataSource(
Expand All @@ -264,14 +267,12 @@ function loadDataSources(sources: DataSource[]) {
}

if (errored.length) {
const errorMessages = errored.map((errResult) => {
// pick first error
const [firstError] = errResult.errors;
// pick innermost dataset that errored
const name = getDataSourceName(firstError.inputDataStackTrace[0]);
const errorMessages = (errored as ErrorResult[]).map((errResult) => {
const { dataSource, error } = errResult;
const name = getDataSourceName(dataSource);
// log error for debugging
logError(firstError.cause);
return `- ${name}: ${firstError.message}`;
logError(error);
return `- ${name}: ${error.message}`;
});
const failedError = new Error(
`These files failed to load:\n${errorMessages.join('\n')}`
Expand Down
6 changes: 4 additions & 2 deletions src/components/DataBrowser.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,18 @@ export default defineComponent({
}
});

const openDicomWeb = computed(() => dicomWeb.isConfigured);
watch(
computed(() => dicomWeb.isConfigured),
openDicomWeb,
(configured) => {
if (configured) {
panels.value.push(DICOM_WEB_KEY);
} else {
// Remove from panels to avoid error in vuetify group.ts
removeFromArray(panels.value, DICOM_WEB_KEY);
}
}
},
{ immediate: true }
);

watch(
Expand Down
4 changes: 4 additions & 0 deletions src/components/EditableChipList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,8 @@ const itemsToRender = computed(() =>
background-color: rgb(var(--v-theme-selection-bg-color));
border-color: rgb(var(--v-theme-selection-border-color));
}

.v-chip:deep() .v-chip__content {
width: 100%;
}
</style>
4 changes: 2 additions & 2 deletions src/components/SampleDataBrowser.vue
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ export default defineComponent({
if (!loadResult) {
throw new Error('Did not receive a load result');
}
if (!loadResult.ok) {
throw loadResult.errors[0].cause;
if (loadResult.type === 'error') {
throw loadResult.error;
}

const selection = convertSuccessResultToDataSelection(loadResult);
Expand Down
10 changes: 10 additions & 0 deletions src/composables/untilLoaded.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { computed, unref } from 'vue';
import { until } from '@vueuse/core';
import useChunkStore from '../store/chunks';

export function untilLoaded(imageID: string) {
const doneLoading = computed(
() => !unref(useChunkStore().chunkImageById[imageID].isLoading)
);
return until(doneLoading).toBe(true);
}
Loading
Loading