dicom-curate 0.38.1 → 0.39.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.
@@ -86083,14 +86083,16 @@ function getParser(inputPathPattern, inputFilePath, naturalData, dicomPS315EOpti
86083
86083
  function getFrom(source, identifier) {
86084
86084
  return source === "dicom" ? getDicom(identifier) : getFilePathComp(identifier);
86085
86085
  }
86086
- const getMapping = !additionalData || !columnMappings ? void 0 : (
86086
+ const getMapping = !additionalData || !additionalData.mapping || !columnMappings ? void 0 : (
86087
86087
  // key: one of the keys defined in the `mapping` object
86088
- function getMapping2(key) {
86089
- const { mapping } = additionalData;
86090
- const { value: valueFn } = mapping[key];
86091
- const value = valueFn({ getDicom, getFilePathComp, getFrom });
86092
- return getCsvMapping(columnMappings, mapping, key, value);
86093
- }
86088
+ (() => {
86089
+ const mapping = additionalData.mapping;
86090
+ return function getMapping2(key) {
86091
+ const { value: valueFn } = mapping[key];
86092
+ const value = valueFn({ getDicom, getFilePathComp, getFrom });
86093
+ return getCsvMapping(columnMappings, mapping, key, value);
86094
+ };
86095
+ })()
86094
86096
  );
86095
86097
  function missingDicom(attrName) {
86096
86098
  const value = getDicom(attrName);
@@ -86183,12 +86185,12 @@ function collectMappings(inputFilePath, dicomData, mappingOptions) {
86183
86185
  });
86184
86186
  const cleanedInfo = info.map((item) => {
86185
86187
  if (Array.isArray(item[1]) && item[1].length === 1 && typeof item[1][0] === "object" && "Alphabetic" in item[1][0] && /^\d+$/.test(item[1][0].Alphabetic)) {
86186
- return [item[0], item[1][0].Alphabetic];
86188
+ return item.length > 2 ? [item[0], item[1][0].Alphabetic, item[2]] : [item[0], item[1][0].Alphabetic];
86187
86189
  } else {
86188
86190
  return item;
86189
86191
  }
86190
86192
  });
86191
- mapResults.listing = { info: cleanedInfo, collectByValue };
86193
+ mapResults.listing = { info: cleanedInfo, collectByValue, lookups };
86192
86194
  }
86193
86195
  if (!mappingOptions.skipModifications) {
86194
86196
  mapResults.outputFilePath = finalSpec.outputFilePathComponents(parser).join("/");
@@ -34147,14 +34147,16 @@ function getParser(inputPathPattern, inputFilePath, naturalData, dicomPS315EOpti
34147
34147
  function getFrom(source, identifier) {
34148
34148
  return source === "dicom" ? getDicom(identifier) : getFilePathComp(identifier);
34149
34149
  }
34150
- const getMapping = !additionalData || !columnMappings ? void 0 : (
34150
+ const getMapping = !additionalData || !additionalData.mapping || !columnMappings ? void 0 : (
34151
34151
  // key: one of the keys defined in the `mapping` object
34152
- function getMapping2(key) {
34153
- const { mapping } = additionalData;
34154
- const { value: valueFn } = mapping[key];
34155
- const value = valueFn({ getDicom, getFilePathComp, getFrom });
34156
- return getCsvMapping(columnMappings, mapping, key, value);
34157
- }
34152
+ (() => {
34153
+ const mapping = additionalData.mapping;
34154
+ return function getMapping2(key) {
34155
+ const { value: valueFn } = mapping[key];
34156
+ const value = valueFn({ getDicom, getFilePathComp, getFrom });
34157
+ return getCsvMapping(columnMappings, mapping, key, value);
34158
+ };
34159
+ })()
34158
34160
  );
34159
34161
  function missingDicom(attrName) {
34160
34162
  const value = getDicom(attrName);
@@ -34247,12 +34249,12 @@ function collectMappings(inputFilePath, dicomData, mappingOptions) {
34247
34249
  });
34248
34250
  const cleanedInfo = info.map((item) => {
34249
34251
  if (Array.isArray(item[1]) && item[1].length === 1 && typeof item[1][0] === "object" && "Alphabetic" in item[1][0] && /^\d+$/.test(item[1][0].Alphabetic)) {
34250
- return [item[0], item[1][0].Alphabetic];
34252
+ return item.length > 2 ? [item[0], item[1][0].Alphabetic, item[2]] : [item[0], item[1][0].Alphabetic];
34251
34253
  } else {
34252
34254
  return item;
34253
34255
  }
34254
34256
  });
34255
- mapResults.listing = { info: cleanedInfo, collectByValue };
34257
+ mapResults.listing = { info: cleanedInfo, collectByValue, lookups };
34256
34258
  }
34257
34259
  if (!mappingOptions.skipModifications) {
34258
34260
  mapResults.outputFilePath = finalSpec.outputFilePathComponents(parser).join("/");
@@ -0,0 +1,40 @@
1
+ // src/config/sampleSummaryCurationSpecification.ts
2
+ function sampleSummaryCurationSpecification() {
3
+ const hostProps = { protocolNumber: "Demo" };
4
+ return {
5
+ inputPathPattern: "any",
6
+ additionalData: {
7
+ type: "listing",
8
+ collect: (parser) => ({
9
+ lookups: {
10
+ PerSeries: parser.getDicom("SeriesInstanceUID")
11
+ },
12
+ info: [
13
+ ["StudyInstanceUID", parser.getDicom("StudyInstanceUID")],
14
+ ["SeriesInstanceUID", parser.getDicom("SeriesInstanceUID")],
15
+ ["SeriesDescription", parser.getDicom("SeriesDescription")],
16
+ ["FolderX", parser.getFilePathComp(0)],
17
+ ["FolderY", parser.getFilePathComp(1)],
18
+ ["SOPInstanceUIDs", parser.getDicom("SOPInstanceUID"), "list"]
19
+ ],
20
+ collect: []
21
+ }),
22
+ output: {
23
+ path: "reports/series_summary.csv",
24
+ rowKey: "PerSeries"
25
+ }
26
+ // No `mapping` => single pass, write CSV summary instead of DICOMs.
27
+ },
28
+ version: "3.0",
29
+ hostProps,
30
+ // No de-identification or header modification in summary mode.
31
+ dicomPS315EOptions: "Off",
32
+ modifyDicomHeader: () => ({}),
33
+ // Unused when consumers run with skipModifications.
34
+ outputFilePathComponents: () => [],
35
+ errors: () => []
36
+ };
37
+ }
38
+ export {
39
+ sampleSummaryCurationSpecification
40
+ };
@@ -34164,14 +34164,16 @@ function getParser(inputPathPattern, inputFilePath, naturalData, dicomPS315EOpti
34164
34164
  function getFrom(source, identifier) {
34165
34165
  return source === "dicom" ? getDicom(identifier) : getFilePathComp(identifier);
34166
34166
  }
34167
- const getMapping = !additionalData || !columnMappings ? void 0 : (
34167
+ const getMapping = !additionalData || !additionalData.mapping || !columnMappings ? void 0 : (
34168
34168
  // key: one of the keys defined in the `mapping` object
34169
- function getMapping2(key) {
34170
- const { mapping } = additionalData;
34171
- const { value: valueFn } = mapping[key];
34172
- const value = valueFn({ getDicom, getFilePathComp, getFrom });
34173
- return getCsvMapping(columnMappings, mapping, key, value);
34174
- }
34169
+ (() => {
34170
+ const mapping = additionalData.mapping;
34171
+ return function getMapping2(key) {
34172
+ const { value: valueFn } = mapping[key];
34173
+ const value = valueFn({ getDicom, getFilePathComp, getFrom });
34174
+ return getCsvMapping(columnMappings, mapping, key, value);
34175
+ };
34176
+ })()
34175
34177
  );
34176
34178
  function missingDicom(attrName) {
34177
34179
  const value = getDicom(attrName);
@@ -34264,12 +34266,12 @@ function collectMappings(inputFilePath, dicomData, mappingOptions) {
34264
34266
  });
34265
34267
  const cleanedInfo = info.map((item) => {
34266
34268
  if (Array.isArray(item[1]) && item[1].length === 1 && typeof item[1][0] === "object" && "Alphabetic" in item[1][0] && /^\d+$/.test(item[1][0].Alphabetic)) {
34267
- return [item[0], item[1][0].Alphabetic];
34269
+ return item.length > 2 ? [item[0], item[1][0].Alphabetic, item[2]] : [item[0], item[1][0].Alphabetic];
34268
34270
  } else {
34269
34271
  return item;
34270
34272
  }
34271
34273
  });
34272
- mapResults.listing = { info: cleanedInfo, collectByValue };
34274
+ mapResults.listing = { info: cleanedInfo, collectByValue, lookups };
34273
34275
  }
34274
34276
  if (!mappingOptions.skipModifications) {
34275
34277
  mapResults.outputFilePath = finalSpec.outputFilePathComponents(parser).join("/");
@@ -79792,14 +79792,16 @@ function getParser(inputPathPattern, inputFilePath, naturalData, dicomPS315EOpti
79792
79792
  function getFrom(source, identifier) {
79793
79793
  return source === "dicom" ? getDicom(identifier) : getFilePathComp(identifier);
79794
79794
  }
79795
- const getMapping = !additionalData || !columnMappings ? void 0 : (
79795
+ const getMapping = !additionalData || !additionalData.mapping || !columnMappings ? void 0 : (
79796
79796
  // key: one of the keys defined in the `mapping` object
79797
- function getMapping2(key) {
79798
- const { mapping } = additionalData;
79799
- const { value: valueFn } = mapping[key];
79800
- const value = valueFn({ getDicom, getFilePathComp, getFrom });
79801
- return getCsvMapping(columnMappings, mapping, key, value);
79802
- }
79797
+ (() => {
79798
+ const mapping = additionalData.mapping;
79799
+ return function getMapping2(key) {
79800
+ const { value: valueFn } = mapping[key];
79801
+ const value = valueFn({ getDicom, getFilePathComp, getFrom });
79802
+ return getCsvMapping(columnMappings, mapping, key, value);
79803
+ };
79804
+ })()
79803
79805
  );
79804
79806
  function missingDicom(attrName) {
79805
79807
  const value = getDicom(attrName);
@@ -79892,12 +79894,12 @@ function collectMappings(inputFilePath, dicomData, mappingOptions) {
79892
79894
  });
79893
79895
  const cleanedInfo = info.map((item) => {
79894
79896
  if (Array.isArray(item[1]) && item[1].length === 1 && typeof item[1][0] === "object" && "Alphabetic" in item[1][0] && /^\d+$/.test(item[1][0].Alphabetic)) {
79895
- return [item[0], item[1][0].Alphabetic];
79897
+ return item.length > 2 ? [item[0], item[1][0].Alphabetic, item[2]] : [item[0], item[1][0].Alphabetic];
79896
79898
  } else {
79897
79899
  return item;
79898
79900
  }
79899
79901
  });
79900
- mapResults.listing = { info: cleanedInfo, collectByValue };
79902
+ mapResults.listing = { info: cleanedInfo, collectByValue, lookups };
79901
79903
  }
79902
79904
  if (!mappingOptions.skipModifications) {
79903
79905
  mapResults.outputFilePath = finalSpec.outputFilePathComponents(parser).join("/");
@@ -32798,14 +32798,16 @@ function getParser(inputPathPattern, inputFilePath, naturalData, dicomPS315EOpti
32798
32798
  function getFrom(source, identifier) {
32799
32799
  return source === "dicom" ? getDicom(identifier) : getFilePathComp(identifier);
32800
32800
  }
32801
- const getMapping = !additionalData || !columnMappings ? void 0 : (
32801
+ const getMapping = !additionalData || !additionalData.mapping || !columnMappings ? void 0 : (
32802
32802
  // key: one of the keys defined in the `mapping` object
32803
- function getMapping2(key) {
32804
- const { mapping } = additionalData;
32805
- const { value: valueFn } = mapping[key];
32806
- const value = valueFn({ getDicom, getFilePathComp, getFrom });
32807
- return getCsvMapping(columnMappings, mapping, key, value);
32808
- }
32803
+ (() => {
32804
+ const mapping = additionalData.mapping;
32805
+ return function getMapping2(key) {
32806
+ const { value: valueFn } = mapping[key];
32807
+ const value = valueFn({ getDicom, getFilePathComp, getFrom });
32808
+ return getCsvMapping(columnMappings, mapping, key, value);
32809
+ };
32810
+ })()
32809
32811
  );
32810
32812
  function missingDicom(attrName) {
32811
32813
  const value = getDicom(attrName);
package/dist/esm/index.js CHANGED
@@ -87646,14 +87646,16 @@ function getParser(inputPathPattern, inputFilePath, naturalData, dicomPS315EOpti
87646
87646
  function getFrom(source, identifier) {
87647
87647
  return source === "dicom" ? getDicom(identifier) : getFilePathComp(identifier);
87648
87648
  }
87649
- const getMapping = !additionalData || !columnMappings ? void 0 : (
87649
+ const getMapping = !additionalData || !additionalData.mapping || !columnMappings ? void 0 : (
87650
87650
  // key: one of the keys defined in the `mapping` object
87651
- function getMapping2(key) {
87652
- const { mapping } = additionalData;
87653
- const { value: valueFn } = mapping[key];
87654
- const value = valueFn({ getDicom, getFilePathComp, getFrom });
87655
- return getCsvMapping(columnMappings, mapping, key, value);
87656
- }
87651
+ (() => {
87652
+ const mapping = additionalData.mapping;
87653
+ return function getMapping2(key) {
87654
+ const { value: valueFn } = mapping[key];
87655
+ const value = valueFn({ getDicom, getFilePathComp, getFrom });
87656
+ return getCsvMapping(columnMappings, mapping, key, value);
87657
+ };
87658
+ })()
87657
87659
  );
87658
87660
  function missingDicom(attrName) {
87659
87661
  const value = getDicom(attrName);
@@ -87746,12 +87748,12 @@ function collectMappings(inputFilePath, dicomData, mappingOptions) {
87746
87748
  });
87747
87749
  const cleanedInfo = info.map((item) => {
87748
87750
  if (Array.isArray(item[1]) && item[1].length === 1 && typeof item[1][0] === "object" && "Alphabetic" in item[1][0] && /^\d+$/.test(item[1][0].Alphabetic)) {
87749
- return [item[0], item[1][0].Alphabetic];
87751
+ return item.length > 2 ? [item[0], item[1][0].Alphabetic, item[2]] : [item[0], item[1][0].Alphabetic];
87750
87752
  } else {
87751
87753
  return item;
87752
87754
  }
87753
87755
  });
87754
- mapResults.listing = { info: cleanedInfo, collectByValue };
87756
+ mapResults.listing = { info: cleanedInfo, collectByValue, lookups };
87755
87757
  }
87756
87758
  if (!mappingOptions.skipModifications) {
87757
87759
  mapResults.outputFilePath = finalSpec.outputFilePathComponents(parser).join("/");
@@ -95006,7 +95008,7 @@ async function collectMappingOptions(organizeOptions) {
95006
95008
  ));
95007
95009
  }
95008
95010
  let columnMappings;
95009
- if (organizeOptions.table && additionalData) {
95011
+ if (organizeOptions.table && additionalData?.mapping) {
95010
95012
  columnMappings = extractColumnMappings(
95011
95013
  organizeOptions.table,
95012
95014
  additionalData.mapping
@@ -95017,6 +95019,11 @@ async function collectMappingOptions(organizeOptions) {
95017
95019
  const skipValidation = organizeOptions.skipValidation ?? false;
95018
95020
  const hashMethod = organizeOptions.hashMethod;
95019
95021
  const hashPartSize = organizeOptions.hashPartSize;
95022
+ if (additionalData?.type === "listing" && additionalData.output && !skipWrite) {
95023
+ throw new Error(
95024
+ "additionalData.output (summary-table mode) requires skipWrite: true \u2014 summary specs produce a CSV summary and no curated output."
95025
+ );
95026
+ }
95020
95027
  const dateOffset = organizeOptions.dateOffset;
95021
95028
  if (requiresDateOffset(deIdOpts) && !dateOffset?.match(iso8601)) {
95022
95029
  throw new Error(
@@ -86083,14 +86083,16 @@ function getParser(inputPathPattern, inputFilePath, naturalData, dicomPS315EOpti
86083
86083
  function getFrom(source, identifier) {
86084
86084
  return source === "dicom" ? getDicom(identifier) : getFilePathComp(identifier);
86085
86085
  }
86086
- const getMapping = !additionalData || !columnMappings ? void 0 : (
86086
+ const getMapping = !additionalData || !additionalData.mapping || !columnMappings ? void 0 : (
86087
86087
  // key: one of the keys defined in the `mapping` object
86088
- function getMapping2(key) {
86089
- const { mapping } = additionalData;
86090
- const { value: valueFn } = mapping[key];
86091
- const value = valueFn({ getDicom, getFilePathComp, getFrom });
86092
- return getCsvMapping(columnMappings, mapping, key, value);
86093
- }
86088
+ (() => {
86089
+ const mapping = additionalData.mapping;
86090
+ return function getMapping2(key) {
86091
+ const { value: valueFn } = mapping[key];
86092
+ const value = valueFn({ getDicom, getFilePathComp, getFrom });
86093
+ return getCsvMapping(columnMappings, mapping, key, value);
86094
+ };
86095
+ })()
86094
86096
  );
86095
86097
  function missingDicom(attrName) {
86096
86098
  const value = getDicom(attrName);
@@ -86183,12 +86185,12 @@ function collectMappings(inputFilePath, dicomData, mappingOptions) {
86183
86185
  });
86184
86186
  const cleanedInfo = info.map((item) => {
86185
86187
  if (Array.isArray(item[1]) && item[1].length === 1 && typeof item[1][0] === "object" && "Alphabetic" in item[1][0] && /^\d+$/.test(item[1][0].Alphabetic)) {
86186
- return [item[0], item[1][0].Alphabetic];
86188
+ return item.length > 2 ? [item[0], item[1][0].Alphabetic, item[2]] : [item[0], item[1][0].Alphabetic];
86187
86189
  } else {
86188
86190
  return item;
86189
86191
  }
86190
86192
  });
86191
- mapResults.listing = { info: cleanedInfo, collectByValue };
86193
+ mapResults.listing = { info: cleanedInfo, collectByValue, lookups };
86192
86194
  }
86193
86195
  if (!mappingOptions.skipModifications) {
86194
86196
  mapResults.outputFilePath = finalSpec.outputFilePathComponents(parser).join("/");
@@ -0,0 +1,2 @@
1
+ import type { TCurationSpecification } from '../types';
2
+ export declare function sampleSummaryCurationSpecification(): TCurationSpecification;
@@ -189,6 +189,9 @@ export type TMapResults = {
189
189
  listing?: {
190
190
  info: TMappingTwoPassInfo[];
191
191
  collectByValue: [...TMappingTwoPassCollect, string | number][];
192
+ lookups: {
193
+ [lookupField: string]: string;
194
+ };
192
195
  };
193
196
  mappedBlob?: Blob;
194
197
  outputUpload?: {
@@ -236,13 +239,19 @@ export type TPostExcludeParser = TParser & {
236
239
  type TMappingInputDirect = {
237
240
  type: 'load';
238
241
  collect: Record<string, RegExp | string[]>;
242
+ mapping: TMappedValues;
239
243
  };
240
- type TMappingTwoPassInfo = [name: string, value: string];
244
+ type TMappingTwoPassInfoMode = 'list';
245
+ type TMappingTwoPassInfo = [name: string, value: string] | [name: string, value: string, mode: TMappingTwoPassInfoMode];
241
246
  type TMappingTwoPassCollect = [
242
247
  value: string,
243
248
  format: RegExp | string[],
244
249
  lookupField: string
245
250
  ];
251
+ type TSummaryOutput = {
252
+ path: string;
253
+ rowKey: string;
254
+ };
246
255
  type TMappingInputTwoPass = {
247
256
  type: 'listing';
248
257
  collect: (parser: Pick<TParser, 'getDicom' | 'getFilePathComp' | 'getFrom'>) => {
@@ -252,7 +261,13 @@ type TMappingInputTwoPass = {
252
261
  info: TMappingTwoPassInfo[];
253
262
  collect: TMappingTwoPassCollect[];
254
263
  };
255
- };
264
+ } & ({
265
+ mapping: TMappedValues;
266
+ output?: never;
267
+ } | {
268
+ output: TSummaryOutput;
269
+ mapping?: never;
270
+ });
256
271
  type HPPrimitive = string | number | boolean | null | RegExp | ((...args: any[]) => any);
257
272
  export type HPValue = HPPrimitive | {
258
273
  [k: string]: HPValue;
@@ -269,9 +284,7 @@ export type TCurationSpecification<THost extends HostProps = HostProps> = {
269
284
  dicomPS315EOptions: TPs315Options | 'Off';
270
285
  inputPathPattern: string;
271
286
  hostProps: THost;
272
- additionalData?: {
273
- mapping: TMappedValues;
274
- } & (TMappingInputDirect | TMappingInputTwoPass);
287
+ additionalData?: TMappingInputDirect | TMappingInputTwoPass;
275
288
  excludedFiletypes?: string[];
276
289
  preExclude?: (parser: TParser) => boolean;
277
290
  postExclude?: (parser: TPostExcludeParser) => boolean;
@@ -53836,15 +53836,17 @@
53836
53836
  ? getDicom(identifier)
53837
53837
  : getFilePathComp(identifier);
53838
53838
  }
53839
- const getMapping = !additionalData || !columnMappings
53839
+ const getMapping = !additionalData || !additionalData.mapping || !columnMappings
53840
53840
  ? undefined
53841
53841
  : // key: one of the keys defined in the `mapping` object
53842
- function getMapping(key) {
53843
- const { mapping } = additionalData;
53844
- const { value: valueFn } = mapping[key];
53845
- const value = valueFn({ getDicom, getFilePathComp, getFrom });
53846
- return getCsvMapping(columnMappings, mapping, key, value);
53847
- };
53842
+ (() => {
53843
+ const mapping = additionalData.mapping;
53844
+ return function getMapping(key) {
53845
+ const { value: valueFn } = mapping[key];
53846
+ const value = valueFn({ getDicom, getFilePathComp, getFrom });
53847
+ return getCsvMapping(columnMappings, mapping, key, value);
53848
+ };
53849
+ })();
53848
53850
  function missingDicom(attrName) {
53849
53851
  const value = getDicom(attrName);
53850
53852
  return typeof value === 'undefined' || value === '';
@@ -53933,20 +53935,23 @@
53933
53935
  const lookupValue = lookups[lookupField];
53934
53936
  return [...item, lookupValue];
53935
53937
  });
53936
- // FIXME: Bug in dcmjs
53938
+ // FIXME: Bug in dcmjs. Flatten single-element PN arrays, preserving any
53939
+ // optional aggregation mode marker (3rd tuple element) untouched.
53937
53940
  const cleanedInfo = info.map((item) => {
53938
53941
  if (Array.isArray(item[1]) &&
53939
53942
  item[1].length === 1 &&
53940
53943
  typeof item[1][0] === 'object' &&
53941
53944
  'Alphabetic' in item[1][0] &&
53942
53945
  /^\d+$/.test(item[1][0].Alphabetic)) {
53943
- return [item[0], item[1][0].Alphabetic];
53946
+ return (item.length > 2
53947
+ ? [item[0], item[1][0].Alphabetic, item[2]]
53948
+ : [item[0], item[1][0].Alphabetic]);
53944
53949
  }
53945
53950
  else {
53946
53951
  return item;
53947
53952
  }
53948
53953
  });
53949
- mapResults.listing = { info: cleanedInfo, collectByValue };
53954
+ mapResults.listing = { info: cleanedInfo, collectByValue, lookups };
53950
53955
  }
53951
53956
  if (!mappingOptions.skipModifications) {
53952
53957
  mapResults.outputFilePath = finalSpec
@@ -125428,15 +125433,17 @@
125428
125433
  ? getDicom(identifier)
125429
125434
  : getFilePathComp(identifier);
125430
125435
  }
125431
- const getMapping = !additionalData || !columnMappings
125436
+ const getMapping = !additionalData || !additionalData.mapping || !columnMappings
125432
125437
  ? undefined
125433
125438
  : // key: one of the keys defined in the `mapping` object
125434
- function getMapping(key) {
125435
- const { mapping } = additionalData;
125436
- const { value: valueFn } = mapping[key];
125437
- const value = valueFn({ getDicom, getFilePathComp, getFrom });
125438
- return getCsvMapping(columnMappings, mapping, key, value);
125439
- };
125439
+ (() => {
125440
+ const mapping = additionalData.mapping;
125441
+ return function getMapping(key) {
125442
+ const { value: valueFn } = mapping[key];
125443
+ const value = valueFn({ getDicom, getFilePathComp, getFrom });
125444
+ return getCsvMapping(columnMappings, mapping, key, value);
125445
+ };
125446
+ })();
125440
125447
  function missingDicom(attrName) {
125441
125448
  const value = getDicom(attrName);
125442
125449
  return typeof value === 'undefined' || value === '';
@@ -125525,20 +125532,23 @@
125525
125532
  const lookupValue = lookups[lookupField];
125526
125533
  return [...item, lookupValue];
125527
125534
  });
125528
- // FIXME: Bug in dcmjs
125535
+ // FIXME: Bug in dcmjs. Flatten single-element PN arrays, preserving any
125536
+ // optional aggregation mode marker (3rd tuple element) untouched.
125529
125537
  const cleanedInfo = info.map((item) => {
125530
125538
  if (Array.isArray(item[1]) &&
125531
125539
  item[1].length === 1 &&
125532
125540
  typeof item[1][0] === 'object' &&
125533
125541
  'Alphabetic' in item[1][0] &&
125534
125542
  /^\d+$/.test(item[1][0].Alphabetic)) {
125535
- return [item[0], item[1][0].Alphabetic];
125543
+ return (item.length > 2
125544
+ ? [item[0], item[1][0].Alphabetic, item[2]]
125545
+ : [item[0], item[1][0].Alphabetic]);
125536
125546
  }
125537
125547
  else {
125538
125548
  return item;
125539
125549
  }
125540
125550
  });
125541
- mapResults.listing = { info: cleanedInfo, collectByValue };
125551
+ mapResults.listing = { info: cleanedInfo, collectByValue, lookups };
125542
125552
  }
125543
125553
  if (!mappingOptions.skipModifications) {
125544
125554
  mapResults.outputFilePath = finalSpec
@@ -141144,7 +141154,7 @@
141144
141154
  // The need for mapping can come from additionalData or from the
141145
141155
  // retainLongitudinalTemporalInformationOptions option
141146
141156
  let columnMappings;
141147
- if (organizeOptions.table && additionalData) {
141157
+ if (organizeOptions.table && additionalData?.mapping) {
141148
141158
  columnMappings = extractColumnMappings(organizeOptions.table, additionalData.mapping);
141149
141159
  }
141150
141160
  const skipWrite = organizeOptions.skipWrite ?? false;
@@ -141152,6 +141162,15 @@
141152
141162
  const skipValidation = organizeOptions.skipValidation ?? false;
141153
141163
  const hashMethod = organizeOptions.hashMethod;
141154
141164
  const hashPartSize = organizeOptions.hashPartSize;
141165
+ // Summary-table mode (additionalData.output set) produces a CSV summary and
141166
+ // no curated DICOMs, so it must run read-only. skipWrite is the option that
141167
+ // actually prevents any file from being written/transferred (skipModifications
141168
+ // only governs whether content is changed). Fail fast on misuse.
141169
+ if (additionalData?.type === 'listing' &&
141170
+ additionalData.output &&
141171
+ !skipWrite) {
141172
+ throw new Error('additionalData.output (summary-table mode) requires skipWrite: true — summary specs produce a CSV summary and no curated output.');
141173
+ }
141155
141174
  const dateOffset = organizeOptions.dateOffset;
141156
141175
  if (requiresDateOffset(deIdOpts) && !dateOffset?.match(iso8601)) {
141157
141176
  throw new Error('When using "Offset" for retainLongitudinalTemporalInformationOptions, an iso8601 compatible dateOffset must be provided.');