Skip to content
43 changes: 37 additions & 6 deletions bun.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions bunfig.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[test]
preload = ["./src/test/bun-preload.ts"]
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"fmt": "biome format --write .",
"format": "biome format --write .",
"test": "biome check . && craco test --watchAll=false",
"test:bun": "bun test",
"predeploy": "REACT_APP_CONFIG=demo PUBLIC_URL='https://imagingdatacommons.github.io/slim/' ./scripts/set-git-env.sh craco build",
"deploy": "gh-pages -d build",
"clean": "rm -rf ./build ./node_modules",
Expand Down Expand Up @@ -52,11 +53,10 @@
"retry": "^0.13.1"
},
"devDependencies": {
"ajv": "6.12.6",
"@biomejs/biome": "^2.0.0",
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.17.12",
"@babel/preset-typescript": "^7.17.12",
"@biomejs/biome": "^2.0.0",
"@craco/craco": "^6.4.0",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/commit-analyzer": "^12.0.0",
Expand All @@ -67,6 +67,7 @@
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^7.1.2",
"@types/d3-dispatch": "3.0.6",
"@types/jest": "^28.1.3",
"@types/lodash": "^4.17.20",
"@types/node": "^14.14.9",
Expand All @@ -75,11 +76,13 @@
"@types/react-router-dom": "^5.3.3",
"@types/retry": "^0.12.1",
"@types/uuid": "^8.3.0",
"ajv": "6.12.6",
"copy-webpack-plugin": "9.1.0",
"craco-less": "^2.0.0",
"eslint": "^8.57.0",
"eslint-plugin-sonarjs": "^0.25.0",
"gh-pages": "^5.0.0",
"happy-dom": "^20.8.7",
"husky": "^9.1.7",
"react-scripts": "5.0.0",
"react-test-renderer": "^18.2.0",
Expand All @@ -88,6 +91,7 @@
"typescript": "^4.7.4"
},
"overrides": {
"@types/d3-dispatch": "3.0.6",
"nth-check": "2.0.1",
"wrap-ansi": "7.0.0",
"make-dir": "3.1.0",
Expand Down
1 change: 1 addition & 0 deletions src/__mocks__/dicomMicroscopyViewerMock.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const TAG_TO_KEYWORD = {
'00201206': 'NumberOfStudyRelatedSeries',
'00201208': 'NumberOfStudyRelatedInstances',
'00080061': 'ModalitiesInStudy',
'00080060': 'Modality',
'00080090': 'ReferringPhysicianName',
'0020000E': 'SeriesInstanceUID'
}
Expand Down
106 changes: 65 additions & 41 deletions src/components/DicomTagBrowser/DicomTagBrowser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import DicomMetadataStore, {
type Study,
} from '../../services/DICOMMetadataStore'
import { formatDicomDate } from '../../utils/formatDicomDate'
import { logger } from '../../utils/logger'
import { getSortedTags, type TagInfo } from './dicomTagUtils'

const { Option } = Select
Expand Down Expand Up @@ -43,6 +44,16 @@ interface DicomTagBrowserProps {
seriesInstanceUID?: string
}

function bucketContainsSopInstance(bucket: unknown[], sop: string): boolean {
if (sop === '') return false
for (const existing of bucket) {
if ((existing as Record<string, unknown>).SOPInstanceUID === sop) {
return true
}
}
return false
}

const DicomTagBrowser = ({
clients,
studyInstanceUID,
Expand Down Expand Up @@ -109,50 +120,65 @@ const DicomTagBrowser = ({
if (slides.length > 0) {
displaySets = slides
.flatMap((slide): DisplaySet[] => {
const slideDisplaySets: DisplaySet[] = []
/** One row per SeriesInstanceUID; volume/overview/label often share a series. */
const imagesBySeries = new Map<string, unknown[]>()

// Helper function to process any image type
const processImageType = (
const addImages = (
images: unknown[] | undefined,
imageType: string,
): void => {
if (images?.[0] !== undefined) {
console.info(
`Found ${images.length} ${imageType} image(s) for slide ${slide.containerIdentifier}`,
)

const img = images[0] as Record<string, unknown>
const {
SeriesDate,
SeriesTime,
SeriesNumber,
SeriesInstanceUID,
SeriesDescription,
Modality,
} = img

processedSeries.push(SeriesInstanceUID as string)

const ds: DisplaySet = {
displaySetInstanceUID: index,
SeriesDate: SeriesDate as string | undefined,
SeriesTime: SeriesTime as string | undefined,
SeriesInstanceUID: SeriesInstanceUID as string,
SeriesNumber: String(SeriesNumber),
SeriesDescription: SeriesDescription as string | undefined,
Modality: Modality as string,
images,
if (images?.[0] === undefined) return
logger.debug(
`Found ${images.length} ${imageType} image(s) for slide ${slide.containerIdentifier}`,
)
for (const image of images) {
const img = image as Record<string, unknown>
const seriesUID = img.SeriesInstanceUID as string | undefined
if (seriesUID === undefined || seriesUID === '') continue

let bucket = imagesBySeries.get(seriesUID)
if (bucket === undefined) {
processedSeries.push(seriesUID)
bucket = []
imagesBySeries.set(seriesUID, bucket)
}

const sop =
typeof img.SOPInstanceUID === 'string' ? img.SOPInstanceUID : ''
if (!bucketContainsSopInstance(bucket, sop)) {
bucket.push(image)
}
slideDisplaySets.push(ds)
index++
}
}

// Process all image types
processImageType(slide.volumeImages, 'volume')
processImageType(slide.overviewImages, 'overview')
processImageType(slide.labelImages, 'label')
addImages(slide.volumeImages, 'volume')
addImages(slide.overviewImages, 'overview')
addImages(slide.labelImages, 'label')

const slideDisplaySets: DisplaySet[] = []
for (const images of imagesBySeries.values()) {
if (images[0] === undefined) continue
const img = images[0] as Record<string, unknown>
const {
SeriesDate,
SeriesTime,
SeriesNumber,
SeriesInstanceUID,
SeriesDescription,
Modality,
} = img
slideDisplaySets.push({
displaySetInstanceUID: index,
SeriesDate: SeriesDate as string | undefined,
SeriesTime: SeriesTime as string | undefined,
SeriesInstanceUID: SeriesInstanceUID as string,
SeriesNumber: String(SeriesNumber),
SeriesDescription: SeriesDescription as string | undefined,
Modality: Modality as string,
images,
})
index++
}
return slideDisplaySets
})
.filter((set): set is DisplaySet => set !== null && set !== undefined)
Expand Down Expand Up @@ -372,12 +398,10 @@ const DicomTagBrowser = ({
matchingPaths.push(currentPath)
}

if (node.children != null) {
node.children.forEach((child) => {
const childPaths = findMatchingPaths(child, currentPath)
matchingPaths = [...matchingPaths, ...childPaths]
})
}
node.children?.forEach((child) => {
const childPaths = findMatchingPaths(child, currentPath)
matchingPaths = [...matchingPaths, ...childPaths]
})

return matchingPaths
}
Expand Down
Loading
Loading