dicom-curate 0.7.1 → 0.8.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.
@@ -25122,6 +25122,9 @@ async function createNestedDirectories(topLevelDirectoryHandle, path) {
25122
25122
  return currentDirectoryHandle;
25123
25123
  }
25124
25124
 
25125
+ // src/config/specVersion.ts
25126
+ var specVersion = "3.0";
25127
+
25125
25128
  // src/config/dicom/dummyValues.ts
25126
25129
  var dummyValues = {
25127
25130
  AE: "INVALID_AE",
@@ -36337,7 +36340,7 @@ function deidentifyPS315E({
36337
36340
  // We should clean it
36338
36341
  normalName in cleanPolicyMap && // Either cleanDescOpt is off (we handle those separately) or
36339
36342
  // if it's on, it's not one of the exception tags
36340
- (!cleanDescriptorsOption || !cleanDescriptorsExceptions.includes(normalName)) && // No condition for an exception applies
36343
+ (!cleanDescriptorsOption || cleanDescriptorsExceptions === false || !cleanDescriptorsExceptions.includes(normalName)) && // No condition for an exception applies
36341
36344
  !cleanPolicyMap[normalName].exceptCondition?.(data3) && data3[name] !== ""
36342
36345
  ) {
36343
36346
  const { rule } = cleanPolicyMap[normalName];
@@ -36441,7 +36444,7 @@ function deidentifyPS315E({
36441
36444
  // protocols, though it still could be cleaned as described in Note 2"
36442
36445
  // Note: Removes tags which contain Comment|Desciprion at any location
36443
36446
  // inside the string.
36444
- cleanDescriptorsOption && normalName.match(/Comment|Description/) && !cleanDescriptorsExceptions.includes(normalName) && data3[name] !== ""
36447
+ cleanDescriptorsOption && normalName.match(/Comment|Description/) && (cleanDescriptorsExceptions === false || !cleanDescriptorsExceptions.includes(normalName)) && data3[name] !== ""
36445
36448
  ) {
36446
36449
  mapResults.mappings[attrPath] = [
36447
36450
  data3[name],
@@ -36537,6 +36540,146 @@ var defaultPs315Options = {
36537
36540
  retainInstitutionIdentityOption: false
36538
36541
  };
36539
36542
 
36543
+ // src/defaultSpec.ts
36544
+ var defaultSpec = {
36545
+ version: specVersion,
36546
+ hostProps: {},
36547
+ excludedFiletypes: [],
36548
+ dicomPS315EOptions: defaultPs315Options,
36549
+ inputPathPattern: "",
36550
+ modifyDicomHeader: () => ({}),
36551
+ outputFilePathComponents: (parser) => [
36552
+ parser.protectUid(parser.getDicom("SeriesInstanceUID")),
36553
+ parser.getFilePathComp(parser.FILENAME)
36554
+ ],
36555
+ errors: () => []
36556
+ };
36557
+
36558
+ // src/composeSpecs.ts
36559
+ function concatUnique(a, b) {
36560
+ return [.../* @__PURE__ */ new Set([...a, ...b])];
36561
+ }
36562
+ function mergeRetain(a, b) {
36563
+ if (b === false)
36564
+ return false;
36565
+ if (a === false || a === void 0)
36566
+ return [...b];
36567
+ return concatUnique(a, b);
36568
+ }
36569
+ function isObj(v) {
36570
+ return !!v && typeof v === "object" && !Array.isArray(v) && !(v instanceof RegExp);
36571
+ }
36572
+ function mergeHostProps(prev, next) {
36573
+ if (!next)
36574
+ return prev;
36575
+ if (!prev)
36576
+ return next;
36577
+ const res = { ...prev };
36578
+ for (const [k, v] of Object.entries(next)) {
36579
+ const pv = res[k];
36580
+ if (Array.isArray(v)) {
36581
+ res[k] = v;
36582
+ } else if (isObj(v) && isObj(pv)) {
36583
+ res[k] = mergeHostProps(pv, v);
36584
+ } else {
36585
+ res[k] = v;
36586
+ }
36587
+ }
36588
+ return res;
36589
+ }
36590
+ function mergePs315(chain) {
36591
+ let acc = chain[0];
36592
+ for (let i = 1; i < chain.length; i++) {
36593
+ let cur = chain[i];
36594
+ if (cur === void 0)
36595
+ continue;
36596
+ if (cur === "Off") {
36597
+ acc = "Off";
36598
+ continue;
36599
+ }
36600
+ if (acc === "Off" || acc === void 0) {
36601
+ acc = { ...defaultPs315Options, ...cur };
36602
+ if (Array.isArray(cur.cleanDescriptorsExceptions)) {
36603
+ acc.cleanDescriptorsExceptions = [...cur.cleanDescriptorsExceptions];
36604
+ }
36605
+ const curRetain = cur.retainPatientCharacteristicsOption;
36606
+ if (Array.isArray(curRetain)) {
36607
+ acc.retainPatientCharacteristicsOption = [...curRetain];
36608
+ }
36609
+ continue;
36610
+ }
36611
+ const prevCleanDesc = acc.cleanDescriptorsExceptions ?? [];
36612
+ const prevRetainPatChars = acc.retainPatientCharacteristicsOption;
36613
+ acc = { ...acc, ...cur };
36614
+ if (cur.cleanDescriptorsExceptions !== void 0) {
36615
+ acc.cleanDescriptorsExceptions = mergeRetain(
36616
+ prevCleanDesc,
36617
+ cur.cleanDescriptorsExceptions
36618
+ );
36619
+ }
36620
+ if (cur.retainPatientCharacteristicsOption !== void 0) {
36621
+ acc.retainPatientCharacteristicsOption = mergeRetain(
36622
+ prevRetainPatChars,
36623
+ cur.retainPatientCharacteristicsOption
36624
+ );
36625
+ }
36626
+ }
36627
+ return acc;
36628
+ }
36629
+ function isTCurationSpec(s) {
36630
+ return typeof s !== "function";
36631
+ }
36632
+ function composeSpecs(specOrComposedSpec) {
36633
+ const specsIn = Array.isArray(specOrComposedSpec) ? specOrComposedSpec : [specOrComposedSpec];
36634
+ let final = defaultSpec;
36635
+ if (specsIn.length === 0) {
36636
+ throw new Error("composeSpecs requires a non-empty spec array");
36637
+ }
36638
+ const ctx = {};
36639
+ let ps315Chain = [final.dicomPS315EOptions];
36640
+ for (const specIn of specsIn) {
36641
+ let { ctx: c = {}, spec = { version: specVersion } } = isTCurationSpec(
36642
+ specIn
36643
+ ) ? { spec: specIn } : specIn(ctx);
36644
+ Object.assign(ctx, c);
36645
+ if (spec.version !== specVersion) {
36646
+ throw new Error(
36647
+ `All curation specification versions must be '${specVersion}'`
36648
+ );
36649
+ }
36650
+ final.hostProps = mergeHostProps(final.hostProps, spec.hostProps);
36651
+ final.inputPathPattern = spec.inputPathPattern ?? final.inputPathPattern;
36652
+ if (spec.dicomPS315EOptions) {
36653
+ ps315Chain.push(spec.dicomPS315EOptions);
36654
+ }
36655
+ if (spec.excludedFiletypes !== void 0) {
36656
+ const prev = [...final.excludedFiletypes ?? []];
36657
+ const next = spec.excludedFiletypes;
36658
+ final.excludedFiletypes = [...prev, ...next];
36659
+ }
36660
+ if (spec.additionalData !== void 0) {
36661
+ final.additionalData = spec.additionalData;
36662
+ }
36663
+ if (spec.modifyDicomHeader) {
36664
+ const prev = final.modifyDicomHeader;
36665
+ const next = spec.modifyDicomHeader;
36666
+ final.modifyDicomHeader = (p) => ({ ...prev(p), ...next(p) });
36667
+ }
36668
+ if (spec.outputFilePathComponents) {
36669
+ final.outputFilePathComponents = spec.outputFilePathComponents;
36670
+ }
36671
+ if (spec.errors) {
36672
+ const prev = final.errors;
36673
+ const next = spec.errors;
36674
+ final.errors = (p) => {
36675
+ return [...prev(p), ...next(p)];
36676
+ };
36677
+ }
36678
+ }
36679
+ final.dicomPS315EOptions = mergePs315(ps315Chain);
36680
+ return final;
36681
+ }
36682
+
36540
36683
  // src/csvMapping.ts
36541
36684
  function getCsvMapping(columnMappings, mapping, mappingKey, value) {
36542
36685
  const rowIdx = columnMappings.rowIndexByFieldValue[mappingKey][value];
@@ -36669,9 +36812,6 @@ function getParser(inputPathPattern, inputFilePath, naturalData, dicomPS315EOpti
36669
36812
  };
36670
36813
  }
36671
36814
 
36672
- // src/config/specVersion.ts
36673
- var specVersion = "3.0";
36674
-
36675
36815
  // src/collectMappings.ts
36676
36816
  var import_lodash2 = __toESM(require_lodash(), 1);
36677
36817
  function collectMappings(inputFilePath, inputFileIndex, dicomData, mappingOptions) {
@@ -36690,29 +36830,7 @@ function collectMappings(inputFilePath, inputFileIndex, dicomData, mappingOption
36690
36830
  dicomData.dict
36691
36831
  );
36692
36832
  mapResults.sourceInstanceUID = naturalData.SOPInstanceUID;
36693
- let finalSpec = {
36694
- dicomPS315EOptions: defaultPs315Options,
36695
- inputPathPattern: "",
36696
- modifyDicomHeader: () => ({}),
36697
- outputFilePathComponents: (parser2) => [
36698
- parser2.protectUid(parser2.getDicom("SeriesInstanceUID")),
36699
- parser2.getFilePathComp(parser2.FILENAME)
36700
- ],
36701
- errors: () => []
36702
- };
36703
- const { modifyDicomHeader, errors, ...restSpec } = mappingOptions.curationSpec();
36704
- if (restSpec.version !== specVersion) {
36705
- throw new Error(
36706
- `Only version ${specVersion} supported in curationSpecification`
36707
- );
36708
- }
36709
- Object.assign(finalSpec, restSpec);
36710
- if (!mappingOptions.skipModifications) {
36711
- finalSpec.modifyDicomHeader = modifyDicomHeader;
36712
- }
36713
- if (!mappingOptions.skipValidation) {
36714
- finalSpec.errors = errors;
36715
- }
36833
+ const finalSpec = composeSpecs(mappingOptions.curationSpec());
36716
36834
  const finalFilePath = finalSpec.dicomPS315EOptions === "Off" ? inputFilePath : inputFilePath.slice(0, inputFilePath.lastIndexOf("/") + 1) + `${String(inputFileIndex + 1).padStart(5, "0")}.dcm`;
36717
36835
  const parser = getParser(
36718
36836
  finalSpec.inputPathPattern,
@@ -18807,6 +18807,9 @@ ValueRepresentation.setDicomMessageClass(DicomMessage);
18807
18807
  ValueRepresentation.setTagClass(Tag);
18808
18808
  Tag.setDicomMessageClass(DicomMessage);
18809
18809
 
18810
+ // src/config/specVersion.ts
18811
+ var specVersion = "3.0";
18812
+
18810
18813
  // src/config/dicom/dummyValues.ts
18811
18814
  var dummyValues = {
18812
18815
  AE: "INVALID_AE",
@@ -30008,7 +30011,7 @@ function deidentifyPS315E({
30008
30011
  // We should clean it
30009
30012
  normalName in cleanPolicyMap && // Either cleanDescOpt is off (we handle those separately) or
30010
30013
  // if it's on, it's not one of the exception tags
30011
- (!cleanDescriptorsOption || !cleanDescriptorsExceptions.includes(normalName)) && // No condition for an exception applies
30014
+ (!cleanDescriptorsOption || cleanDescriptorsExceptions === false || !cleanDescriptorsExceptions.includes(normalName)) && // No condition for an exception applies
30012
30015
  !cleanPolicyMap[normalName].exceptCondition?.(data2) && data2[name] !== ""
30013
30016
  ) {
30014
30017
  const { rule } = cleanPolicyMap[normalName];
@@ -30112,7 +30115,7 @@ function deidentifyPS315E({
30112
30115
  // protocols, though it still could be cleaned as described in Note 2"
30113
30116
  // Note: Removes tags which contain Comment|Desciprion at any location
30114
30117
  // inside the string.
30115
- cleanDescriptorsOption && normalName.match(/Comment|Description/) && !cleanDescriptorsExceptions.includes(normalName) && data2[name] !== ""
30118
+ cleanDescriptorsOption && normalName.match(/Comment|Description/) && (cleanDescriptorsExceptions === false || !cleanDescriptorsExceptions.includes(normalName)) && data2[name] !== ""
30116
30119
  ) {
30117
30120
  mapResults.mappings[attrPath] = [
30118
30121
  data2[name],
@@ -30208,6 +30211,146 @@ var defaultPs315Options = {
30208
30211
  retainInstitutionIdentityOption: false
30209
30212
  };
30210
30213
 
30214
+ // src/defaultSpec.ts
30215
+ var defaultSpec = {
30216
+ version: specVersion,
30217
+ hostProps: {},
30218
+ excludedFiletypes: [],
30219
+ dicomPS315EOptions: defaultPs315Options,
30220
+ inputPathPattern: "",
30221
+ modifyDicomHeader: () => ({}),
30222
+ outputFilePathComponents: (parser) => [
30223
+ parser.protectUid(parser.getDicom("SeriesInstanceUID")),
30224
+ parser.getFilePathComp(parser.FILENAME)
30225
+ ],
30226
+ errors: () => []
30227
+ };
30228
+
30229
+ // src/composeSpecs.ts
30230
+ function concatUnique(a, b) {
30231
+ return [.../* @__PURE__ */ new Set([...a, ...b])];
30232
+ }
30233
+ function mergeRetain(a, b) {
30234
+ if (b === false)
30235
+ return false;
30236
+ if (a === false || a === void 0)
30237
+ return [...b];
30238
+ return concatUnique(a, b);
30239
+ }
30240
+ function isObj(v) {
30241
+ return !!v && typeof v === "object" && !Array.isArray(v) && !(v instanceof RegExp);
30242
+ }
30243
+ function mergeHostProps(prev, next) {
30244
+ if (!next)
30245
+ return prev;
30246
+ if (!prev)
30247
+ return next;
30248
+ const res = { ...prev };
30249
+ for (const [k, v] of Object.entries(next)) {
30250
+ const pv = res[k];
30251
+ if (Array.isArray(v)) {
30252
+ res[k] = v;
30253
+ } else if (isObj(v) && isObj(pv)) {
30254
+ res[k] = mergeHostProps(pv, v);
30255
+ } else {
30256
+ res[k] = v;
30257
+ }
30258
+ }
30259
+ return res;
30260
+ }
30261
+ function mergePs315(chain) {
30262
+ let acc = chain[0];
30263
+ for (let i = 1; i < chain.length; i++) {
30264
+ let cur = chain[i];
30265
+ if (cur === void 0)
30266
+ continue;
30267
+ if (cur === "Off") {
30268
+ acc = "Off";
30269
+ continue;
30270
+ }
30271
+ if (acc === "Off" || acc === void 0) {
30272
+ acc = { ...defaultPs315Options, ...cur };
30273
+ if (Array.isArray(cur.cleanDescriptorsExceptions)) {
30274
+ acc.cleanDescriptorsExceptions = [...cur.cleanDescriptorsExceptions];
30275
+ }
30276
+ const curRetain = cur.retainPatientCharacteristicsOption;
30277
+ if (Array.isArray(curRetain)) {
30278
+ acc.retainPatientCharacteristicsOption = [...curRetain];
30279
+ }
30280
+ continue;
30281
+ }
30282
+ const prevCleanDesc = acc.cleanDescriptorsExceptions ?? [];
30283
+ const prevRetainPatChars = acc.retainPatientCharacteristicsOption;
30284
+ acc = { ...acc, ...cur };
30285
+ if (cur.cleanDescriptorsExceptions !== void 0) {
30286
+ acc.cleanDescriptorsExceptions = mergeRetain(
30287
+ prevCleanDesc,
30288
+ cur.cleanDescriptorsExceptions
30289
+ );
30290
+ }
30291
+ if (cur.retainPatientCharacteristicsOption !== void 0) {
30292
+ acc.retainPatientCharacteristicsOption = mergeRetain(
30293
+ prevRetainPatChars,
30294
+ cur.retainPatientCharacteristicsOption
30295
+ );
30296
+ }
30297
+ }
30298
+ return acc;
30299
+ }
30300
+ function isTCurationSpec(s) {
30301
+ return typeof s !== "function";
30302
+ }
30303
+ function composeSpecs(specOrComposedSpec) {
30304
+ const specsIn = Array.isArray(specOrComposedSpec) ? specOrComposedSpec : [specOrComposedSpec];
30305
+ let final = defaultSpec;
30306
+ if (specsIn.length === 0) {
30307
+ throw new Error("composeSpecs requires a non-empty spec array");
30308
+ }
30309
+ const ctx = {};
30310
+ let ps315Chain = [final.dicomPS315EOptions];
30311
+ for (const specIn of specsIn) {
30312
+ let { ctx: c = {}, spec = { version: specVersion } } = isTCurationSpec(
30313
+ specIn
30314
+ ) ? { spec: specIn } : specIn(ctx);
30315
+ Object.assign(ctx, c);
30316
+ if (spec.version !== specVersion) {
30317
+ throw new Error(
30318
+ `All curation specification versions must be '${specVersion}'`
30319
+ );
30320
+ }
30321
+ final.hostProps = mergeHostProps(final.hostProps, spec.hostProps);
30322
+ final.inputPathPattern = spec.inputPathPattern ?? final.inputPathPattern;
30323
+ if (spec.dicomPS315EOptions) {
30324
+ ps315Chain.push(spec.dicomPS315EOptions);
30325
+ }
30326
+ if (spec.excludedFiletypes !== void 0) {
30327
+ const prev = [...final.excludedFiletypes ?? []];
30328
+ const next = spec.excludedFiletypes;
30329
+ final.excludedFiletypes = [...prev, ...next];
30330
+ }
30331
+ if (spec.additionalData !== void 0) {
30332
+ final.additionalData = spec.additionalData;
30333
+ }
30334
+ if (spec.modifyDicomHeader) {
30335
+ const prev = final.modifyDicomHeader;
30336
+ const next = spec.modifyDicomHeader;
30337
+ final.modifyDicomHeader = (p) => ({ ...prev(p), ...next(p) });
30338
+ }
30339
+ if (spec.outputFilePathComponents) {
30340
+ final.outputFilePathComponents = spec.outputFilePathComponents;
30341
+ }
30342
+ if (spec.errors) {
30343
+ const prev = final.errors;
30344
+ const next = spec.errors;
30345
+ final.errors = (p) => {
30346
+ return [...prev(p), ...next(p)];
30347
+ };
30348
+ }
30349
+ }
30350
+ final.dicomPS315EOptions = mergePs315(ps315Chain);
30351
+ return final;
30352
+ }
30353
+
30211
30354
  // src/csvMapping.ts
30212
30355
  function getCsvMapping(columnMappings, mapping, mappingKey, value) {
30213
30356
  const rowIdx = columnMappings.rowIndexByFieldValue[mappingKey][value];
@@ -30340,9 +30483,6 @@ function getParser(inputPathPattern, inputFilePath, naturalData, dicomPS315EOpti
30340
30483
  };
30341
30484
  }
30342
30485
 
30343
- // src/config/specVersion.ts
30344
- var specVersion = "3.0";
30345
-
30346
30486
  // src/collectMappings.ts
30347
30487
  var import_lodash2 = __toESM(require_lodash(), 1);
30348
30488
  function collectMappings(inputFilePath, inputFileIndex, dicomData, mappingOptions) {
@@ -30361,29 +30501,7 @@ function collectMappings(inputFilePath, inputFileIndex, dicomData, mappingOption
30361
30501
  dicomData.dict
30362
30502
  );
30363
30503
  mapResults.sourceInstanceUID = naturalData.SOPInstanceUID;
30364
- let finalSpec = {
30365
- dicomPS315EOptions: defaultPs315Options,
30366
- inputPathPattern: "",
30367
- modifyDicomHeader: () => ({}),
30368
- outputFilePathComponents: (parser2) => [
30369
- parser2.protectUid(parser2.getDicom("SeriesInstanceUID")),
30370
- parser2.getFilePathComp(parser2.FILENAME)
30371
- ],
30372
- errors: () => []
30373
- };
30374
- const { modifyDicomHeader, errors, ...restSpec } = mappingOptions.curationSpec();
30375
- if (restSpec.version !== specVersion) {
30376
- throw new Error(
30377
- `Only version ${specVersion} supported in curationSpecification`
30378
- );
30379
- }
30380
- Object.assign(finalSpec, restSpec);
30381
- if (!mappingOptions.skipModifications) {
30382
- finalSpec.modifyDicomHeader = modifyDicomHeader;
30383
- }
30384
- if (!mappingOptions.skipValidation) {
30385
- finalSpec.errors = errors;
30386
- }
30504
+ const finalSpec = composeSpecs(mappingOptions.curationSpec());
30387
30505
  const finalFilePath = finalSpec.dicomPS315EOptions === "Off" ? inputFilePath : inputFilePath.slice(0, inputFilePath.lastIndexOf("/") + 1) + `${String(inputFileIndex + 1).padStart(5, "0")}.dcm`;
30388
30506
  const parser = getParser(
30389
30507
  finalSpec.inputPathPattern,