Skip to content

Commit

Permalink
feat: support includefield
Browse files Browse the repository at this point in the history
- See: https://dicom.nema.org/medical/dicom/current/output/html/part18.html#sect_8.3.4.3
- Add `IncludeFieldsFactory` to handle every level's fields
  • Loading branch information
Chinlinlee committed Apr 16, 2023
1 parent 7d296da commit f23606d
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 53 deletions.
12 changes: 12 additions & 0 deletions api/dicom-web/controller/QIDO-RS/service/QIDO-RS.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ class QidoRsService {
this.skip_ = parseInt(this.request.query.offset) || 0;
delete this.request.query["offset"];

/**
* @private
*/
this.includeFields_ = this.request.query["includefield"] || [];

if (this.includeFields_.includes("all")) {
this.includeFields_ = ["all"];
}

delete this.request.query["includefield"];

this.initQuery_();
}

Expand All @@ -65,6 +76,7 @@ class QidoRsService {
query: this.query,
skip: this.skip_,
limit: this.limit_,
includeFields: this.includeFields_,
retrieveBaseUrl: `${dicomWebService.getBasicURL()}/studies`,
requestParams: this.request.params
};
Expand Down
69 changes: 62 additions & 7 deletions api/dicom-web/qido-rs.route.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,52 @@
const express = require("express");
const Joi = require("joi");
const { validateParams, intArrayJoi } = require("../validator");
const { validateParams, intArrayJoi, stringArrayJoi } = require("../validator");
const router = express();
const {
dictionary
} = require("../../models/DICOM/dicom-tags-dic");

const KEYWORD_KEYS = Object.keys(dictionary.keyword);
const HEX_KEYS = Object.keys(dictionary.tag);

//#region QIDO-RS
const queryValidation = {
limit: Joi.number().integer().min(1).max(100),
offset: Joi.number().integer().min(0),
includefield: stringArrayJoi.stringArray().items(
Joi.string().custom(
(attribute, helper) => {
if (!isValidAttribute(attribute)) {
return helper.message(`Invalid DICOM attribute: ${attribute}, please enter valid keyword or tag`);
}
return convertKeywordToHex(attribute);
}
)
).single()
};

/**
*
* @param {string} attribute
*/
function isValidAttribute(attribute) {
if (KEYWORD_KEYS.indexOf(attribute) >= 0 ||
HEX_KEYS.indexOf(attribute) >= 0 ||
attribute === "all") {

return true;
}

return false;
}

function convertKeywordToHex(attribute) {
if (KEYWORD_KEYS.indexOf(attribute) >= 0) {
return dictionary.keyword[attribute];
}
return attribute;
}


/**
* @openapi
Expand Down Expand Up @@ -32,7 +75,9 @@ const router = express();
* allOf:
* - $ref: "#/components/schemas/StudyRequiredMatchingAttributes"
*/
router.get("/studies", require("./controller/QIDO-RS/queryAllStudies"));
router.get("/studies", validateParams(queryValidation, "query", {
allowUnknown: true
}), require("./controller/QIDO-RS/queryAllStudies"));

/**
* @openapi
Expand Down Expand Up @@ -66,7 +111,9 @@ router.get("/studies", require("./controller/QIDO-RS/queryAllStudies"));
* - $ref: "#/components/schemas/SeriesRequiredMatchingAttributes"
*/
router.get(
"/studies/:studyUID/series",
"/studies/:studyUID/series", validateParams(queryValidation, "query", {
allowUnknown: true
}),
require("./controller/QIDO-RS/queryStudies-Series")
);

Expand Down Expand Up @@ -105,7 +152,9 @@ router.get(
* - $ref: "#/components/schemas/InstanceRequiredMatchingAttributes"
*/
router.get(
"/studies/:studyUID/instances",
"/studies/:studyUID/instances", validateParams(queryValidation, "query", {
allowUnknown: true
}),
require("./controller/QIDO-RS/queryStudies-Instances")
);

Expand Down Expand Up @@ -145,7 +194,9 @@ router.get(
* - $ref: "#/components/schemas/InstanceRequiredMatchingAttributes"
*/
router.get(
"/studies/:studyUID/series/:seriesUID/instances",
"/studies/:studyUID/series/:seriesUID/instances", validateParams(queryValidation, "query", {
allowUnknown: true
}),
require("./controller/QIDO-RS/queryStudies-Series-Instance")
);

Expand Down Expand Up @@ -181,7 +232,9 @@ router.get(
*
*/
router.get(
"/series",
"/series", validateParams(queryValidation, "query", {
allowUnknown: true
}),
require("./controller/QIDO-RS/queryAllSeries")
);

Expand Down Expand Up @@ -219,7 +272,9 @@ router.get(
* - $ref: "#/components/schemas/InstanceRequiredMatchingAttributes"
*/
router.get(
"/instances",
"/instances", validateParams(queryValidation, "query", {
allowUnknown: true
}),
require("./controller/QIDO-RS/queryAllInstances")
);

Expand Down
25 changes: 24 additions & 1 deletion api/validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,36 @@ const intArrayJoi = Joi.extend((joi) => {
type: "intArray",
base: joi.array(),
coerce: (value, helper) => {
let item = (value.split ? value.split(',').map(v=> parseInt(v)) : parseInt(value));
let item = (value.split ? value.split(',').map(v => parseInt(v)) : parseInt(value));
return {
value: item
};
}
};
});

const stringArrayJoi = Joi.extend((joi) => {
return {
type: "stringArray",
base: joi.array(),
coerce: (value, helper) => {
if (lodash.isArray(value)) {
value = value.join(",");
}

if (typeof value !== 'string') {
return {
value: value
};
}

return {
value: value.replace(/^,+|,+$/mg, '').split(',')
};
}
};
});

/**
* @param {Object} paramSchema the valid scheama
* @param {string} item body , query , param
Expand Down Expand Up @@ -81,3 +103,4 @@ module.exports = {
intArrayJoi: intArrayJoi
};
module.exports.validateByJoi = validateByJoi;
module.exports.stringArrayJoi = stringArrayJoi;
21 changes: 4 additions & 17 deletions models/mongodb/models/dicom.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@ const {
dicomJsonAttributeDASchema,
getVRSchema
} = require("../schema/dicomJsonAttribute");
const { getStoreDicomFullPath } = require("../service");
const { getStoreDicomFullPath, IncludeFieldsFactory } = require("../service");
const { logger } = require("../../../utils/logs/log");
const { getStudyLevelFields } = require("./dicomStudy");
const { getSeriesLevelFields } = require("./dicomSeries");
const { tagsOfRequiredMatching } = require("../../DICOM/dicom-tags-mapping");

let dicomModelSchema = new mongoose.Schema(
{
Expand Down Expand Up @@ -318,17 +315,15 @@ async function updateStudyNumberOfStudyRelatedInstance(doc) {
* @returns
*/
dicomModelSchema.statics.getDicomJson = async function (queryOptions) {
let studyFields = getStudyLevelFields();
let seriesFields = getSeriesLevelFields();
let instanceFields = getInstanceLevelFields();

let includeFieldsFactory = new IncludeFieldsFactory(queryOptions.includeFields);
let instanceFields = includeFieldsFactory.getInstanceLevelFields();

try {

let docs = await mongoose
.model("dicom")
.find(queryOptions.query, {
...studyFields,
...seriesFields,
...instanceFields
})
.setOptions({
Expand Down Expand Up @@ -359,14 +354,6 @@ dicomModelSchema.statics.getDicomJson = async function (queryOptions) {

};

function getInstanceLevelFields() {
let fields = {};
for (let tag in tagsOfRequiredMatching.Instance) {
fields[tag] = 1;
}
return fields;
}

/**
*
* @param {object} iParam
Expand Down
19 changes: 3 additions & 16 deletions models/mongodb/models/dicomSeries.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ const mongoose = require("mongoose");
const _ = require("lodash");
const { tagsNeedStore } = require("../../DICOM/dicom-tags-mapping");
const { getVRSchema } = require("../schema/dicomJsonAttribute");
const { getStoreDicomFullPathGroup } = require("../service");
const {
tagsOfRequiredMatching
} = require("../../DICOM/dicom-tags-mapping");
const { getStudyLevelFields } = require("./dicomStudy");
const { getStoreDicomFullPathGroup, IncludeFieldsFactory } = require("../service");

let dicomSeriesSchema = new mongoose.Schema(
{
Expand Down Expand Up @@ -65,28 +61,20 @@ dicomSeriesSchema.index({
"0020000E": 1
});

function getSeriesLevelFields() {
let fields = {};
for (let tag in tagsOfRequiredMatching.Series) {
fields[tag] = 1;
}
return fields;
}

/**
*
* @param {import("../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions} queryOptions
* @returns
*/
dicomSeriesSchema.statics.getDicomJson = async function(queryOptions) {
let studyFields = getStudyLevelFields();
let seriesFields = getSeriesLevelFields();
let includeFieldsFactory = new IncludeFieldsFactory(queryOptions.includeFields);
let seriesFields = includeFieldsFactory.getSeriesLevelFields();

try {
let docs = await mongoose
.model("dicomSeries")
.find(queryOptions.query, {
...studyFields,
...seriesFields
})
.setOptions({
Expand Down Expand Up @@ -183,4 +171,3 @@ let dicomSeriesModel = mongoose.model(
);

module.exports = dicomSeriesModel;
module.exports.getSeriesLevelFields = getSeriesLevelFields;
17 changes: 6 additions & 11 deletions models/mongodb/models/dicomStudy.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ const mongoose = require("mongoose");
const _ = require("lodash");
const { tagsNeedStore } = require("../../DICOM/dicom-tags-mapping");
const { getVRSchema } = require("../schema/dicomJsonAttribute");
const { getStoreDicomFullPathGroup } = require("../service");
const {
getStoreDicomFullPathGroup,
IncludeFieldsFactory
} = require("../service");
const {
tagsOfRequiredMatching
} = require("../../DICOM/dicom-tags-mapping");
Expand Down Expand Up @@ -51,7 +54,8 @@ dicomStudySchema.index({
* @returns
*/
dicomStudySchema.statics.getDicomJson = async function (queryOptions) {
let studyFields = getStudyLevelFields();
let includeFieldsFactory = new IncludeFieldsFactory(queryOptions.includeFields);
let studyFields = includeFieldsFactory.getStudyLevelFields();

try {
let docs = await mongoose.model("dicomStudy").find(queryOptions.query, studyFields)
Expand Down Expand Up @@ -80,14 +84,6 @@ dicomStudySchema.statics.getDicomJson = async function (queryOptions) {
}
};

function getStudyLevelFields() {
let fields = {};
for (let tag in tagsOfRequiredMatching.Study) {
fields[tag] = 1;
}
return fields;
}

/**
*
* @param {Object} iParam
Expand Down Expand Up @@ -145,4 +141,3 @@ let dicomStudyModel = mongoose.model(
);

module.exports = dicomStudyModel;
module.exports.getStudyLevelFields = getStudyLevelFields;
Loading

0 comments on commit f23606d

Please sign in to comment.