dicom-curate 0.34.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -0
- package/dist/esm/applyMappingsWorker.js +87 -33
- package/dist/esm/collectMappings.js +35 -0
- package/dist/esm/composeSpecs.js +10 -0
- package/dist/esm/curateDict.js +35 -0
- package/dist/esm/curateOne.js +87 -33
- package/dist/esm/index.js +55 -1
- package/dist/types/types.d.ts +6 -0
- package/dist/umd/dicom-curate.umd.js +154 -32
- package/dist/umd/dicom-curate.umd.js.map +1 -1
- package/dist/umd/dicom-curate.umd.min.js +6 -6
- package/dist/umd/dicom-curate.umd.min.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -287,6 +287,64 @@ export function sampleBatchCurationSpecification(): TCurationSpecification {
|
|
|
287
287
|
}
|
|
288
288
|
```
|
|
289
289
|
|
|
290
|
+
## Excluding files with preExclude and postExclude
|
|
291
|
+
|
|
292
|
+
The curation specification supports two optional exclusion functions that let you skip files at different stages of processing. Both return **`true` to exclude** the file; returning `false` (or omitting the function entirely) lets the file through.
|
|
293
|
+
|
|
294
|
+
### preExclude — skip before mapping
|
|
295
|
+
|
|
296
|
+
`preExclude` receives a `parser` with access to the **original, unmapped DICOM tags**. Return `true` to skip the file entirely — it will not be mapped, written, or uploaded.
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
export function myCurationSpec(): TCurationSpecification {
|
|
300
|
+
return {
|
|
301
|
+
// ... other fields ...
|
|
302
|
+
|
|
303
|
+
// Exclude files whose PatientID doesn't match the expected study format.
|
|
304
|
+
preExclude(parser) {
|
|
305
|
+
return !/^AB\d{2}-\d{3}$/.test(parser.getDicom('PatientID'))
|
|
306
|
+
},
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### postExclude — skip after mapping
|
|
312
|
+
|
|
313
|
+
`postExclude` receives a `parser` whose `getDicom()` returns **de-identified tag values** (PS315E de-identification has already run at this point), and exposes the computed output path as `parser.outputFilePath`. Return `true` to skip writing or uploading the mapped file.
|
|
314
|
+
|
|
315
|
+
Note: `parser.getFilePathComp()` still returns **input** path components inside `postExclude`, the same as in `preExclude`. Only `parser.outputFilePath` reflects the post-mapping location, as a full string.
|
|
316
|
+
|
|
317
|
+
```ts
|
|
318
|
+
export function myCurationSpec(): TCurationSpecification {
|
|
319
|
+
return {
|
|
320
|
+
// ... other fields ...
|
|
321
|
+
|
|
322
|
+
// Exclude structured reports and files routed to an 'exclude' output folder.
|
|
323
|
+
postExclude(parser) {
|
|
324
|
+
if (parser.getDicom('Modality') === 'SR') return true
|
|
325
|
+
if (parser.outputFilePath.includes('/exclude/')) return true
|
|
326
|
+
return false
|
|
327
|
+
},
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Behaviour notes
|
|
333
|
+
|
|
334
|
+
- **Exclusions are re-evaluated on every run.** When a `preExclude` or `postExclude` is configured, the "unchanged source bytes" short-circuit is disabled so an exclusion added in a later run takes effect even if the file itself didn't change.
|
|
335
|
+
- **Composition across multiple specs is OR.** When `composeSpecs` merges specs that each define `preExclude` / `postExclude`, the composed function excludes a file if **any** spec's function returns `true`. Evaluation short-circuits on the first `true`.
|
|
336
|
+
- **Exceptions are fail-safe.** If an exclusion function throws, the file is treated as **included** and the error message is appended to `mapResults.errors`.
|
|
337
|
+
|
|
338
|
+
### Result shape
|
|
339
|
+
|
|
340
|
+
When a file is excluded, `curateOne` / `curateMany` still returns a result object for it. The `excluded` field indicates which function rejected it:
|
|
341
|
+
|
|
342
|
+
```ts
|
|
343
|
+
// 'pre' — excluded by preExclude (file was never mapped)
|
|
344
|
+
// 'post' — excluded by postExclude (file was mapped but not written)
|
|
345
|
+
result.excluded // => 'pre' | 'post' | undefined
|
|
346
|
+
```
|
|
347
|
+
|
|
290
348
|
## DICOM Conformance Notes
|
|
291
349
|
|
|
292
350
|
dicom-curate
|
|
@@ -67499,38 +67499,6 @@ var require_acorn_globals = __commonJS({
|
|
|
67499
67499
|
// src/curateOne.ts
|
|
67500
67500
|
var dcmjs7 = __toESM(require_dcmjs(), 1);
|
|
67501
67501
|
|
|
67502
|
-
// src/createNestedDirectories.ts
|
|
67503
|
-
async function createNestedDirectories(topLevelDirectoryHandle, path) {
|
|
67504
|
-
const pathSegments = path.split("/").filter((segment) => segment !== "");
|
|
67505
|
-
let currentDirectoryHandle = topLevelDirectoryHandle;
|
|
67506
|
-
for (const segment of pathSegments) {
|
|
67507
|
-
try {
|
|
67508
|
-
const entry = await currentDirectoryHandle.getDirectoryHandle(segment, {
|
|
67509
|
-
create: false
|
|
67510
|
-
});
|
|
67511
|
-
currentDirectoryHandle = entry;
|
|
67512
|
-
} catch (error2) {
|
|
67513
|
-
if (error2.name === "NotFoundError") {
|
|
67514
|
-
const entry = await currentDirectoryHandle.getDirectoryHandle(segment, {
|
|
67515
|
-
create: true
|
|
67516
|
-
});
|
|
67517
|
-
currentDirectoryHandle = entry;
|
|
67518
|
-
} else {
|
|
67519
|
-
return false;
|
|
67520
|
-
}
|
|
67521
|
-
}
|
|
67522
|
-
}
|
|
67523
|
-
return currentDirectoryHandle;
|
|
67524
|
-
}
|
|
67525
|
-
|
|
67526
|
-
// src/curateDict.ts
|
|
67527
|
-
var dcmjs6 = __toESM(require_dcmjs(), 1);
|
|
67528
|
-
var import_lodash4 = __toESM(require_lodash(), 1);
|
|
67529
|
-
|
|
67530
|
-
// src/collectMappings.ts
|
|
67531
|
-
var dcmjs4 = __toESM(require_dcmjs(), 1);
|
|
67532
|
-
var import_lodash3 = __toESM(require_lodash(), 1);
|
|
67533
|
-
|
|
67534
67502
|
// src/config/specVersion.ts
|
|
67535
67503
|
var specVersion = "3.0";
|
|
67536
67504
|
|
|
@@ -79352,11 +79320,53 @@ function composeSpecs(specOrComposedSpec) {
|
|
|
79352
79320
|
return [...prev(p4), ...next(p4)];
|
|
79353
79321
|
};
|
|
79354
79322
|
}
|
|
79323
|
+
if (spec.preExclude) {
|
|
79324
|
+
const prev = final.preExclude;
|
|
79325
|
+
const next = spec.preExclude;
|
|
79326
|
+
final.preExclude = prev ? (p4) => prev(p4) || next(p4) : next;
|
|
79327
|
+
}
|
|
79328
|
+
if (spec.postExclude) {
|
|
79329
|
+
const prev = final.postExclude;
|
|
79330
|
+
const next = spec.postExclude;
|
|
79331
|
+
final.postExclude = prev ? (p4) => prev(p4) || next(p4) : next;
|
|
79332
|
+
}
|
|
79355
79333
|
}
|
|
79356
79334
|
final.dicomPS315EOptions = mergePs315(ps315Chain);
|
|
79357
79335
|
return final;
|
|
79358
79336
|
}
|
|
79359
79337
|
|
|
79338
|
+
// src/createNestedDirectories.ts
|
|
79339
|
+
async function createNestedDirectories(topLevelDirectoryHandle, path) {
|
|
79340
|
+
const pathSegments = path.split("/").filter((segment) => segment !== "");
|
|
79341
|
+
let currentDirectoryHandle = topLevelDirectoryHandle;
|
|
79342
|
+
for (const segment of pathSegments) {
|
|
79343
|
+
try {
|
|
79344
|
+
const entry = await currentDirectoryHandle.getDirectoryHandle(segment, {
|
|
79345
|
+
create: false
|
|
79346
|
+
});
|
|
79347
|
+
currentDirectoryHandle = entry;
|
|
79348
|
+
} catch (error2) {
|
|
79349
|
+
if (error2.name === "NotFoundError") {
|
|
79350
|
+
const entry = await currentDirectoryHandle.getDirectoryHandle(segment, {
|
|
79351
|
+
create: true
|
|
79352
|
+
});
|
|
79353
|
+
currentDirectoryHandle = entry;
|
|
79354
|
+
} else {
|
|
79355
|
+
return false;
|
|
79356
|
+
}
|
|
79357
|
+
}
|
|
79358
|
+
}
|
|
79359
|
+
return currentDirectoryHandle;
|
|
79360
|
+
}
|
|
79361
|
+
|
|
79362
|
+
// src/curateDict.ts
|
|
79363
|
+
var dcmjs6 = __toESM(require_dcmjs(), 1);
|
|
79364
|
+
var import_lodash4 = __toESM(require_lodash(), 1);
|
|
79365
|
+
|
|
79366
|
+
// src/collectMappings.ts
|
|
79367
|
+
var dcmjs4 = __toESM(require_dcmjs(), 1);
|
|
79368
|
+
var import_lodash3 = __toESM(require_lodash(), 1);
|
|
79369
|
+
|
|
79360
79370
|
// src/getParser.ts
|
|
79361
79371
|
var dcmjs3 = __toESM(require_dcmjs(), 1);
|
|
79362
79372
|
var import_lodash2 = __toESM(require_lodash(), 1);
|
|
@@ -79484,9 +79494,21 @@ function collectMappings(inputFilePath, dicomData, mappingOptions) {
|
|
|
79484
79494
|
mappingOptions.columnMappings,
|
|
79485
79495
|
finalSpec.additionalData
|
|
79486
79496
|
);
|
|
79497
|
+
let preExcludeError;
|
|
79498
|
+
try {
|
|
79499
|
+
if (finalSpec.preExclude?.(parser)) {
|
|
79500
|
+
mapResults.excluded = "pre";
|
|
79501
|
+
return [naturalData, mapResults];
|
|
79502
|
+
}
|
|
79503
|
+
} catch (e4) {
|
|
79504
|
+
preExcludeError = `preExclude threw an error: ${e4 instanceof Error ? e4.message : String(e4)} \u2014 treating file as included (fail-safe)`;
|
|
79505
|
+
}
|
|
79487
79506
|
if (!mappingOptions.skipValidation) {
|
|
79488
79507
|
mapResults.errors = finalSpec.errors(parser).filter(([, failure]) => failure).map(([message]) => message);
|
|
79489
79508
|
}
|
|
79509
|
+
if (preExcludeError) {
|
|
79510
|
+
mapResults.errors.push(preExcludeError);
|
|
79511
|
+
}
|
|
79490
79512
|
if (finalSpec.additionalData?.type === "listing") {
|
|
79491
79513
|
const { lookups, info, collect } = finalSpec.additionalData.collect(parser);
|
|
79492
79514
|
const collectByValue = collect.map((item) => {
|
|
@@ -79538,6 +79560,19 @@ function collectMappings(inputFilePath, dicomData, mappingOptions) {
|
|
|
79538
79560
|
mapResults.outputFilePath = parts.join("/");
|
|
79539
79561
|
}
|
|
79540
79562
|
}
|
|
79563
|
+
try {
|
|
79564
|
+
if (finalSpec.postExclude?.({
|
|
79565
|
+
...parser,
|
|
79566
|
+
outputFilePath: mapResults.outputFilePath
|
|
79567
|
+
})) {
|
|
79568
|
+
mapResults.excluded = "post";
|
|
79569
|
+
return [naturalData, mapResults];
|
|
79570
|
+
}
|
|
79571
|
+
} catch (e4) {
|
|
79572
|
+
mapResults.errors.push(
|
|
79573
|
+
`postExclude threw an error: ${e4 instanceof Error ? e4.message : String(e4)} \u2014 treating file as included (fail-safe)`
|
|
79574
|
+
);
|
|
79575
|
+
}
|
|
79541
79576
|
if (!mappingOptions.skipModifications) {
|
|
79542
79577
|
const dicomMap = finalSpec.modifyDicomHeader(parser);
|
|
79543
79578
|
for (const attrPath in dicomMap) {
|
|
@@ -79804,6 +79839,12 @@ async function loadS3Client() {
|
|
|
79804
79839
|
}
|
|
79805
79840
|
|
|
79806
79841
|
// src/curateOne.ts
|
|
79842
|
+
function specHasFilter(mappingOptions) {
|
|
79843
|
+
if (mappingOptions.curationSpec === "none")
|
|
79844
|
+
return false;
|
|
79845
|
+
const composed = composeSpecs(mappingOptions.curationSpec());
|
|
79846
|
+
return !!(composed.preExclude ?? composed.postExclude);
|
|
79847
|
+
}
|
|
79807
79848
|
async function curateOne({
|
|
79808
79849
|
fileInfo,
|
|
79809
79850
|
outputTarget,
|
|
@@ -79950,7 +79991,8 @@ async function curateOne({
|
|
|
79950
79991
|
};
|
|
79951
79992
|
return retval;
|
|
79952
79993
|
};
|
|
79953
|
-
|
|
79994
|
+
const hasFilter = specHasFilter(mappingOptions);
|
|
79995
|
+
if (canSkip && previousSourceFileInfo && !hasFilter) {
|
|
79954
79996
|
return noMapResult();
|
|
79955
79997
|
}
|
|
79956
79998
|
let mappedDicomData;
|
|
@@ -79997,6 +80039,18 @@ async function curateOne({
|
|
|
79997
80039
|
write: () => fileArrayBuffer
|
|
79998
80040
|
};
|
|
79999
80041
|
}
|
|
80042
|
+
if (clonedMapResults.excluded) {
|
|
80043
|
+
clonedMapResults.mappingRequired = false;
|
|
80044
|
+
clonedMapResults.fileInfo = {
|
|
80045
|
+
name: fileInfo.name,
|
|
80046
|
+
size: fileInfo.size,
|
|
80047
|
+
path: fileInfo.path,
|
|
80048
|
+
mtime,
|
|
80049
|
+
preMappedHash
|
|
80050
|
+
};
|
|
80051
|
+
clonedMapResults.curationTime = performance.now() - startTime;
|
|
80052
|
+
return clonedMapResults;
|
|
80053
|
+
}
|
|
80000
80054
|
clonedMapResults.mappingRequired = true;
|
|
80001
80055
|
} else {
|
|
80002
80056
|
mappedDicomData = {
|
|
@@ -35345,6 +35345,16 @@ function composeSpecs(specOrComposedSpec) {
|
|
|
35345
35345
|
return [...prev(p), ...next(p)];
|
|
35346
35346
|
};
|
|
35347
35347
|
}
|
|
35348
|
+
if (spec.preExclude) {
|
|
35349
|
+
const prev = final.preExclude;
|
|
35350
|
+
const next = spec.preExclude;
|
|
35351
|
+
final.preExclude = prev ? (p) => prev(p) || next(p) : next;
|
|
35352
|
+
}
|
|
35353
|
+
if (spec.postExclude) {
|
|
35354
|
+
const prev = final.postExclude;
|
|
35355
|
+
const next = spec.postExclude;
|
|
35356
|
+
final.postExclude = prev ? (p) => prev(p) || next(p) : next;
|
|
35357
|
+
}
|
|
35348
35358
|
}
|
|
35349
35359
|
final.dicomPS315EOptions = mergePs315(ps315Chain);
|
|
35350
35360
|
return final;
|
|
@@ -35477,9 +35487,21 @@ function collectMappings(inputFilePath, dicomData, mappingOptions) {
|
|
|
35477
35487
|
mappingOptions.columnMappings,
|
|
35478
35488
|
finalSpec.additionalData
|
|
35479
35489
|
);
|
|
35490
|
+
let preExcludeError;
|
|
35491
|
+
try {
|
|
35492
|
+
if (finalSpec.preExclude?.(parser)) {
|
|
35493
|
+
mapResults.excluded = "pre";
|
|
35494
|
+
return [naturalData, mapResults];
|
|
35495
|
+
}
|
|
35496
|
+
} catch (e) {
|
|
35497
|
+
preExcludeError = `preExclude threw an error: ${e instanceof Error ? e.message : String(e)} \u2014 treating file as included (fail-safe)`;
|
|
35498
|
+
}
|
|
35480
35499
|
if (!mappingOptions.skipValidation) {
|
|
35481
35500
|
mapResults.errors = finalSpec.errors(parser).filter(([, failure]) => failure).map(([message]) => message);
|
|
35482
35501
|
}
|
|
35502
|
+
if (preExcludeError) {
|
|
35503
|
+
mapResults.errors.push(preExcludeError);
|
|
35504
|
+
}
|
|
35483
35505
|
if (finalSpec.additionalData?.type === "listing") {
|
|
35484
35506
|
const { lookups, info, collect } = finalSpec.additionalData.collect(parser);
|
|
35485
35507
|
const collectByValue = collect.map((item) => {
|
|
@@ -35531,6 +35553,19 @@ function collectMappings(inputFilePath, dicomData, mappingOptions) {
|
|
|
35531
35553
|
mapResults.outputFilePath = parts.join("/");
|
|
35532
35554
|
}
|
|
35533
35555
|
}
|
|
35556
|
+
try {
|
|
35557
|
+
if (finalSpec.postExclude?.({
|
|
35558
|
+
...parser,
|
|
35559
|
+
outputFilePath: mapResults.outputFilePath
|
|
35560
|
+
})) {
|
|
35561
|
+
mapResults.excluded = "post";
|
|
35562
|
+
return [naturalData, mapResults];
|
|
35563
|
+
}
|
|
35564
|
+
} catch (e) {
|
|
35565
|
+
mapResults.errors.push(
|
|
35566
|
+
`postExclude threw an error: ${e instanceof Error ? e.message : String(e)} \u2014 treating file as included (fail-safe)`
|
|
35567
|
+
);
|
|
35568
|
+
}
|
|
35534
35569
|
if (!mappingOptions.skipModifications) {
|
|
35535
35570
|
const dicomMap = finalSpec.modifyDicomHeader(parser);
|
|
35536
35571
|
for (const attrPath in dicomMap) {
|
package/dist/esm/composeSpecs.js
CHANGED
|
@@ -33805,6 +33805,16 @@ function composeSpecs(specOrComposedSpec) {
|
|
|
33805
33805
|
return [...prev(p), ...next(p)];
|
|
33806
33806
|
};
|
|
33807
33807
|
}
|
|
33808
|
+
if (spec.preExclude) {
|
|
33809
|
+
const prev = final.preExclude;
|
|
33810
|
+
const next = spec.preExclude;
|
|
33811
|
+
final.preExclude = prev ? (p) => prev(p) || next(p) : next;
|
|
33812
|
+
}
|
|
33813
|
+
if (spec.postExclude) {
|
|
33814
|
+
const prev = final.postExclude;
|
|
33815
|
+
const next = spec.postExclude;
|
|
33816
|
+
final.postExclude = prev ? (p) => prev(p) || next(p) : next;
|
|
33817
|
+
}
|
|
33808
33818
|
}
|
|
33809
33819
|
final.dicomPS315EOptions = mergePs315(ps315Chain);
|
|
33810
33820
|
return final;
|
package/dist/esm/curateDict.js
CHANGED
|
@@ -35363,6 +35363,16 @@ function composeSpecs(specOrComposedSpec) {
|
|
|
35363
35363
|
return [...prev(p), ...next(p)];
|
|
35364
35364
|
};
|
|
35365
35365
|
}
|
|
35366
|
+
if (spec.preExclude) {
|
|
35367
|
+
const prev = final.preExclude;
|
|
35368
|
+
const next = spec.preExclude;
|
|
35369
|
+
final.preExclude = prev ? (p) => prev(p) || next(p) : next;
|
|
35370
|
+
}
|
|
35371
|
+
if (spec.postExclude) {
|
|
35372
|
+
const prev = final.postExclude;
|
|
35373
|
+
const next = spec.postExclude;
|
|
35374
|
+
final.postExclude = prev ? (p) => prev(p) || next(p) : next;
|
|
35375
|
+
}
|
|
35366
35376
|
}
|
|
35367
35377
|
final.dicomPS315EOptions = mergePs315(ps315Chain);
|
|
35368
35378
|
return final;
|
|
@@ -35495,9 +35505,21 @@ function collectMappings(inputFilePath, dicomData, mappingOptions) {
|
|
|
35495
35505
|
mappingOptions.columnMappings,
|
|
35496
35506
|
finalSpec.additionalData
|
|
35497
35507
|
);
|
|
35508
|
+
let preExcludeError;
|
|
35509
|
+
try {
|
|
35510
|
+
if (finalSpec.preExclude?.(parser)) {
|
|
35511
|
+
mapResults.excluded = "pre";
|
|
35512
|
+
return [naturalData, mapResults];
|
|
35513
|
+
}
|
|
35514
|
+
} catch (e) {
|
|
35515
|
+
preExcludeError = `preExclude threw an error: ${e instanceof Error ? e.message : String(e)} \u2014 treating file as included (fail-safe)`;
|
|
35516
|
+
}
|
|
35498
35517
|
if (!mappingOptions.skipValidation) {
|
|
35499
35518
|
mapResults.errors = finalSpec.errors(parser).filter(([, failure]) => failure).map(([message]) => message);
|
|
35500
35519
|
}
|
|
35520
|
+
if (preExcludeError) {
|
|
35521
|
+
mapResults.errors.push(preExcludeError);
|
|
35522
|
+
}
|
|
35501
35523
|
if (finalSpec.additionalData?.type === "listing") {
|
|
35502
35524
|
const { lookups, info, collect } = finalSpec.additionalData.collect(parser);
|
|
35503
35525
|
const collectByValue = collect.map((item) => {
|
|
@@ -35549,6 +35571,19 @@ function collectMappings(inputFilePath, dicomData, mappingOptions) {
|
|
|
35549
35571
|
mapResults.outputFilePath = parts.join("/");
|
|
35550
35572
|
}
|
|
35551
35573
|
}
|
|
35574
|
+
try {
|
|
35575
|
+
if (finalSpec.postExclude?.({
|
|
35576
|
+
...parser,
|
|
35577
|
+
outputFilePath: mapResults.outputFilePath
|
|
35578
|
+
})) {
|
|
35579
|
+
mapResults.excluded = "post";
|
|
35580
|
+
return [naturalData, mapResults];
|
|
35581
|
+
}
|
|
35582
|
+
} catch (e) {
|
|
35583
|
+
mapResults.errors.push(
|
|
35584
|
+
`postExclude threw an error: ${e instanceof Error ? e.message : String(e)} \u2014 treating file as included (fail-safe)`
|
|
35585
|
+
);
|
|
35586
|
+
}
|
|
35552
35587
|
if (!mappingOptions.skipModifications) {
|
|
35553
35588
|
const dicomMap = finalSpec.modifyDicomHeader(parser);
|
|
35554
35589
|
for (const attrPath in dicomMap) {
|
package/dist/esm/curateOne.js
CHANGED
|
@@ -61208,38 +61208,6 @@ var require_dist_cjs71 = __commonJS({
|
|
|
61208
61208
|
// src/curateOne.ts
|
|
61209
61209
|
var dcmjs7 = __toESM(require_dcmjs(), 1);
|
|
61210
61210
|
|
|
61211
|
-
// src/createNestedDirectories.ts
|
|
61212
|
-
async function createNestedDirectories(topLevelDirectoryHandle, path) {
|
|
61213
|
-
const pathSegments = path.split("/").filter((segment) => segment !== "");
|
|
61214
|
-
let currentDirectoryHandle = topLevelDirectoryHandle;
|
|
61215
|
-
for (const segment of pathSegments) {
|
|
61216
|
-
try {
|
|
61217
|
-
const entry = await currentDirectoryHandle.getDirectoryHandle(segment, {
|
|
61218
|
-
create: false
|
|
61219
|
-
});
|
|
61220
|
-
currentDirectoryHandle = entry;
|
|
61221
|
-
} catch (error2) {
|
|
61222
|
-
if (error2.name === "NotFoundError") {
|
|
61223
|
-
const entry = await currentDirectoryHandle.getDirectoryHandle(segment, {
|
|
61224
|
-
create: true
|
|
61225
|
-
});
|
|
61226
|
-
currentDirectoryHandle = entry;
|
|
61227
|
-
} else {
|
|
61228
|
-
return false;
|
|
61229
|
-
}
|
|
61230
|
-
}
|
|
61231
|
-
}
|
|
61232
|
-
return currentDirectoryHandle;
|
|
61233
|
-
}
|
|
61234
|
-
|
|
61235
|
-
// src/curateDict.ts
|
|
61236
|
-
var dcmjs6 = __toESM(require_dcmjs(), 1);
|
|
61237
|
-
var import_lodash4 = __toESM(require_lodash(), 1);
|
|
61238
|
-
|
|
61239
|
-
// src/collectMappings.ts
|
|
61240
|
-
var dcmjs4 = __toESM(require_dcmjs(), 1);
|
|
61241
|
-
var import_lodash3 = __toESM(require_lodash(), 1);
|
|
61242
|
-
|
|
61243
61211
|
// src/config/specVersion.ts
|
|
61244
61212
|
var specVersion = "3.0";
|
|
61245
61213
|
|
|
@@ -73061,11 +73029,53 @@ function composeSpecs(specOrComposedSpec) {
|
|
|
73061
73029
|
return [...prev(p4), ...next(p4)];
|
|
73062
73030
|
};
|
|
73063
73031
|
}
|
|
73032
|
+
if (spec.preExclude) {
|
|
73033
|
+
const prev = final.preExclude;
|
|
73034
|
+
const next = spec.preExclude;
|
|
73035
|
+
final.preExclude = prev ? (p4) => prev(p4) || next(p4) : next;
|
|
73036
|
+
}
|
|
73037
|
+
if (spec.postExclude) {
|
|
73038
|
+
const prev = final.postExclude;
|
|
73039
|
+
const next = spec.postExclude;
|
|
73040
|
+
final.postExclude = prev ? (p4) => prev(p4) || next(p4) : next;
|
|
73041
|
+
}
|
|
73064
73042
|
}
|
|
73065
73043
|
final.dicomPS315EOptions = mergePs315(ps315Chain);
|
|
73066
73044
|
return final;
|
|
73067
73045
|
}
|
|
73068
73046
|
|
|
73047
|
+
// src/createNestedDirectories.ts
|
|
73048
|
+
async function createNestedDirectories(topLevelDirectoryHandle, path) {
|
|
73049
|
+
const pathSegments = path.split("/").filter((segment) => segment !== "");
|
|
73050
|
+
let currentDirectoryHandle = topLevelDirectoryHandle;
|
|
73051
|
+
for (const segment of pathSegments) {
|
|
73052
|
+
try {
|
|
73053
|
+
const entry = await currentDirectoryHandle.getDirectoryHandle(segment, {
|
|
73054
|
+
create: false
|
|
73055
|
+
});
|
|
73056
|
+
currentDirectoryHandle = entry;
|
|
73057
|
+
} catch (error2) {
|
|
73058
|
+
if (error2.name === "NotFoundError") {
|
|
73059
|
+
const entry = await currentDirectoryHandle.getDirectoryHandle(segment, {
|
|
73060
|
+
create: true
|
|
73061
|
+
});
|
|
73062
|
+
currentDirectoryHandle = entry;
|
|
73063
|
+
} else {
|
|
73064
|
+
return false;
|
|
73065
|
+
}
|
|
73066
|
+
}
|
|
73067
|
+
}
|
|
73068
|
+
return currentDirectoryHandle;
|
|
73069
|
+
}
|
|
73070
|
+
|
|
73071
|
+
// src/curateDict.ts
|
|
73072
|
+
var dcmjs6 = __toESM(require_dcmjs(), 1);
|
|
73073
|
+
var import_lodash4 = __toESM(require_lodash(), 1);
|
|
73074
|
+
|
|
73075
|
+
// src/collectMappings.ts
|
|
73076
|
+
var dcmjs4 = __toESM(require_dcmjs(), 1);
|
|
73077
|
+
var import_lodash3 = __toESM(require_lodash(), 1);
|
|
73078
|
+
|
|
73069
73079
|
// src/getParser.ts
|
|
73070
73080
|
var dcmjs3 = __toESM(require_dcmjs(), 1);
|
|
73071
73081
|
var import_lodash2 = __toESM(require_lodash(), 1);
|
|
@@ -73193,9 +73203,21 @@ function collectMappings(inputFilePath, dicomData, mappingOptions) {
|
|
|
73193
73203
|
mappingOptions.columnMappings,
|
|
73194
73204
|
finalSpec.additionalData
|
|
73195
73205
|
);
|
|
73206
|
+
let preExcludeError;
|
|
73207
|
+
try {
|
|
73208
|
+
if (finalSpec.preExclude?.(parser)) {
|
|
73209
|
+
mapResults.excluded = "pre";
|
|
73210
|
+
return [naturalData, mapResults];
|
|
73211
|
+
}
|
|
73212
|
+
} catch (e4) {
|
|
73213
|
+
preExcludeError = `preExclude threw an error: ${e4 instanceof Error ? e4.message : String(e4)} \u2014 treating file as included (fail-safe)`;
|
|
73214
|
+
}
|
|
73196
73215
|
if (!mappingOptions.skipValidation) {
|
|
73197
73216
|
mapResults.errors = finalSpec.errors(parser).filter(([, failure]) => failure).map(([message]) => message);
|
|
73198
73217
|
}
|
|
73218
|
+
if (preExcludeError) {
|
|
73219
|
+
mapResults.errors.push(preExcludeError);
|
|
73220
|
+
}
|
|
73199
73221
|
if (finalSpec.additionalData?.type === "listing") {
|
|
73200
73222
|
const { lookups, info, collect } = finalSpec.additionalData.collect(parser);
|
|
73201
73223
|
const collectByValue = collect.map((item) => {
|
|
@@ -73247,6 +73269,19 @@ function collectMappings(inputFilePath, dicomData, mappingOptions) {
|
|
|
73247
73269
|
mapResults.outputFilePath = parts.join("/");
|
|
73248
73270
|
}
|
|
73249
73271
|
}
|
|
73272
|
+
try {
|
|
73273
|
+
if (finalSpec.postExclude?.({
|
|
73274
|
+
...parser,
|
|
73275
|
+
outputFilePath: mapResults.outputFilePath
|
|
73276
|
+
})) {
|
|
73277
|
+
mapResults.excluded = "post";
|
|
73278
|
+
return [naturalData, mapResults];
|
|
73279
|
+
}
|
|
73280
|
+
} catch (e4) {
|
|
73281
|
+
mapResults.errors.push(
|
|
73282
|
+
`postExclude threw an error: ${e4 instanceof Error ? e4.message : String(e4)} \u2014 treating file as included (fail-safe)`
|
|
73283
|
+
);
|
|
73284
|
+
}
|
|
73250
73285
|
if (!mappingOptions.skipModifications) {
|
|
73251
73286
|
const dicomMap = finalSpec.modifyDicomHeader(parser);
|
|
73252
73287
|
for (const attrPath in dicomMap) {
|
|
@@ -73513,6 +73548,12 @@ async function loadS3Client() {
|
|
|
73513
73548
|
}
|
|
73514
73549
|
|
|
73515
73550
|
// src/curateOne.ts
|
|
73551
|
+
function specHasFilter(mappingOptions) {
|
|
73552
|
+
if (mappingOptions.curationSpec === "none")
|
|
73553
|
+
return false;
|
|
73554
|
+
const composed = composeSpecs(mappingOptions.curationSpec());
|
|
73555
|
+
return !!(composed.preExclude ?? composed.postExclude);
|
|
73556
|
+
}
|
|
73516
73557
|
async function curateOne({
|
|
73517
73558
|
fileInfo,
|
|
73518
73559
|
outputTarget,
|
|
@@ -73659,7 +73700,8 @@ async function curateOne({
|
|
|
73659
73700
|
};
|
|
73660
73701
|
return retval;
|
|
73661
73702
|
};
|
|
73662
|
-
|
|
73703
|
+
const hasFilter = specHasFilter(mappingOptions);
|
|
73704
|
+
if (canSkip && previousSourceFileInfo && !hasFilter) {
|
|
73663
73705
|
return noMapResult();
|
|
73664
73706
|
}
|
|
73665
73707
|
let mappedDicomData;
|
|
@@ -73706,6 +73748,18 @@ async function curateOne({
|
|
|
73706
73748
|
write: () => fileArrayBuffer
|
|
73707
73749
|
};
|
|
73708
73750
|
}
|
|
73751
|
+
if (clonedMapResults.excluded) {
|
|
73752
|
+
clonedMapResults.mappingRequired = false;
|
|
73753
|
+
clonedMapResults.fileInfo = {
|
|
73754
|
+
name: fileInfo.name,
|
|
73755
|
+
size: fileInfo.size,
|
|
73756
|
+
path: fileInfo.path,
|
|
73757
|
+
mtime,
|
|
73758
|
+
preMappedHash
|
|
73759
|
+
};
|
|
73760
|
+
clonedMapResults.curationTime = performance.now() - startTime;
|
|
73761
|
+
return clonedMapResults;
|
|
73762
|
+
}
|
|
73709
73763
|
clonedMapResults.mappingRequired = true;
|
|
73710
73764
|
} else {
|
|
73711
73765
|
mappedDicomData = {
|
package/dist/esm/index.js
CHANGED
|
@@ -80855,6 +80855,16 @@ function composeSpecs(specOrComposedSpec) {
|
|
|
80855
80855
|
return [...prev(p4), ...next(p4)];
|
|
80856
80856
|
};
|
|
80857
80857
|
}
|
|
80858
|
+
if (spec.preExclude) {
|
|
80859
|
+
const prev = final.preExclude;
|
|
80860
|
+
const next = spec.preExclude;
|
|
80861
|
+
final.preExclude = prev ? (p4) => prev(p4) || next(p4) : next;
|
|
80862
|
+
}
|
|
80863
|
+
if (spec.postExclude) {
|
|
80864
|
+
const prev = final.postExclude;
|
|
80865
|
+
const next = spec.postExclude;
|
|
80866
|
+
final.postExclude = prev ? (p4) => prev(p4) || next(p4) : next;
|
|
80867
|
+
}
|
|
80858
80868
|
}
|
|
80859
80869
|
final.dicomPS315EOptions = mergePs315(ps315Chain);
|
|
80860
80870
|
return final;
|
|
@@ -81047,9 +81057,21 @@ function collectMappings(inputFilePath, dicomData, mappingOptions) {
|
|
|
81047
81057
|
mappingOptions.columnMappings,
|
|
81048
81058
|
finalSpec.additionalData
|
|
81049
81059
|
);
|
|
81060
|
+
let preExcludeError;
|
|
81061
|
+
try {
|
|
81062
|
+
if (finalSpec.preExclude?.(parser)) {
|
|
81063
|
+
mapResults.excluded = "pre";
|
|
81064
|
+
return [naturalData, mapResults];
|
|
81065
|
+
}
|
|
81066
|
+
} catch (e4) {
|
|
81067
|
+
preExcludeError = `preExclude threw an error: ${e4 instanceof Error ? e4.message : String(e4)} \u2014 treating file as included (fail-safe)`;
|
|
81068
|
+
}
|
|
81050
81069
|
if (!mappingOptions.skipValidation) {
|
|
81051
81070
|
mapResults.errors = finalSpec.errors(parser).filter(([, failure]) => failure).map(([message]) => message);
|
|
81052
81071
|
}
|
|
81072
|
+
if (preExcludeError) {
|
|
81073
|
+
mapResults.errors.push(preExcludeError);
|
|
81074
|
+
}
|
|
81053
81075
|
if (finalSpec.additionalData?.type === "listing") {
|
|
81054
81076
|
const { lookups, info, collect } = finalSpec.additionalData.collect(parser);
|
|
81055
81077
|
const collectByValue = collect.map((item) => {
|
|
@@ -81101,6 +81123,19 @@ function collectMappings(inputFilePath, dicomData, mappingOptions) {
|
|
|
81101
81123
|
mapResults.outputFilePath = parts.join("/");
|
|
81102
81124
|
}
|
|
81103
81125
|
}
|
|
81126
|
+
try {
|
|
81127
|
+
if (finalSpec.postExclude?.({
|
|
81128
|
+
...parser,
|
|
81129
|
+
outputFilePath: mapResults.outputFilePath
|
|
81130
|
+
})) {
|
|
81131
|
+
mapResults.excluded = "post";
|
|
81132
|
+
return [naturalData, mapResults];
|
|
81133
|
+
}
|
|
81134
|
+
} catch (e4) {
|
|
81135
|
+
mapResults.errors.push(
|
|
81136
|
+
`postExclude threw an error: ${e4 instanceof Error ? e4.message : String(e4)} \u2014 treating file as included (fail-safe)`
|
|
81137
|
+
);
|
|
81138
|
+
}
|
|
81104
81139
|
if (!mappingOptions.skipModifications) {
|
|
81105
81140
|
const dicomMap = finalSpec.modifyDicomHeader(parser);
|
|
81106
81141
|
for (const attrPath in dicomMap) {
|
|
@@ -81367,6 +81402,12 @@ async function loadS3Client() {
|
|
|
81367
81402
|
}
|
|
81368
81403
|
|
|
81369
81404
|
// src/curateOne.ts
|
|
81405
|
+
function specHasFilter(mappingOptions) {
|
|
81406
|
+
if (mappingOptions.curationSpec === "none")
|
|
81407
|
+
return false;
|
|
81408
|
+
const composed = composeSpecs(mappingOptions.curationSpec());
|
|
81409
|
+
return !!(composed.preExclude ?? composed.postExclude);
|
|
81410
|
+
}
|
|
81370
81411
|
async function curateOne({
|
|
81371
81412
|
fileInfo,
|
|
81372
81413
|
outputTarget,
|
|
@@ -81513,7 +81554,8 @@ async function curateOne({
|
|
|
81513
81554
|
};
|
|
81514
81555
|
return retval;
|
|
81515
81556
|
};
|
|
81516
|
-
|
|
81557
|
+
const hasFilter = specHasFilter(mappingOptions);
|
|
81558
|
+
if (canSkip && previousSourceFileInfo && !hasFilter) {
|
|
81517
81559
|
return noMapResult();
|
|
81518
81560
|
}
|
|
81519
81561
|
let mappedDicomData;
|
|
@@ -81560,6 +81602,18 @@ async function curateOne({
|
|
|
81560
81602
|
write: () => fileArrayBuffer
|
|
81561
81603
|
};
|
|
81562
81604
|
}
|
|
81605
|
+
if (clonedMapResults.excluded) {
|
|
81606
|
+
clonedMapResults.mappingRequired = false;
|
|
81607
|
+
clonedMapResults.fileInfo = {
|
|
81608
|
+
name: fileInfo.name,
|
|
81609
|
+
size: fileInfo.size,
|
|
81610
|
+
path: fileInfo.path,
|
|
81611
|
+
mtime,
|
|
81612
|
+
preMappedHash
|
|
81613
|
+
};
|
|
81614
|
+
clonedMapResults.curationTime = performance.now() - startTime;
|
|
81615
|
+
return clonedMapResults;
|
|
81616
|
+
}
|
|
81563
81617
|
clonedMapResults.mappingRequired = true;
|
|
81564
81618
|
} else {
|
|
81565
81619
|
mappedDicomData = {
|
package/dist/types/types.d.ts
CHANGED
|
@@ -148,6 +148,7 @@ export type TMapResults = {
|
|
|
148
148
|
etag?: string;
|
|
149
149
|
};
|
|
150
150
|
mappingRequired?: boolean;
|
|
151
|
+
excluded?: 'pre' | 'post';
|
|
151
152
|
curationTime?: number;
|
|
152
153
|
};
|
|
153
154
|
export type TPs315EElement = {
|
|
@@ -180,6 +181,9 @@ export type TParser = {
|
|
|
180
181
|
FILENAME: symbol;
|
|
181
182
|
FILEBASENAME: symbol;
|
|
182
183
|
};
|
|
184
|
+
export type TPostExcludeParser = TParser & {
|
|
185
|
+
outputFilePath: string;
|
|
186
|
+
};
|
|
183
187
|
type TMappingInputDirect = {
|
|
184
188
|
type: 'load';
|
|
185
189
|
collect: Record<string, RegExp | string[]>;
|
|
@@ -220,6 +224,8 @@ export type TCurationSpecification<THost extends HostProps = HostProps> = {
|
|
|
220
224
|
mapping: TMappedValues;
|
|
221
225
|
} & (TMappingInputDirect | TMappingInputTwoPass);
|
|
222
226
|
excludedFiletypes?: string[];
|
|
227
|
+
preExclude?: (parser: TParser) => boolean;
|
|
228
|
+
postExclude?: (parser: TPostExcludeParser) => boolean;
|
|
223
229
|
};
|
|
224
230
|
type TProgressMessageBase = {
|
|
225
231
|
totalFiles?: number;
|