Skip to content

WIP - feat/extended-download-dicomweb #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
studies/

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
Expand Down
33 changes: 23 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
To install, clone the directory and run:

```
yarn install
yarn link:exec
bun install
bun run build
bun run link:exec
```

# dcmjs commands
Expand All @@ -29,13 +30,25 @@ dicomwebjs dump https://d33do7qe4w26qo.cloudfront.net/dicomweb/studies/1.3.6.1.4
dicomwebjs dump testdata/studies/1.2.276.1.74.1.2.132733202464108492637644434464108492/series/2.16.840.1.113883.3.8467.132733202477512857637644434477512857/metadata.gz
```

## File Locations
Files dicomweb can be paths to JSON files. However, tree structure data must follow the Static DICOMweb format, specifically starting at `studies/` relative to the base directory, and containing some/all of the ones below.
## download `url`

dicomwebjs can be used to download a directory of file locations. By default it will fetch everything at and below the specified URL, plus referenced bulkdata. Bulkdata will be
placed in the `studies/<studyUID>/bulkdata` directory, with metadata references to the bulkdata using the `../../bulkdata/<path>` relative URI locations.

### Example Commands

```
dicomwebjs download https://d33do7qe4w26qo.cloudfront.net/dicomweb/studies/1.3.6.1.4.1.14519.5.2.1.4792.2001.105216574054253895819671475627 -d ~/dicomweb
```

### File Locations

Files dicomweb can be paths to JSON files. However, tree structure data must follow the Static DICOMweb format, specifically starting at `studies/` relative to the base directory, and containing some/all of the ones below.
Note that un-compressed files are acceptable as well, but will not be found on a search.

* `studies/index.json.gz` - an index in DICOMweb QIDO response format for the studies
* `studies/<studyUID>/index.json.gz` - the index entry of this study
* `studies/<studyUID>/series/index.json.gz` - the series QIDO response
* `studies/<studyUID>/series/<seriesUID>/metadata.gz` - the metadata WADO response
* `studies/<studyUID>/bulkdata/...` - bulkdata files
* `studies/<studyUID>/series/<seriesUID>/instances/<instanceUID>/frame/<frameNumber>` - compressed frame data
- `studies/index.json.gz` - an index in DICOMweb QIDO response format for the studies
- `studies/<studyUID>/index.json.gz` - the index entry of this study
- `studies/<studyUID>/series/index.json.gz` - the series QIDO response
- `studies/<studyUID>/series/<seriesUID>/metadata.gz` - the metadata WADO response
- `studies/<studyUID>/bulkdata/...` - bulkdata files
- `studies/<studyUID>/series/<seriesUID>/instances/<instanceUID>/frame/<frameNumber>` - compressed frame data
56 changes: 56 additions & 0 deletions bin/cliDownload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { DicomAccess } from "@dcmjs/commands";

const action = async (url, options) => {
try {
const destination = await DicomAccess.createInstance(options.directory, {
...options,
scheme: "file",
});

// Create access instance (currently supports only DICOMweb)
console.log(
"πŸ”Œ Creating DICOM access instance...",
url,
"to",
options.directory
);
const access = await DicomAccess.createInstance(url, options);

const { StudyInstanceUID: studyUID } = options;
if (!studyUID) {
console.warn("Please provide a studyUID");
return -2;
}
const srcStudy = await access.queryStudy(studyUID);

await destination.store(srcStudy, options);

console.log(
`πŸŽ‰ Download complete. Study saved to: ${options.directory}/studies/${studyUID}`
);
} catch (err) {
console.error("❌ An error occurred during download:", err);
process.exit(1);
}

process.exit(0);
};

// CLI registration
export default async function cliDownload(program) {
program
.command("download")
.description("Download a full DICOM study from any supported source")
.argument("<url>", "DICOMweb URL or local path (e.g. ./study, scp://...)")
.option(
"-S, --StudyInstanceUID <StudyInstanceUID>",
"StudyInstanceUID to download"
)
.option(
"-d, --directory <targetDir>",
"Download to local directory",
"./studies"
)
.option("--debug", "Enable debug logging")
.action(action);
}
43 changes: 27 additions & 16 deletions bin/dicomwebjs.js
100644 β†’ 100755
Original file line number Diff line number Diff line change
@@ -1,35 +1,46 @@
#!/usr/bin/env node
import { Command } from 'commander';
import { dicomweb, instanceDicom, dumpDicom } from '../src/index.js';
#!/usr/bin/env bun
import { Command } from "commander";
import { dicomweb, instanceDicom, dumpDicom } from "../src/index.js";
import cliDownload from "./cliDownload.js";

const program = new Command();

program.option(
"-s, --study <studyInstanceUID>",
"Download a specific study instance UID"
);

program
.name('dicomwebjs')
.description('dicomwebjs based tools for manipulation of DICOMweb')
.version('0.0.1')
.option('--seriesUID <seriesUID>', 'For a specific seriesUID');
.name("dicomwebjs")
.description("dicomwebjs based tools for manipulation of DICOMweb")
.version("0.0.1")
.option("--seriesUID <seriesUID>", "For a specific seriesUID");

program.command('dump')
.description('Dump a dicomweb file')
.argument('<dicomwebUrl>', 'dicomweb URL or file location')
program
.command("dump")
.description("Dump a dicomweb file")
.argument("<dicomwebUrl>", "dicomweb URL or file location")
.option("--debug", "Set debug level logging")
.action(async (fileName, options) => {
const qido = await dicomweb.readDicomWeb(fileName, options);
for (const dict of qido) {
dumpDicom({ dict });
}
});

program.command('instance')
.description('Write the instance data')
.argument('<part10>', 'part 10 file')
.option('-p, --pretty', 'Pretty print')
program
.command("instance")
.description("Write the instance data")
.argument("<part10>", "part 10 file")
.option("-p, --pretty", "Pretty print")
.option("--debug", "Set debug level logging")
.action(async (fileName, options) => {
const qido = await dicomweb.readDicomWeb(fileName, options);
for (const dict of qido) {
instanceDicom({ dict }, options);
}
})
});

cliDownload(program);

program.parse();
program.parse();
Loading