2021-08-20 12:14:19 -04:00
|
|
|
import { api } from 'dicomweb-client';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* An implementation of the static wado client, that fetches data from
|
|
|
|
|
* a static response rather than actually doing real queries. This allows
|
|
|
|
|
* fast encoding of test data, but because it is static, anything actually
|
|
|
|
|
* performing searches doesn't work. This version fixes the query issue
|
|
|
|
|
* by manually implementing a query option.
|
|
|
|
|
*/
|
|
|
|
|
export default class StaticWadoClient extends api.DICOMwebClient {
|
2022-10-25 10:20:32 -04:00
|
|
|
static studyFilterKeys = {
|
|
|
|
|
StudyInstanceUID: '0020000D',
|
|
|
|
|
PatientName: '00100010',
|
|
|
|
|
'00100020': 'mrn',
|
|
|
|
|
StudyDescription: '00081030',
|
|
|
|
|
StudyDate: '00080020',
|
|
|
|
|
ModalitiesInStudy: '00080061',
|
|
|
|
|
AccessionNumber: '00080050',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static seriesFilterKeys = {
|
|
|
|
|
SeriesInstanceUID: '0020000E',
|
|
|
|
|
SeriesNumber: '00200011',
|
2021-08-20 12:14:19 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
constructor(qidoConfig) {
|
|
|
|
|
super(qidoConfig);
|
|
|
|
|
this.staticWado = qidoConfig.staticWado;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-14 15:28:59 -05:00
|
|
|
/**
|
|
|
|
|
* Replace the search for studies remote query with a local version which
|
|
|
|
|
* retrieves a complete query list and then sub-selects from it locally.
|
|
|
|
|
* @param {*} options
|
|
|
|
|
* @returns
|
|
|
|
|
*/
|
2021-08-20 12:14:19 -04:00
|
|
|
async searchForStudies(options) {
|
|
|
|
|
if (!this.staticWado) return super.searchForStudies(options);
|
|
|
|
|
|
|
|
|
|
let searchResult = await super.searchForStudies(options);
|
|
|
|
|
const { queryParams } = options;
|
|
|
|
|
if (!queryParams) return searchResult;
|
|
|
|
|
const filtered = searchResult.filter(study => {
|
2022-10-25 10:20:32 -04:00
|
|
|
for (const key of Object.keys(StaticWadoClient.studyFilterKeys)) {
|
2021-08-20 12:14:19 -04:00
|
|
|
if (!this.filterItem(key, queryParams, study)) return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
return filtered;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-25 10:20:32 -04:00
|
|
|
async searchForSeries(options) {
|
|
|
|
|
if (!this.staticWado) return super.searchForSeries(options);
|
|
|
|
|
|
|
|
|
|
let searchResult = await super.searchForSeries(options);
|
|
|
|
|
const { queryParams } = options;
|
|
|
|
|
if (!queryParams) return searchResult;
|
|
|
|
|
const filtered = searchResult.filter(study => {
|
|
|
|
|
for (const key of Object.keys(StaticWadoClient.studyFilterKeys)) {
|
|
|
|
|
if (!this.filterItem(key, queryParams, study)) return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return filtered;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-14 15:28:59 -05:00
|
|
|
/**
|
|
|
|
|
* Compares values, matching any instance of desired to any instance of
|
|
|
|
|
* actual by recursively go through the paired set of values. That is,
|
|
|
|
|
* this is O(m*n) where m is how many items in desired and n is the length of actual
|
|
|
|
|
* Then, at the individual item node, compares the Alphabetic name if present,
|
|
|
|
|
* and does a sub-string matching on string values, and otherwise does an
|
|
|
|
|
* exact match comparison.
|
|
|
|
|
*
|
|
|
|
|
* @param {*} desired
|
|
|
|
|
* @param {*} actual
|
|
|
|
|
* @returns true if the values match
|
|
|
|
|
*/
|
|
|
|
|
compareValues(desired, actual) {
|
|
|
|
|
if (Array.isArray(desired)) {
|
|
|
|
|
return desired.find(item => this.compareValues(item, actual));
|
|
|
|
|
}
|
|
|
|
|
if (Array.isArray(actual)) {
|
|
|
|
|
return actual.find(actualItem => this.compareValues(desired, actualItem));
|
|
|
|
|
}
|
|
|
|
|
if (actual?.Alphabetic) {
|
|
|
|
|
actual = actual.Alphabetic;
|
|
|
|
|
}
|
2022-10-25 10:20:32 -04:00
|
|
|
if (typeof actual == 'string') {
|
2022-01-31 14:29:20 -05:00
|
|
|
if (actual.length === 0) return true;
|
|
|
|
|
if (desired.length === 0 || desired === '*') return true;
|
|
|
|
|
if (desired[0] === '*' && desired[desired.length - 1] === '*') {
|
2022-04-04 09:48:14 -04:00
|
|
|
// console.log(`Comparing ${actual} to ${desired.substring(1, desired.length - 1)}`)
|
2022-01-31 14:29:20 -05:00
|
|
|
return actual.indexOf(desired.substring(1, desired.length - 1)) != -1;
|
|
|
|
|
} else if (desired[desired.length - 1] === '*') {
|
|
|
|
|
return actual.indexOf(desired.substring(0, desired.length - 1)) != -1;
|
|
|
|
|
} else if (desired[0] === '*') {
|
2022-10-25 10:20:32 -04:00
|
|
|
return (
|
|
|
|
|
actual.indexOf(desired.substring(1)) ===
|
|
|
|
|
actual.length - desired.length + 1
|
|
|
|
|
);
|
2022-01-31 14:29:20 -05:00
|
|
|
}
|
2022-01-14 15:28:59 -05:00
|
|
|
}
|
|
|
|
|
return desired === actual;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Compares a pair of dates to see if the value is within the range */
|
|
|
|
|
compareDateRange(range, value) {
|
|
|
|
|
if (!value) return true;
|
|
|
|
|
const dash = range.indexOf('-');
|
|
|
|
|
if (dash === -1) return this.compareValues(range, value);
|
|
|
|
|
const start = range.substring(0, dash);
|
|
|
|
|
const end = range.substring(dash + 1);
|
2022-10-25 10:20:32 -04:00
|
|
|
return (!start || value >= start) && (!end || value <= end);
|
2022-01-14 15:28:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Filters the return list by the query parameters.
|
|
|
|
|
*
|
|
|
|
|
* @param {*} key
|
|
|
|
|
* @param {*} queryParams
|
|
|
|
|
* @param {*} study
|
|
|
|
|
* @returns
|
|
|
|
|
*/
|
2021-08-20 12:14:19 -04:00
|
|
|
filterItem(key, queryParams, study) {
|
2022-10-25 10:20:32 -04:00
|
|
|
const altKey = StaticWadoClient.studyFilterKeys[key] || key;
|
2021-08-20 12:14:19 -04:00
|
|
|
if (!queryParams) return true;
|
|
|
|
|
const testValue = queryParams[key] || queryParams[altKey];
|
|
|
|
|
if (!testValue) return true;
|
|
|
|
|
const valueElem = study[key] || study[altKey];
|
|
|
|
|
if (!valueElem) return false;
|
2022-10-25 10:20:32 -04:00
|
|
|
if (valueElem.vr == 'DA') {
|
|
|
|
|
return this.compareDateRange(testValue, valueElem.Value[0]);
|
|
|
|
|
}
|
2021-08-20 12:14:19 -04:00
|
|
|
const value = valueElem.Value;
|
2022-01-14 15:28:59 -05:00
|
|
|
return this.compareValues(testValue, value) && true;
|
2021-08-20 12:14:19 -04:00
|
|
|
}
|
|
|
|
|
}
|