Skip to content

Commit

Permalink
rewrite code async for browser crypto usage
Browse files Browse the repository at this point in the history
  • Loading branch information
lmdulz committed Feb 14, 2024
1 parent 8457f55 commit 03b8229
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 122 deletions.
45 changes: 24 additions & 21 deletions src/addressanonymizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class AddressAnonymizer {
addressTag: string = data.DicomMetaDictionary.nameMap["PatientAddress"].tag;
regionTag: string = data.DicomMetaDictionary.nameMap["RegionOfResidence"].tag;
countryTag: string = data.DicomMetaDictionary.nameMap["CountryOfResidence"].tag;
valueFactories: { [key: string]: (originalValue: string) => string };
valueFactories; //: { [key: string]: (originalValue: string) => Promise<string> } | undefined;

constructor(Randomizer: Randomizer) {
this.randomizer = Randomizer;
Expand All @@ -19,69 +19,72 @@ export class AddressAnonymizer {
this.addressTag = data.DicomMetaDictionary.unpunctuateTag(this.addressTag);
this.regionTag = data.DicomMetaDictionary.unpunctuateTag(this.regionTag);
this.countryTag = data.DicomMetaDictionary.unpunctuateTag(this.countryTag);

// this._setupValueFactories();
this.valueFactories = {
[this.addressTag]: this.getLegalAddress,
[this.regionTag]: this.getRegion,
[this.countryTag]: this.getCountry,
};
}

anonymize = (dataset: dataSet, dataTag: string): boolean => {
const valueFactory: (originalValue: string) => string = this.valueFactories[dataTag];
anonymize = async (dataset: dataSet, dataTag: string): Promise<boolean> => {
const valueFactory = await this.valueFactories[dataTag];

if (valueFactory == undefined) {
return false;
}

if (dataset[dataTag].Value.length > 1) {
dataset[dataTag].Value = dataset[dataTag].Value.map((originalValue: string) => {
return valueFactory(originalValue);
});
for (let i = 0; i < dataset[dataTag].Value.length; i++) {
dataset[dataTag].Value[i] = await valueFactory(dataset[dataTag].Value[i]);
}

return true;
} else {
const originalValue: string = dataset[dataTag].Value[0];
dataset[dataTag].Value[0] = valueFactory(originalValue);

dataset[dataTag].Value[0] = await valueFactory(originalValue);
return true;
}
};

getLegalAddress = (originalValue: string): string => {
const street: string = this.getStreetAddress(originalValue);
const streetNumber: string = this.getStreetNumber(originalValue);
const city: string = this.getRegion(originalValue);
getLegalAddress = async (originalValue: string): Promise<string> => {
const street: string = await this.getStreetAddress(originalValue);
const streetNumber: string = await this.getStreetNumber(originalValue);
const city: string = await this.getRegion(originalValue);

return `${streetNumber} ${street}, ${city}`;
};

getStreetNumber = (originalValue: string): string => {
const [streetNumberIndex]: number[] = this.randomizer.getIntsFromRanges(originalValue, 1000);
getStreetNumber = async (originalValue: string): Promise<string> => {
const [streetNumberIndex]: number[] = await this.randomizer.getIntsFromRanges(
originalValue,
1000
);
const streetNumber: number = streetNumberIndex + 1;

return `${streetNumber}`;
};

getStreetAddress = (originalValue: string): string => {
const [streetIndex]: number[] = this.randomizer.getIntsFromRanges(
getStreetAddress = async (originalValue: string): Promise<string> => {
const [streetIndex]: number[] = await this.randomizer.getIntsFromRanges(
originalValue,
this.lists.streets.length
);

return `${this.lists.streets[streetIndex]}`;
};

getRegion = (originalValue: string): string => {
const [cityIndex]: number[] = this.randomizer.getIntsFromRanges(
getRegion = async (originalValue: string): Promise<string> => {
const [cityIndex]: number[] = await this.randomizer.getIntsFromRanges(
originalValue,
this.lists.cities.length
);

return `${this.lists.cities[cityIndex]}`;
};

getCountry = (originalValue: string): string => {
const [countryIndex]: number[] = this.randomizer.getIntsFromRanges(
getCountry = async (originalValue: string): Promise<string> => {
const [countryIndex]: number[] = await this.randomizer.getIntsFromRanges(
originalValue,
this.lists.countries.length
);
Expand Down
61 changes: 37 additions & 24 deletions src/anonymizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { UIAnonymizer } from "./uianonymizer";
import { UnwantedElementStripper } from "./unwantedelements";
import { ValueKeeper } from "./valuekeeper";

type ElementHandler = (dataset: dataSet, tag: string) => boolean;
type ElementHandler = (dataset: dataSet, tag: string) => boolean | Promise<boolean>;

export class Anonymizer {
/**
Expand All @@ -23,7 +23,8 @@ export class Anonymizer {
*/

private patientID?: string;
private dateOffsetHours: number;
private anonymizePrivateTags?: boolean = true;
private dateOffsetHours = 0;
randomizer: Randomizer;
addressAnonymizer: AddressAnonymizer;
elementHandlers: ElementHandler[];
Expand All @@ -36,22 +37,16 @@ export class Anonymizer {
idSuffix?: string,
seed?: string
) {
const minimumOffsetHours: number = 62 * 24;
const maximumOffsetHours: number = 730 * 24;
if (patientID) {
this.patientID = patientID;
}
if (anonymizePrivateTags) {
this.anonymizePrivateTags = anonymizePrivateTags;
}

this.randomizer = new Randomizer(seed);
this.dateOffsetHours = Number(0);
this.randomizer.toInt("dateOffset", (res) => {
this.dateOffsetHours = Number(
-(
(res % (BigInt(maximumOffsetHours) - BigInt(minimumOffsetHours))) +
BigInt(minimumOffsetHours)
)
);
});
//this.data = data;
this.setOffset();

this.addressAnonymizer = new AddressAnonymizer(this.randomizer);
this.elementHandlers = [
new UnwantedElementStripper([
Expand Down Expand Up @@ -93,29 +88,44 @@ export class Anonymizer {
new InstitutionAnonymizer(this.addressAnonymizer).anonymize,
new FixedValueAnonymizer("00321033", "").anonymize, // RequestingService
new FixedValueAnonymizer("00380300", "").anonymize, // CurrentPatientLocation
new DateTimeAnonymizer(this.dateOffsetHours).anonymize,
];
if (protectedTags) {
this.elementHandlers.unshift(new ValueKeeper(protectedTags).keep);
}
if (this.patientID) {
this.elementHandlers.push(new FixedValueAnonymizer("00100020", this.patientID).anonymize);
this.elementHandlers.unshift(new FixedValueAnonymizer("00100020", this.patientID).anonymize);
}
if (anonymizePrivateTags) {
if (this.anonymizePrivateTags) {
this.elementHandlers.push(new PrivatTagAnonymizer().anonymize);
}
}
anonymize(data: data.DicomDict) {
this.walk(data.meta, this.elementHandlers);
this.walk(data.dict, this.elementHandlers);
async setOffset() {
const res = await this.randomizer.toInt("dateOffset");
const minimumOffsetHours: number = 62 * 24;
const maximumOffsetHours: number = 730 * 24;
console.log(res);
this.dateOffsetHours = Number(
-(
(res % (BigInt(maximumOffsetHours) - BigInt(minimumOffsetHours))) +
BigInt(minimumOffsetHours)
)
);

this.elementHandlers.push(new DateTimeAnonymizer(this.dateOffsetHours).anonymize);
}

async anonymize(dcmDict: data.DicomDict) {
await this.walk(dcmDict.meta, this.elementHandlers);
await this.walk(dcmDict.dict, this.elementHandlers);
}

walk(dataset: dataSet, handler: ElementHandler[]) {
async walk(dataset: dataSet, handler: ElementHandler[]) {
const tagList = Object.keys(dataset);

for (const tag of tagList) {
const element = dataset[tag];

this.anonymizeElement(dataset, tag, handler);
await this.anonymizeElement(dataset, tag, handler);

// If the element is a sequence, recursively walk through its items
if (tag in dataset && element.vr == "SQ") {
Expand All @@ -129,10 +139,13 @@ export class Anonymizer {
}
}

anonymizeElement(dataset: dataSet, tag: string, handler: ElementHandler[]) {
async anonymizeElement(dataset: dataSet, tag: string, handler: ElementHandler[]) {
// Perform operations on the element

for (const callback of handler) {
if (callback(dataset, tag)) {
const result = await callback(dataset, tag);

if (result) {
return;
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/datetimeanonymizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class DateTimeAnonymizer {
this.offset = dateOffsetHours * 60 * 60 * 1000;
}

anonymize = (dataset: dataSet, dataTag: string): boolean => {
anonymize = async (dataset: dataSet, dataTag: string): Promise<boolean> => {
if (dataset[dataTag].vr != "DA" && dataset[dataTag].vr != "DT") {
return false;
}
Expand All @@ -21,9 +21,9 @@ export class DateTimeAnonymizer {
}

if (dataset[dataTag].vr == "DA") {
this.anonymizeDateAndTime(dataset, dataTag);
await this.anonymizeDateAndTime(dataset, dataTag);
} else {
this.anonymizeDatetime(dataset, dataTag);
await this.anonymizeDatetime(dataset, dataTag);
}

return true;
Expand Down
2 changes: 1 addition & 1 deletion src/fixedvalueanonymizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export class FixedValueAnonymizer {
this.value = value;
}

anonymize = (dataset: dataSet, dataTag: string): boolean => {
anonymize = async (dataset: dataSet, dataTag: string): Promise<boolean> => {
if (dataTag == this.tag) {
dataset[dataTag].Value[0] = this.value;

Expand Down
22 changes: 13 additions & 9 deletions src/idanonymizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ export class IDAnonymizer {
);
}

anonymize = (dataset: dataSet, dataTag: string): boolean => {
anonymize = async (dataset: dataSet, dataTag: string): Promise<boolean> => {
if (this.keywords.includes(dataTag)) {
this.replaceID(dataset, dataTag);
await this.replaceID(dataset, dataTag);

return true;
} else if (dataTag == this.issuerTag && dataset[dataTag].Value[0] != "") {
dataset[dataTag].Value[0] = "DICOM_ANONYMIZER";
Expand All @@ -37,19 +38,22 @@ export class IDAnonymizer {
}
};

replaceID = (dataset: dataSet, dataTag: string): void => {
replaceID = async (dataset: dataSet, dataTag: string) => {
if (dataset[dataTag].Value.length > 1) {
dataset[dataTag].Value = dataset[dataTag].Value.map((originalValue: string) => {
return this.newID(originalValue);
});
for (let i = 0; i < dataset[dataTag].Value.length; i++) {
dataset[dataTag].Value[i] = await this.newID(dataset[dataTag].Value[i]);
}
} else {
const originalValue = dataset[dataTag].Value[0];
dataset[dataTag].Value[0] = this.newID(originalValue);
dataset[dataTag].Value[0] = await this.newID(originalValue);
}
};

newID = (originalValue: string): string => {
const indexes = this.randomizer.getIntsFromRanges(originalValue, ...this.indicesForRandomizer);
newID = async (originalValue: string) => {
const indexes = await this.randomizer.getIntsFromRanges(
originalValue,
...this.indicesForRandomizer
);
const idRoot: string = indexes.map((i) => this.alphabet[i]).join("");

return this.idPrefix + idRoot + this.idSuffix;
Expand Down
42 changes: 25 additions & 17 deletions src/institutionanonymizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class InstitutionAnonymizer {
institutionAddress: string = data.DicomMetaDictionary.nameMap["InstitutionAddress"].tag; //0008,0081
institutionalDepartmentName: string =
data.DicomMetaDictionary.nameMap["InstitutionalDepartmentName"].tag; //0008,1040
valueFactories: { [key: string]: (originalValue: string) => string };
valueFactories; //: { [key: string]: (originalValue: string) => string };

constructor(AddressAnonymizer: AddressAnonymizer) {
this.addressAnonymizer = AddressAnonymizer;
Expand All @@ -26,41 +26,49 @@ export class InstitutionAnonymizer {
};
}

anonymize = (dataset: dataSet, dataTag: string): boolean => {
const valueFactory: (originalValue: string) => string = this.valueFactories[dataTag];
anonymize = async (dataset: dataSet, dataTag: string): Promise<boolean> => {
const valueFactory: (originalValue: string) => Promise<string> = await this.valueFactories[
dataTag
];
if (valueFactory == undefined) {
return false;
}

if (dataset[dataTag].Value.length > 1) {
dataset[dataTag].Value = dataset[dataTag].Value.map((originalValue: string) => {
return valueFactory(originalValue);
});
for (let i = 0; i < dataset[dataTag].Value.length; i++) {
dataset[dataTag].Value[i] = await valueFactory(dataset[dataTag].Value[i]);
}
// dataset[dataTag].Value = await dataset[dataTag].Value.map((originalValue: string) => {
// return valueFactory(originalValue);
// });

return true;
} else {
const originalValue: string = dataset[dataTag].Value[0];
dataset[dataTag].Value[0] = valueFactory(originalValue);
dataset[dataTag].Value[0] = await valueFactory(originalValue);

return true;
}
};

anonymizeInstitutionName = (originalValue: string): string => {
const region: string = this.addressAnonymizer.getRegion(originalValue);
const streetAddress: string = this.addressAnonymizer.getStreetAddress(originalValue);
anonymizeInstitutionName = async (originalValue: string): Promise<string> => {
const region = await this.addressAnonymizer.getRegion(originalValue);
const streetAddress = await this.addressAnonymizer.getStreetAddress(originalValue);
const returnString = `${region}'S ${streetAddress} CLINIC`;

return `${region}'S ${streetAddress} CLINIC`;
return returnString;
};

anonymizeInstitutionAddress = (originalValue: string): string => {
const fullAddress: string = this.addressAnonymizer.getLegalAddress(originalValue);
const country: string = this.addressAnonymizer.getCountry(originalValue);
anonymizeInstitutionAddress = async (originalValue: string): Promise<string> => {
const fullAddress = await this.addressAnonymizer.getLegalAddress(originalValue);
const country = await this.addressAnonymizer.getCountry(originalValue);

return `${fullAddress}, ${country}`;
const returnString = `${fullAddress}, ${country}`;

return returnString;
};

anonymizeDepartmentName = (): string => {
return "RADIOLOGY";
anonymizeDepartmentName = (): Promise<string> => {
return Promise.resolve("RADIOLOGY");
};
}
Loading

0 comments on commit 03b8229

Please sign in to comment.