dicom-curate 0.2.0 → 0.4.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 +4 -4
- package/dist/esm/applyMappingsWorker.js +6 -1
- package/dist/esm/collectMappings.js +16 -9
- package/dist/esm/curateDict.js +2 -2
- package/dist/esm/curateOne.js +3 -3
- package/dist/esm/deidentifyPS315E.js +4 -1
- package/dist/esm/getParser.js +11 -1
- package/dist/esm/index.js +57 -37
- package/dist/esm/scanDirectoryWorker.js +25 -16
- package/dist/types/applyMappingsWorker.d.ts +8 -1
- package/dist/types/collectMappings.d.ts +1 -1
- package/dist/types/curateDict.d.ts +1 -1
- package/dist/types/curateOne.d.ts +7 -1
- package/dist/types/deidentifyPS315E.d.ts +1 -0
- package/dist/types/getParser.d.ts +1 -1
- package/dist/types/index.d.ts +3 -2
- package/dist/types/scanDirectoryWorker.d.ts +8 -0
- package/dist/types/serializeMappingOptions.d.ts +1 -5
- package/dist/types/types.d.ts +13 -2
- package/dist/umd/dicom-curate.umd.js +258 -219
- package/dist/umd/dicom-curate.umd.js.map +1 -1
- package/dist/umd/dicom-curate.umd.min.js +2 -2
- package/dist/umd/dicom-curate.umd.min.js.map +1 -1
- package/package.json +1 -1
|
@@ -19938,173 +19938,6 @@
|
|
|
19938
19938
|
memoized.clear();
|
|
19939
19939
|
}
|
|
19940
19940
|
|
|
19941
|
-
function UniqueNumbers(padding = 5) {
|
|
19942
|
-
// Internal state to hold counters for each grouping ID
|
|
19943
|
-
let groupCounters = {};
|
|
19944
|
-
// Provide uniqueness within group
|
|
19945
|
-
function getUniqueNumberInGroup(groupingId) {
|
|
19946
|
-
groupCounters[groupingId] = groupCounters[groupingId]
|
|
19947
|
-
? groupCounters[groupingId] + 1
|
|
19948
|
-
: 1;
|
|
19949
|
-
return groupCounters[groupingId].toString().padStart(padding, '0');
|
|
19950
|
-
}
|
|
19951
|
-
function clearUniqueNumberCache() {
|
|
19952
|
-
groupCounters = {};
|
|
19953
|
-
}
|
|
19954
|
-
return { getUniqueNumberInGroup, clearUniqueNumberCache };
|
|
19955
|
-
}
|
|
19956
|
-
|
|
19957
|
-
const FILEBASENAME = Symbol('fileBasename');
|
|
19958
|
-
const FILENAME = Symbol('filename');
|
|
19959
|
-
const { getUniqueNumberInGroup, clearUniqueNumberCache } = UniqueNumbers(6);
|
|
19960
|
-
const { isUniqueInGroup, clearUniqueInGroupCache } = (function () {
|
|
19961
|
-
let cache = new Set();
|
|
19962
|
-
let lastGroupId = '';
|
|
19963
|
-
return {
|
|
19964
|
-
isUniqueInGroup(value, groupId) {
|
|
19965
|
-
if (groupId !== lastGroupId) {
|
|
19966
|
-
cache = new Set();
|
|
19967
|
-
lastGroupId = groupId;
|
|
19968
|
-
}
|
|
19969
|
-
if (cache.has(value)) {
|
|
19970
|
-
return false;
|
|
19971
|
-
}
|
|
19972
|
-
cache.add(value);
|
|
19973
|
-
return true;
|
|
19974
|
-
},
|
|
19975
|
-
clearUniqueInGroupCache() {
|
|
19976
|
-
cache = new Set();
|
|
19977
|
-
},
|
|
19978
|
-
};
|
|
19979
|
-
})();
|
|
19980
|
-
function getParser(inputPathPattern, inputFilePath, naturalData, columnMappings, additionalData) {
|
|
19981
|
-
function getDicom(attrName) {
|
|
19982
|
-
if (attrName in data$1.DicomMetaDictionary.dictionary) {
|
|
19983
|
-
// if in hex like "(0008,0100)", convert to text key
|
|
19984
|
-
attrName = data$1.DicomMetaDictionary.dictionary[attrName].name;
|
|
19985
|
-
}
|
|
19986
|
-
return naturalData[attrName];
|
|
19987
|
-
}
|
|
19988
|
-
function getFilePathComp(component) {
|
|
19989
|
-
var _a;
|
|
19990
|
-
const patternParts = inputPathPattern.split('/');
|
|
19991
|
-
const fileParts = inputFilePath.split('/');
|
|
19992
|
-
let idx;
|
|
19993
|
-
if (typeof component === 'number') {
|
|
19994
|
-
// numeric indexing (supports negatives)
|
|
19995
|
-
idx =
|
|
19996
|
-
component < 0
|
|
19997
|
-
? fileParts.length + component // -1 → last, -2 → second-to-last, etc.
|
|
19998
|
-
: component;
|
|
19999
|
-
}
|
|
20000
|
-
else if (typeof component === 'symbol') {
|
|
20001
|
-
// force last‐segment lookup on FILENAME or FILEBASENAME
|
|
20002
|
-
idx = fileParts.length - 1;
|
|
20003
|
-
}
|
|
20004
|
-
else {
|
|
20005
|
-
// string lookup against pattern
|
|
20006
|
-
idx = patternParts.indexOf(component);
|
|
20007
|
-
}
|
|
20008
|
-
// return the segment if in range, else empty string
|
|
20009
|
-
const segment = (_a = fileParts[idx]) !== null && _a !== void 0 ? _a : '';
|
|
20010
|
-
// Return last component without a suffix
|
|
20011
|
-
if (component === FILEBASENAME) {
|
|
20012
|
-
return segment.replace(/\.[^/.]+$/, '');
|
|
20013
|
-
}
|
|
20014
|
-
return segment;
|
|
20015
|
-
}
|
|
20016
|
-
function getFrom(source, identifier) {
|
|
20017
|
-
return source === 'dicom'
|
|
20018
|
-
? getDicom(identifier)
|
|
20019
|
-
: getFilePathComp(identifier);
|
|
20020
|
-
}
|
|
20021
|
-
const getMapping = !additionalData || !columnMappings
|
|
20022
|
-
? undefined
|
|
20023
|
-
: // key: one of the keys defined in the `mapping` object
|
|
20024
|
-
function getMapping(key) {
|
|
20025
|
-
const { mapping } = additionalData;
|
|
20026
|
-
const { value: valueFn } = mapping[key];
|
|
20027
|
-
const value = valueFn({ getDicom, getFilePathComp, getFrom });
|
|
20028
|
-
return getCsvMapping(columnMappings, mapping, key, value);
|
|
20029
|
-
};
|
|
20030
|
-
function missingDicom(attrName) {
|
|
20031
|
-
const value = getDicom(attrName);
|
|
20032
|
-
return typeof value === 'undefined' || value === '';
|
|
20033
|
-
}
|
|
20034
|
-
return {
|
|
20035
|
-
// This function enables errors like:
|
|
20036
|
-
// [
|
|
20037
|
-
// 'Duplicate Instance Number(s)',
|
|
20038
|
-
// !parser.isUniqueInGroup(instanceNumber, seriesUid),
|
|
20039
|
-
// ],
|
|
20040
|
-
isUniqueInGroup,
|
|
20041
|
-
getUniqueNumberInGroup,
|
|
20042
|
-
getFrom,
|
|
20043
|
-
getFilePathComp,
|
|
20044
|
-
getMapping,
|
|
20045
|
-
getDicom,
|
|
20046
|
-
missingDicom,
|
|
20047
|
-
// TODO: Phase this out in favor of ISO8601 duration handling.
|
|
20048
|
-
// Example of this logic:
|
|
20049
|
-
// ContentDate:
|
|
20050
|
-
// parser.addDays(parser.getDicom('StudyDate'), parser.getMapping(
|
|
20051
|
-
// parser.getDicom('PatientID'), 'CURR_ID', 'DATE_OFFSET')),
|
|
20052
|
-
addDays: (dicomDateString, offsetDays) => {
|
|
20053
|
-
const year = Number(dicomDateString.slice(0, 4));
|
|
20054
|
-
const monthIndex = Number(dicomDateString.slice(4, 6)) - 1;
|
|
20055
|
-
const day = Number(dicomDateString.slice(6, 8));
|
|
20056
|
-
const date = new Date(year, monthIndex, day);
|
|
20057
|
-
let time = date.getTime();
|
|
20058
|
-
const millisecondsPerDay = 1000 * 60 * 60 * 24;
|
|
20059
|
-
time += offsetDays * millisecondsPerDay;
|
|
20060
|
-
date.setTime(time);
|
|
20061
|
-
const yearString = date.getFullYear();
|
|
20062
|
-
const monthString = (date.getMonth() + 1).toString().padStart(2, '0');
|
|
20063
|
-
const dayString = date.getDate().toString().padStart(2, '0');
|
|
20064
|
-
return yearString + monthString + dayString;
|
|
20065
|
-
},
|
|
20066
|
-
FILENAME,
|
|
20067
|
-
FILEBASENAME,
|
|
20068
|
-
};
|
|
20069
|
-
}
|
|
20070
|
-
|
|
20071
|
-
function clearCaches() {
|
|
20072
|
-
clearReplaceUidCache();
|
|
20073
|
-
clearUniqueNumberCache();
|
|
20074
|
-
clearUniqueInGroupCache();
|
|
20075
|
-
}
|
|
20076
|
-
|
|
20077
|
-
function createNestedDirectories(topLevelDirectoryHandle, path) {
|
|
20078
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
20079
|
-
const pathSegments = path.split('/').filter((segment) => segment !== '');
|
|
20080
|
-
let currentDirectoryHandle = topLevelDirectoryHandle;
|
|
20081
|
-
for (const segment of pathSegments) {
|
|
20082
|
-
try {
|
|
20083
|
-
// Attempt to get the directory handle without creating it
|
|
20084
|
-
const entry = yield currentDirectoryHandle.getDirectoryHandle(segment, {
|
|
20085
|
-
create: false,
|
|
20086
|
-
});
|
|
20087
|
-
currentDirectoryHandle = entry;
|
|
20088
|
-
}
|
|
20089
|
-
catch (error) {
|
|
20090
|
-
// If the error is specifically about the directory not existing, create it
|
|
20091
|
-
if (error.name === 'NotFoundError') {
|
|
20092
|
-
const entry = yield currentDirectoryHandle.getDirectoryHandle(segment, {
|
|
20093
|
-
create: true,
|
|
20094
|
-
});
|
|
20095
|
-
currentDirectoryHandle = entry;
|
|
20096
|
-
}
|
|
20097
|
-
else {
|
|
20098
|
-
// Handle other potential errors (e.g., name conflicts)
|
|
20099
|
-
return false; // Indicate failure
|
|
20100
|
-
}
|
|
20101
|
-
}
|
|
20102
|
-
}
|
|
20103
|
-
// Return the last directory handle
|
|
20104
|
-
return currentDirectoryHandle;
|
|
20105
|
-
});
|
|
20106
|
-
}
|
|
20107
|
-
|
|
20108
19941
|
const dummyValues = {
|
|
20109
19942
|
AE: 'INVALID_AE',
|
|
20110
19943
|
CS: 'REDACTED',
|
|
@@ -49094,6 +48927,9 @@
|
|
|
49094
48927
|
function removeRetiredPrefix(name) {
|
|
49095
48928
|
return name.startsWith('RETIRED_') ? name.slice(8) : name;
|
|
49096
48929
|
}
|
|
48930
|
+
function protectUid(uid, retainUIDsOption) {
|
|
48931
|
+
return retainUIDsOption === 'Hashed' ? hashUid(uid) : memoized(uid);
|
|
48932
|
+
}
|
|
49097
48933
|
const elementNamesToAlwaysKeepSet = new Set(elementNamesToAlwaysKeep);
|
|
49098
48934
|
// Special conditions for some PS3.15 E1.1 elements.
|
|
49099
48935
|
const ps315EElements = ps315EElements$1.map((elm) => {
|
|
@@ -49296,7 +49132,7 @@
|
|
|
49296
49132
|
// UID is not a known class UID.
|
|
49297
49133
|
!(uid in uidRegistryPS3_06_A1)) {
|
|
49298
49134
|
// UIDs that need to be mapped
|
|
49299
|
-
const mappedUID =
|
|
49135
|
+
const mappedUID = protectUid(uid, retainUIDsOption);
|
|
49300
49136
|
mapResults.mappings[attrPath] = [
|
|
49301
49137
|
uid,
|
|
49302
49138
|
'replace',
|
|
@@ -49424,9 +49260,185 @@
|
|
|
49424
49260
|
retainInstitutionIdentityOption: false,
|
|
49425
49261
|
};
|
|
49426
49262
|
|
|
49263
|
+
function UniqueNumbers(padding = 5) {
|
|
49264
|
+
// Internal state to hold counters for each grouping ID
|
|
49265
|
+
let groupCounters = {};
|
|
49266
|
+
// Provide uniqueness within group
|
|
49267
|
+
function getUniqueNumberInGroup(groupingId) {
|
|
49268
|
+
groupCounters[groupingId] = groupCounters[groupingId]
|
|
49269
|
+
? groupCounters[groupingId] + 1
|
|
49270
|
+
: 1;
|
|
49271
|
+
return groupCounters[groupingId].toString().padStart(padding, '0');
|
|
49272
|
+
}
|
|
49273
|
+
function clearUniqueNumberCache() {
|
|
49274
|
+
groupCounters = {};
|
|
49275
|
+
}
|
|
49276
|
+
return { getUniqueNumberInGroup, clearUniqueNumberCache };
|
|
49277
|
+
}
|
|
49278
|
+
|
|
49279
|
+
const FILEBASENAME = Symbol('fileBasename');
|
|
49280
|
+
const FILENAME = Symbol('filename');
|
|
49281
|
+
const { getUniqueNumberInGroup, clearUniqueNumberCache } = UniqueNumbers(6);
|
|
49282
|
+
const { isUniqueInGroup, clearUniqueInGroupCache } = (function () {
|
|
49283
|
+
let cache = new Set();
|
|
49284
|
+
let lastGroupId = '';
|
|
49285
|
+
return {
|
|
49286
|
+
isUniqueInGroup(value, groupId) {
|
|
49287
|
+
if (groupId !== lastGroupId) {
|
|
49288
|
+
cache = new Set();
|
|
49289
|
+
lastGroupId = groupId;
|
|
49290
|
+
}
|
|
49291
|
+
if (cache.has(value)) {
|
|
49292
|
+
return false;
|
|
49293
|
+
}
|
|
49294
|
+
cache.add(value);
|
|
49295
|
+
return true;
|
|
49296
|
+
},
|
|
49297
|
+
clearUniqueInGroupCache() {
|
|
49298
|
+
cache = new Set();
|
|
49299
|
+
},
|
|
49300
|
+
};
|
|
49301
|
+
})();
|
|
49302
|
+
function getParser(inputPathPattern, inputFilePath, naturalData, dicomPS315EOptions, columnMappings, additionalData) {
|
|
49303
|
+
function protectUid$1(uid) {
|
|
49304
|
+
let protectedUid = uid;
|
|
49305
|
+
if (dicomPS315EOptions !== 'Off') {
|
|
49306
|
+
const { retainUIDsOption } = dicomPS315EOptions;
|
|
49307
|
+
protectedUid = protectUid(uid, retainUIDsOption);
|
|
49308
|
+
}
|
|
49309
|
+
return protectedUid;
|
|
49310
|
+
}
|
|
49311
|
+
function getDicom(attrName) {
|
|
49312
|
+
if (attrName in data$1.DicomMetaDictionary.dictionary) {
|
|
49313
|
+
// if in hex like "(0008,0100)", convert to text key
|
|
49314
|
+
attrName = data$1.DicomMetaDictionary.dictionary[attrName].name;
|
|
49315
|
+
}
|
|
49316
|
+
return naturalData[attrName];
|
|
49317
|
+
}
|
|
49318
|
+
function getFilePathComp(component) {
|
|
49319
|
+
var _a;
|
|
49320
|
+
const patternParts = inputPathPattern.split('/');
|
|
49321
|
+
const fileParts = inputFilePath.split('/');
|
|
49322
|
+
let idx;
|
|
49323
|
+
if (typeof component === 'number') {
|
|
49324
|
+
// numeric indexing (supports negatives)
|
|
49325
|
+
idx =
|
|
49326
|
+
component < 0
|
|
49327
|
+
? fileParts.length + component // -1 → last, -2 → second-to-last, etc.
|
|
49328
|
+
: component;
|
|
49329
|
+
}
|
|
49330
|
+
else if (typeof component === 'symbol') {
|
|
49331
|
+
// force last‐segment lookup on FILENAME or FILEBASENAME
|
|
49332
|
+
idx = fileParts.length - 1;
|
|
49333
|
+
}
|
|
49334
|
+
else {
|
|
49335
|
+
// string lookup against pattern
|
|
49336
|
+
idx = patternParts.indexOf(component);
|
|
49337
|
+
}
|
|
49338
|
+
// return the segment if in range, else empty string
|
|
49339
|
+
const segment = (_a = fileParts[idx]) !== null && _a !== void 0 ? _a : '';
|
|
49340
|
+
// Return last component without a suffix
|
|
49341
|
+
if (component === FILEBASENAME) {
|
|
49342
|
+
return segment.replace(/\.[^/.]+$/, '');
|
|
49343
|
+
}
|
|
49344
|
+
return segment;
|
|
49345
|
+
}
|
|
49346
|
+
function getFrom(source, identifier) {
|
|
49347
|
+
return source === 'dicom'
|
|
49348
|
+
? getDicom(identifier)
|
|
49349
|
+
: getFilePathComp(identifier);
|
|
49350
|
+
}
|
|
49351
|
+
const getMapping = !additionalData || !columnMappings
|
|
49352
|
+
? undefined
|
|
49353
|
+
: // key: one of the keys defined in the `mapping` object
|
|
49354
|
+
function getMapping(key) {
|
|
49355
|
+
const { mapping } = additionalData;
|
|
49356
|
+
const { value: valueFn } = mapping[key];
|
|
49357
|
+
const value = valueFn({ getDicom, getFilePathComp, getFrom });
|
|
49358
|
+
return getCsvMapping(columnMappings, mapping, key, value);
|
|
49359
|
+
};
|
|
49360
|
+
function missingDicom(attrName) {
|
|
49361
|
+
const value = getDicom(attrName);
|
|
49362
|
+
return typeof value === 'undefined' || value === '';
|
|
49363
|
+
}
|
|
49364
|
+
return {
|
|
49365
|
+
// This function enables errors like:
|
|
49366
|
+
// [
|
|
49367
|
+
// 'Duplicate Instance Number(s)',
|
|
49368
|
+
// !parser.isUniqueInGroup(instanceNumber, seriesUid),
|
|
49369
|
+
// ],
|
|
49370
|
+
isUniqueInGroup,
|
|
49371
|
+
getUniqueNumberInGroup,
|
|
49372
|
+
getFrom,
|
|
49373
|
+
getFilePathComp,
|
|
49374
|
+
getMapping,
|
|
49375
|
+
getDicom,
|
|
49376
|
+
missingDicom,
|
|
49377
|
+
protectUid: protectUid$1,
|
|
49378
|
+
// TODO: Phase this out in favor of ISO8601 duration handling.
|
|
49379
|
+
// Example of this logic:
|
|
49380
|
+
// ContentDate:
|
|
49381
|
+
// parser.addDays(parser.getDicom('StudyDate'), parser.getMapping(
|
|
49382
|
+
// parser.getDicom('PatientID'), 'CURR_ID', 'DATE_OFFSET')),
|
|
49383
|
+
addDays: (dicomDateString, offsetDays) => {
|
|
49384
|
+
const year = Number(dicomDateString.slice(0, 4));
|
|
49385
|
+
const monthIndex = Number(dicomDateString.slice(4, 6)) - 1;
|
|
49386
|
+
const day = Number(dicomDateString.slice(6, 8));
|
|
49387
|
+
const date = new Date(year, monthIndex, day);
|
|
49388
|
+
let time = date.getTime();
|
|
49389
|
+
const millisecondsPerDay = 1000 * 60 * 60 * 24;
|
|
49390
|
+
time += offsetDays * millisecondsPerDay;
|
|
49391
|
+
date.setTime(time);
|
|
49392
|
+
const yearString = date.getFullYear();
|
|
49393
|
+
const monthString = (date.getMonth() + 1).toString().padStart(2, '0');
|
|
49394
|
+
const dayString = date.getDate().toString().padStart(2, '0');
|
|
49395
|
+
return yearString + monthString + dayString;
|
|
49396
|
+
},
|
|
49397
|
+
FILENAME,
|
|
49398
|
+
FILEBASENAME,
|
|
49399
|
+
};
|
|
49400
|
+
}
|
|
49401
|
+
|
|
49402
|
+
function clearCaches() {
|
|
49403
|
+
clearReplaceUidCache();
|
|
49404
|
+
clearUniqueNumberCache();
|
|
49405
|
+
clearUniqueInGroupCache();
|
|
49406
|
+
}
|
|
49407
|
+
|
|
49408
|
+
function createNestedDirectories(topLevelDirectoryHandle, path) {
|
|
49409
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
49410
|
+
const pathSegments = path.split('/').filter((segment) => segment !== '');
|
|
49411
|
+
let currentDirectoryHandle = topLevelDirectoryHandle;
|
|
49412
|
+
for (const segment of pathSegments) {
|
|
49413
|
+
try {
|
|
49414
|
+
// Attempt to get the directory handle without creating it
|
|
49415
|
+
const entry = yield currentDirectoryHandle.getDirectoryHandle(segment, {
|
|
49416
|
+
create: false,
|
|
49417
|
+
});
|
|
49418
|
+
currentDirectoryHandle = entry;
|
|
49419
|
+
}
|
|
49420
|
+
catch (error) {
|
|
49421
|
+
// If the error is specifically about the directory not existing, create it
|
|
49422
|
+
if (error.name === 'NotFoundError') {
|
|
49423
|
+
const entry = yield currentDirectoryHandle.getDirectoryHandle(segment, {
|
|
49424
|
+
create: true,
|
|
49425
|
+
});
|
|
49426
|
+
currentDirectoryHandle = entry;
|
|
49427
|
+
}
|
|
49428
|
+
else {
|
|
49429
|
+
// Handle other potential errors (e.g., name conflicts)
|
|
49430
|
+
return false; // Indicate failure
|
|
49431
|
+
}
|
|
49432
|
+
}
|
|
49433
|
+
}
|
|
49434
|
+
// Return the last directory handle
|
|
49435
|
+
return currentDirectoryHandle;
|
|
49436
|
+
});
|
|
49437
|
+
}
|
|
49438
|
+
|
|
49427
49439
|
const specVersion = '2.0';
|
|
49428
49440
|
|
|
49429
|
-
function collectMappings(inputFilePath, dicomData, mappingOptions) {
|
|
49441
|
+
function collectMappings(inputFilePath, inputFileIndex, dicomData, mappingOptions) {
|
|
49430
49442
|
var _a;
|
|
49431
49443
|
const mapResults = {
|
|
49432
49444
|
// original UID for this dicomData
|
|
@@ -49445,13 +49457,15 @@
|
|
|
49445
49457
|
let finalSpec = {
|
|
49446
49458
|
dicomPS315EOptions: defaultPs315Options,
|
|
49447
49459
|
inputPathPattern: '',
|
|
49448
|
-
modifications
|
|
49449
|
-
|
|
49450
|
-
|
|
49451
|
-
|
|
49452
|
-
|
|
49453
|
-
|
|
49454
|
-
|
|
49460
|
+
modifications(parser) {
|
|
49461
|
+
return {
|
|
49462
|
+
dicomHeader: {},
|
|
49463
|
+
outputFilePathComponents: [
|
|
49464
|
+
parser.protectUid(parser.getDicom('SeriesInstanceUID')),
|
|
49465
|
+
parser.getFilePathComp(parser.FILENAME),
|
|
49466
|
+
],
|
|
49467
|
+
};
|
|
49468
|
+
},
|
|
49455
49469
|
validation: () => ({ errors: [] }),
|
|
49456
49470
|
};
|
|
49457
49471
|
const _b = mappingOptions.curationSpec(), { modifications, validation } = _b, restSpec = __rest(_b, ["modifications", "validation"]);
|
|
@@ -49466,8 +49480,13 @@
|
|
|
49466
49480
|
if (!mappingOptions.skipValidation) {
|
|
49467
49481
|
finalSpec.validation = validation;
|
|
49468
49482
|
}
|
|
49483
|
+
// protect filename if we de-identify
|
|
49484
|
+
const finalFilePath = finalSpec.dicomPS315EOptions === 'Off'
|
|
49485
|
+
? inputFilePath
|
|
49486
|
+
: inputFilePath.slice(0, inputFilePath.lastIndexOf('/') + 1) +
|
|
49487
|
+
`${String(inputFileIndex + 1).padStart(5, '0')}.dcm`;
|
|
49469
49488
|
// create a parser object to be used in the eval'ed mappingFunctions
|
|
49470
|
-
const parser = getParser(finalSpec.inputPathPattern,
|
|
49489
|
+
const parser = getParser(finalSpec.inputPathPattern, finalFilePath, naturalData, finalSpec.dicomPS315EOptions, mappingOptions.columnMappings, finalSpec.additionalData);
|
|
49471
49490
|
let modificationMap = finalSpec.modifications(parser);
|
|
49472
49491
|
// List all validation errors
|
|
49473
49492
|
mapResults.errors = finalSpec
|
|
@@ -49548,11 +49567,11 @@
|
|
|
49548
49567
|
return mappedMetaheader;
|
|
49549
49568
|
}
|
|
49550
49569
|
|
|
49551
|
-
function curateDict(inputFilePath, dicomData, mappingOptions) {
|
|
49570
|
+
function curateDict(inputFilePath, inputFileIndex, dicomData, mappingOptions) {
|
|
49552
49571
|
//
|
|
49553
49572
|
// Collect the mappings and apply them to the data
|
|
49554
49573
|
//
|
|
49555
|
-
const [naturalData, mapResults] = collectMappings(inputFilePath, dicomData, mappingOptions);
|
|
49574
|
+
const [naturalData, mapResults] = collectMappings(inputFilePath, inputFileIndex, dicomData, mappingOptions);
|
|
49556
49575
|
for (let tagPath in mapResults.mappings) {
|
|
49557
49576
|
const [, operation, , mappedValue] = mapResults.mappings[tagPath];
|
|
49558
49577
|
switch (operation) {
|
|
@@ -49576,8 +49595,8 @@
|
|
|
49576
49595
|
return { dicomData: mappedDicomData, mapResults: lodashExports.cloneDeep(mapResults) };
|
|
49577
49596
|
}
|
|
49578
49597
|
|
|
49579
|
-
function curateOne(
|
|
49580
|
-
return __awaiter(this,
|
|
49598
|
+
function curateOne(_a) {
|
|
49599
|
+
return __awaiter(this, arguments, void 0, function* ({ fileInfo, fileIndex = 0, outputDirectory, mappingOptions, }) {
|
|
49581
49600
|
//
|
|
49582
49601
|
// First, read the dicom instance data from the file handle or blob
|
|
49583
49602
|
//
|
|
@@ -49602,7 +49621,7 @@
|
|
|
49602
49621
|
};
|
|
49603
49622
|
return mapResults;
|
|
49604
49623
|
}
|
|
49605
|
-
const { dicomData: mappedDicomData, mapResults: clonedMapResults } = curateDict(`${fileInfo.path}/${fileInfo.name}`, dicomData, mappingOptions);
|
|
49624
|
+
const { dicomData: mappedDicomData, mapResults: clonedMapResults } = curateDict(`${fileInfo.path}/${fileInfo.name}`, fileIndex, dicomData, mappingOptions);
|
|
49606
49625
|
if (!mappingOptions.skipWrite) {
|
|
49607
49626
|
// Finally, write the results
|
|
49608
49627
|
const dirPath = clonedMapResults.outputFilePath
|
|
@@ -62883,7 +62902,8 @@
|
|
|
62883
62902
|
fileListWorker.addEventListener('message', (event) => {
|
|
62884
62903
|
switch (event.data.response) {
|
|
62885
62904
|
case 'file':
|
|
62886
|
-
|
|
62905
|
+
const { fileIndex, fileInfo } = event.data;
|
|
62906
|
+
filesToProcess.push({ fileIndex, fileInfo });
|
|
62887
62907
|
// Could do some throttling:
|
|
62888
62908
|
// if (filesToProcess.length > 10) {
|
|
62889
62909
|
// fileListWorker.postMessage({ request: 'stop' })
|
|
@@ -62895,6 +62915,7 @@
|
|
|
62895
62915
|
directoryScanFinished = true;
|
|
62896
62916
|
break;
|
|
62897
62917
|
default:
|
|
62918
|
+
// @ts-expect-error: response is string here, not never
|
|
62898
62919
|
console.error(`Unknown response from worker ${event.data.response}`);
|
|
62899
62920
|
}
|
|
62900
62921
|
dispatchMappingJobs();
|
|
@@ -62928,14 +62949,12 @@
|
|
|
62928
62949
|
mapResultsList.push(event.data.mapResults);
|
|
62929
62950
|
workersActive -= 1;
|
|
62930
62951
|
// Report progress
|
|
62931
|
-
|
|
62932
|
-
|
|
62933
|
-
|
|
62934
|
-
|
|
62935
|
-
|
|
62936
|
-
|
|
62937
|
-
});
|
|
62938
|
-
}
|
|
62952
|
+
progressCallback({
|
|
62953
|
+
response: 'progress',
|
|
62954
|
+
mapResults: event.data.mapResults,
|
|
62955
|
+
processedFiles: mapResultsList.length,
|
|
62956
|
+
totalFiles: filesToProcess.length + mapResultsList.length + workersActive,
|
|
62957
|
+
});
|
|
62939
62958
|
dispatchMappingJobs();
|
|
62940
62959
|
if (mapResultsList.length % 100 === 0) {
|
|
62941
62960
|
console.log(`Finished mapping ${mapResultsList.length} files`);
|
|
@@ -62951,7 +62970,7 @@
|
|
|
62951
62970
|
}
|
|
62952
62971
|
function dispatchMappingJobs() {
|
|
62953
62972
|
while (filesToProcess.length > 0 && availableMappingWorkers.length > 0) {
|
|
62954
|
-
const fileInfo = filesToProcess.pop();
|
|
62973
|
+
const { fileInfo, fileIndex } = filesToProcess.pop();
|
|
62955
62974
|
const mappingWorker = availableMappingWorkers.pop();
|
|
62956
62975
|
const _a =
|
|
62957
62976
|
// Not partial anymore.
|
|
@@ -62959,6 +62978,7 @@
|
|
|
62959
62978
|
mappingWorker.postMessage({
|
|
62960
62979
|
request: 'apply',
|
|
62961
62980
|
fileInfo,
|
|
62981
|
+
fileIndex,
|
|
62962
62982
|
outputDirectory,
|
|
62963
62983
|
serializedMappingOptions: serializeMappingOptions(mappingOptions),
|
|
62964
62984
|
});
|
|
@@ -62974,6 +62994,12 @@
|
|
|
62974
62994
|
clearCaches();
|
|
62975
62995
|
console.log(`Finished mapping ${mapResultsList.length} files`);
|
|
62976
62996
|
console.log('job is finished');
|
|
62997
|
+
progressCallback({
|
|
62998
|
+
response: 'done',
|
|
62999
|
+
mapResultsList: mapResultsList,
|
|
63000
|
+
processedFiles: mapResultsList.length,
|
|
63001
|
+
totalFiles: mapResultsList.length,
|
|
63002
|
+
});
|
|
62977
63003
|
}
|
|
62978
63004
|
}
|
|
62979
63005
|
function collectMappingOptions(organizeOptions) {
|
|
@@ -63014,7 +63040,7 @@
|
|
|
63014
63040
|
});
|
|
63015
63041
|
}
|
|
63016
63042
|
function queueFilesForMapping(organizeOptions) {
|
|
63017
|
-
organizeOptions.inputFiles.forEach((inputFile) => {
|
|
63043
|
+
organizeOptions.inputFiles.forEach((inputFile, fileIndex) => {
|
|
63018
63044
|
const fileInfo = {
|
|
63019
63045
|
path: '',
|
|
63020
63046
|
name: inputFile.name,
|
|
@@ -63022,38 +63048,51 @@
|
|
|
63022
63048
|
kind: 'blob',
|
|
63023
63049
|
blob: inputFile,
|
|
63024
63050
|
};
|
|
63025
|
-
filesToProcess.push(fileInfo);
|
|
63051
|
+
filesToProcess.push({ fileInfo, fileIndex });
|
|
63026
63052
|
dispatchMappingJobs();
|
|
63027
63053
|
});
|
|
63028
63054
|
}
|
|
63029
63055
|
let progressCallback;
|
|
63030
63056
|
function curateMany(organizeOptions, onProgress) {
|
|
63031
63057
|
return __awaiter(this, void 0, void 0, function* () {
|
|
63032
|
-
|
|
63033
|
-
|
|
63034
|
-
|
|
63035
|
-
|
|
63036
|
-
|
|
63037
|
-
|
|
63038
|
-
|
|
63039
|
-
|
|
63040
|
-
|
|
63041
|
-
|
|
63042
|
-
|
|
63043
|
-
|
|
63044
|
-
|
|
63045
|
-
|
|
63046
|
-
request
|
|
63047
|
-
|
|
63048
|
-
|
|
63049
|
-
|
|
63050
|
-
|
|
63051
|
-
|
|
63052
|
-
|
|
63053
|
-
|
|
63054
|
-
|
|
63055
|
-
|
|
63056
|
-
|
|
63058
|
+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
63059
|
+
// Resolve promise if progressCallback gets called with 'done'
|
|
63060
|
+
progressCallback = (msg) => {
|
|
63061
|
+
onProgress === null || onProgress === void 0 ? void 0 : onProgress(msg);
|
|
63062
|
+
if (msg.response === 'done') {
|
|
63063
|
+
resolve(msg);
|
|
63064
|
+
}
|
|
63065
|
+
};
|
|
63066
|
+
try {
|
|
63067
|
+
// create the mapping workers
|
|
63068
|
+
initializeMappingWorkers();
|
|
63069
|
+
// Set global mappingWorkerOptions
|
|
63070
|
+
mappingWorkerOptions = (yield collectMappingOptions(organizeOptions));
|
|
63071
|
+
//
|
|
63072
|
+
// If the request provides a directory, then use the worker
|
|
63073
|
+
// to recursively convert to fileSystemHandles.
|
|
63074
|
+
// If the request provides a list of File objects,
|
|
63075
|
+
// send them to the mapping workers directly.
|
|
63076
|
+
//
|
|
63077
|
+
if (organizeOptions.inputType === 'directory') {
|
|
63078
|
+
const fileListWorker = initializeFileListWorker();
|
|
63079
|
+
fileListWorker.postMessage({
|
|
63080
|
+
request: 'scan',
|
|
63081
|
+
directoryHandle: organizeOptions.inputDirectory,
|
|
63082
|
+
});
|
|
63083
|
+
}
|
|
63084
|
+
else if (organizeOptions.inputType === 'files') {
|
|
63085
|
+
queueFilesForMapping(organizeOptions);
|
|
63086
|
+
}
|
|
63087
|
+
else {
|
|
63088
|
+
console.error('`inputType` should be "directory" or "files"');
|
|
63089
|
+
}
|
|
63090
|
+
dispatchMappingJobs();
|
|
63091
|
+
}
|
|
63092
|
+
catch (error) {
|
|
63093
|
+
reject(error);
|
|
63094
|
+
}
|
|
63095
|
+
}));
|
|
63057
63096
|
});
|
|
63058
63097
|
}
|
|
63059
63098
|
|