/** * QIDO - Query based on ID for DICOM Objects * search for studies, series and instances by patient ID, and receive their * unique identifiers for further usage. * * Quick: https://www.dicomstandard.org/dicomweb/query-qido-rs/ * Standard: http://dicom.nema.org/medical/dicom/current/output/html/part18.html#sect_10.6 * * Routes: * ========== * /studies? * /studies/{studyInstanceUid}/series? * /studies/{studyInstanceUid}/series/{seriesInstanceUid}/instances? * * Query Parameters: * ================ * | KEY | VALUE | * |------------------|--------------------| * | {attributeId} | {value} | * | includeField | {attribute} or all | * | fuzzymatching | true OR false | * | limit | {number} | * | offset | {number} | */ import { DICOMWeb } from '@ohif/core'; const { getString, getName, getModalities } = DICOMWeb; /** * Parses resulting data from a QIDO call into a set of Study MetaData * * @param {Array} qidoStudies - An array of study objects. Each object contains a keys for DICOM tags. * @param {object} qidoStudies[0].qidoStudy - An object where each key is the DICOM Tag group+element * @param {object} qidoStudies[0].qidoStudy[dicomTag] - Optional object that represents DICOM Tag * @param {string} qidoStudies[0].qidoStudy[dicomTag].vr - Value Representation * @param {string[]} qidoStudies[0].qidoStudy[dicomTag].Value - Optional string array representation of the DICOM Tag's value * @returns {Array} An array of Study MetaData objects */ function processResults(qidoStudies) { if (!qidoStudies || !qidoStudies.length) { return []; } const studies = []; qidoStudies.forEach(qidoStudy => studies.push({ studyInstanceUid: getString(qidoStudy['0020000D']), date: getString(qidoStudy['00080020']), // YYYYMMDD time: getString(qidoStudy['00080030']), // HHmmss.SSS (24-hour, minutes, seconds, fractional seconds) accession: getString(qidoStudy['00080050']) || '', // short string, probably a number? mrn: getString(qidoStudy['00100020']) || '', // medicalRecordNumber patientName: getName(qidoStudy['00100010']) || '', instances: Number(getString(qidoStudy['00201208'])) || 0, // number description: getString(qidoStudy['00081030']) || '', modalities: getString( getModalities(qidoStudy['00080060'], qidoStudy['00080061']) ) || '', }) ); return studies; } /** * Parses resulting data from a QIDO call into a set of Study MetaData * * @param {Array} qidoSeries - An array of study objects. Each object contains a keys for DICOM tags. * @param {object} qidoSeries[0].qidoSeries - An object where each key is the DICOM Tag group+element * @param {object} qidoSeries[0].qidoSeries[dicomTag] - Optional object that represents DICOM Tag * @param {string} qidoSeries[0].qidoSeries[dicomTag].vr - Value Representation * @param {string[]} qidoSeries[0].qidoSeries[dicomTag].Value - Optional string array representation of the DICOM Tag's value * @returns {Array} An array of Study MetaData objects */ export function processSeriesResults(qidoSeries) { const series = []; if (qidoSeries && qidoSeries.length) { qidoSeries.forEach(qidoSeries => series.push({ studyInstanceUid: getString(qidoSeries['0020000D']), seriesInstanceUid: getString(qidoSeries['0020000E']), modality: getString(qidoSeries['00080060']), seriesNumber: getString(qidoSeries['00200011']), numSeriesInstances: Number(getString(qidoSeries['00201209'])), description: getString(qidoSeries['0008103E']), }) ); } return series; } /** * * @param {object} dicomWebClient - Client similar to what's provided by `dicomweb-client` library * @param {function} dicomWebClient.searchForStudies - * @param {string} [studyInstanceUid] * @param {string} [seriesInstanceUid] * @param {string} [queryParamaters] * @returns {Promise} - Promise that resolves results */ function search( dicomWebClient, studyInstanceUid, seriesInstanceUid, queryParameters ) { // Studies return dicomWebClient.searchForStudies({ studyInstanceUid: undefined, queryParams: queryParameters, }); } /** * * @param {string} studyInstanceUID - ID of study to return a list of series for * @returns {Promise} - Resolves SeriesMetadata[] in study */ export function seriesInStudy(dicomWebClient, studyInstanceUID) { // Series Description // Already included? const commaSeparatedFields = ['0008103E'].join(','); const queryParams = { // includefield: commaSeparatedFields, }; return dicomWebClient.searchForSeries({ studyInstanceUID, queryParams }); } export default function searchStudies(server, filter) { const queryParams = getQIDOQueryParams( filter, server.qidoSupportsIncludeField ); const options = { queryParams, }; return dicomWeb.searchForStudies(options).then(resultDataToStudies); } /** * Produces a QIDO URL given server details and a set of specified search filter * items * * @param filter * @param serverSupportsQIDOIncludeField * @returns {string} The URL with encoded filter query data */ function mapParams(params) { if (!params) { return; } const commaSeparatedFields = [ '00081030', // Study Description '00080060', // Modality // Add more fields here if you want them in the result ].join(','); const parameters = { // Named PatientName: params.patientName, PatientID: params.patientId, AccessionNumber: params.accessionNumber, StudyDescription: params.studyDescription, ModalitiesInStudy: params.modalitiesInStudy, // Other limit: params.limit || 101, offset: params.offset || 0, fuzzymatching: params.fuzzymatching === undefined ? false : true, includefield: commaSeparatedFields, // serverSupportsQIDOIncludeField ? commaSeparatedFields : 'all', }; // build the StudyDate range parameter if (params.startDate && params.endDate) { parameters.StudyDate = `${params.startDate}-${params.endDate}`; } else if (params.startDate) { const today = new Date(); const DD = String(today.getDate()).padStart(2, '0'); const MM = String(today.getMonth() + 1).padStart(2, '0'); //January is 0! const YYYY = today.getFullYear(); const todayStr = `${YYYY}${MM}${DD}`; parameters.StudyDate = `${params.startDate}-${todayStr}`; } else if (params.endDate) { const oldDateStr = `19700102`; parameters.StudyDate = `${oldDateStr}-${params.endDate}`; } // Build the StudyInstanceUID parameter if (params.studyInstanceUid) { let studyUids = params.studyInstanceUid; studyUids = Array.isArray(studyUids) ? studyUids.join() : studyUids; studyUids = studyUids.replace(/[^0-9.]+/g, '\\'); parameters.StudyInstanceUID = studyUids; } // Clean query params of undefined values. const final = {}; Object.keys(parameters).forEach(key => { if (parameters[key] !== undefined && parameters[key] !== '') { final[key] = parameters[key]; } }); return final; } export { mapParams, search, processResults };