Skip to content

Commit

Permalink
feat: instance query builder
Browse files Browse the repository at this point in the history
- related: 2ea6320
  • Loading branch information
Chinlinlee committed Aug 9, 2023
1 parent d2f1ccc commit bbfe1d8
Show file tree
Hide file tree
Showing 4 changed files with 339 additions and 3 deletions.
2 changes: 1 addition & 1 deletion api-sql/dicom-web/controller/QIDO-RS/queryAllInstances.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const {
QidoRsService
SqlQidoRsService: QidoRsService
} = require("./service/QIDO-RS.service");
const { ApiLogger } = require("@root/utils/logs/api-logger");
const { Controller } = require("@root/api/controller.class");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const {
} = require("@error/dicom-web-service");
const { StudyModel } = require("@models/sql/models/study.model");
const { SeriesModel } = require("@models/sql/models/series.model");
const { InstanceModel } = require("@models/sql/models/instance.mode");


class SqlQidoRsService extends QidoRsService {
Expand Down Expand Up @@ -94,6 +95,7 @@ class QidoDicomJsonFactory {
},
"instance": async () => {
// return await getInstanceDicomJson(queryOptions);
return await InstanceModel.getDicomJson(queryOptions);
}
};
}
Expand Down
311 changes: 310 additions & 1 deletion api-sql/dicom-web/controller/QIDO-RS/service/instanceQueryBuilder.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,37 @@
const { dictionary } = require("@models/DICOM/dicom-tags-dic");
const { BaseQueryBuilder } = require("./querybuilder");
const { DicomCodeModel } = require("@models/sql/models/dicomCode.model");
const { DicomContentSqModel } = require("@models/sql/models/dicomContentSQ.model");
const { VerifyIngObserverSqModel } = require("@models/sql/models/verifyingObserverSQ.model");
const { PersonNameModel } = require("@models/sql/models/personName.model");

class InstanceQueryBuilder extends BaseQueryBuilder {
constructor(queryOptions) {
super(queryOptions);

let conceptNameCodeQueryBuilder = new ConceptNameCodeSqQueryBuilder(this);
this["0040A043.00080100"] = ConceptNameCodeSqQueryBuilder.prototype.getCodeValue.bind(conceptNameCodeQueryBuilder);
this["0040A043.00080102"] = ConceptNameCodeSqQueryBuilder.prototype.getCodingSchemeDesignator.bind(conceptNameCodeQueryBuilder);
this["0040A043.00080103"] = ConceptNameCodeSqQueryBuilder.prototype.getCodingSchemeVersion.bind(conceptNameCodeQueryBuilder);
this["0040A043.00080104"] = ConceptNameCodeSqQueryBuilder.prototype.getCodeMeaning.bind(conceptNameCodeQueryBuilder);

let contentQueryBuilder = new ContentSqQueryBuilder(this);
this["0040A730.0040A040"] = ContentSqQueryBuilder.prototype.getValueType.bind(contentQueryBuilder);
this["0040A730.0040A010"] = ContentSqQueryBuilder.prototype.getRelationshipType.bind(contentQueryBuilder);
this["0040A730.0040A160"] = ContentSqQueryBuilder.prototype.getValueType.bind(contentQueryBuilder);
this["0040A730.0040A043.00080100"] = ContentSqQueryBuilder.prototype.getConceptNameCodeValue.bind(contentQueryBuilder);
this["0040A730.0040A043.00080102"] = ContentSqQueryBuilder.prototype.getConceptNameCodingSchemeDesignator.bind(contentQueryBuilder);
this["0040A730.0040A043.00080103"] = ContentSqQueryBuilder.prototype.getConceptNameCodingSchemeVersion.bind(contentQueryBuilder);
this["0040A730.0040A043.00080104"] = ContentSqQueryBuilder.prototype.getConceptNameCodeMeaning.bind(contentQueryBuilder);
this["0040A730.0040A168.00080100"] = ContentSqQueryBuilder.prototype.getConceptCodeValue.bind(contentQueryBuilder);
this["0040A730.0040A168.00080102"] = ContentSqQueryBuilder.prototype.getConceptCodingSchemeDesignator.bind(contentQueryBuilder);
this["0040A730.0040A168.00080103"] = ContentSqQueryBuilder.prototype.getConceptCodingSchemeVersion.bind(contentQueryBuilder);
this["0040A730.0040A168.00080104"] = ContentSqQueryBuilder.prototype.getConceptCodeMeaning.bind(contentQueryBuilder);

let verifyingObserverQueryBuilder = new VerifyingObserverQueryBuilder(this);
this["0040A073.0040A075"] = VerifyingObserverQueryBuilder.prototype.getName.bind(verifyingObserverQueryBuilder);
this["0040A073.0040A030"] = VerifyingObserverQueryBuilder.prototype.getDateTime.bind(verifyingObserverQueryBuilder);
this["0040A073.0040A027"] = VerifyingObserverQueryBuilder.prototype.getOrganization.bind(verifyingObserverQueryBuilder);
}

/**
Expand Down Expand Up @@ -65,4 +93,285 @@ class InstanceQueryBuilder extends BaseQueryBuilder {
...q
};
}
}
}

class ConceptNameCodeSqQueryBuilder {
constructor(instanceQueryBuilder) {
/** @type {InstanceQueryBuilder} */
this.instanceQueryBuilder = instanceQueryBuilder;
}

isModelIncluded() {
return this.instanceQueryBuilder.includeQueries.find(v=> v.model.getTableName() === "ConceptNameCodeSQ");
}

getCodeValue(value) {
let q = this.instanceQueryBuilder.getStringQuery(dictionary.keyword.CodeValue, value);
this.addQuery(q);
}

getCodingSchemeDesignator(value) {
let q = this.instanceQueryBuilder.getStringQuery(dictionary.keyword.CodingSchemeDesignator, value);
this.addQuery(q);
}

getCodingSchemeVersion(value) {
let q = this.instanceQueryBuilder.getStringQuery(dictionary.keyword.CodingSchemeVersion ,value);
this.addQuery(q);
}

getCodeMeaning(value) {
let q = this.instanceQueryBuilder.getStringQuery(dictionary.keyword.CodeMeaning, value);
this.addQuery(q);
}

addQuery(q) {
let currentModel = this.isModelIncluded();
if (currentModel) {
currentModel.where = {
...currentModel.where,
...q
};
} else {
this.instanceQueryBuilder.includeQueries.push({
model: DicomCodeModel,
where: {
...q
},
attributes: []
});
}
}
}

class ContentSqQueryBuilder {
constructor(instanceQueryBuilder) {
/** @type {InstanceQueryBuilder} */
this.instanceQueryBuilder = instanceQueryBuilder;
this.conceptNameCodeInclude = undefined;
}

isModelIncluded() {
return this.instanceQueryBuilder.includeQueries.find(v=> v.model.getTableName() === "DicomContentSQ");
}

isConceptNameCodeInclude() {
let currentModel = this.isModelIncluded();
if (currentModel && currentModel.include) {
return currentModel.include.find( v => v.model.getTableName() === "ConceptNameCode");
}
return false;
}

isConceptCodeInclude() {
let currentModel = this.isModelIncluded();
if (currentModel && currentModel.include) {
return currentModel.include.find( v=> v.model.getTableName() === "ConceptCode");
}
return false;
}

getValueType(value) {
let q = this.instanceQueryBuilder.getStringQuery(dictionary.keyword.ValueType, value);
this.addQuery(q);
}

getTextValue(value) {
let q = this.instanceQueryBuilder.getStringQuery(dictionary.keyword.TextValue, value);
this.addQuery(q);
}

getRelationshipType(value) {
let q = this.instanceQueryBuilder.getStringQuery(dictionary.keyword.RelationshipType, value);
this.addQuery(q);
}

getConceptNameCodeValue(value) {
let q = this.instanceQueryBuilder.getStringQuery(dictionary.keyword.CodeValue, value);
this.addConceptNameCodeQuery(q);
}

getConceptNameCodingSchemeDesignator(value) {
let q = this.instanceQueryBuilder.getStringQuery(dictionary.keyword.CodingSchemeDesignator, value);
this.addConceptNameCodeQuery(q);
}

getConceptNameCodingSchemeVersion(value) {
let q = this.instanceQueryBuilder.getStringQuery(dictionary.keyword.CodingSchemeVersion, value);
this.addConceptNameCodeQuery(q);
}

getConceptNameCodeMeaning(value) {
let q = this.instanceQueryBuilder.getStringQuery(dictionary.keyword.CodeMeaning, value);
this.addConceptNameCodeQuery(q);
}

getConceptCodeValue (value) {
let q = this.instanceQueryBuilder.getStringQuery(dictionary.keyword.CodeValue, value);
this.addConceptCodeQuery(q);
}

getConceptCodingSchemeDesignator(value) {
let q = this.instanceQueryBuilder.getStringQuery(dictionary.keyword.CodingSchemeDesignator, value);
this.addConceptCodeQuery(q);
}

getConceptCodingSchemeVersion(value) {
let q = this.instanceQueryBuilder.getStringQuery(dictionary.keyword.CodingSchemeVersion, value);
this.addConceptCodeQuery(q);
}

getConceptCodeMeaning(value) {
let q = this.instanceQueryBuilder.getStringQuery(dictionary.keyword.CodeMeaning, value);
this.addConceptCodeQuery(q);
}

addQuery(q) {
let currentModel = this.isModelIncluded();
if (currentModel) {
currentModel.where = {
...currentModel.where,
...q
};
} else {
this.instanceQueryBuilder.includeQueries.push({
model: DicomContentSqModel,
where: {
...q
},
attributes: []
});
}
}

addConceptNameCodeQuery(q) {
if (!this.conceptNameCodeInclude) {
this.conceptNameCodeInclude = {
model: DicomCodeModel,
as: "ConceptNameCode",
where: {
...q
},
attributes: []
};
} else {
this.conceptNameCodeInclude.where = {
...this.conceptNameCodeInclude.where,
...q
};
}

let conceptNameIncluded = this.isConceptNameCodeInclude();

if (conceptNameIncluded) {
conceptNameIncluded = this.conceptNameCodeInclude;
} else {
this.addQuery({});
let currentModel = this.isModelIncluded();
currentModel.include = currentModel.include ? currentModel.include : [];
conceptNameIncluded = this.isConceptNameCodeInclude();
currentModel.include.push(this.conceptNameCodeInclude);
}
}

addConceptCodeQuery(q) {
if (!this.conceptCodeInclude) {
this.conceptCodeInclude = {
model: DicomCodeModel,
as: "ConceptCode",
where: {
...q
},
attributes: []
};
} else {
this.conceptCodeInclude.where = {
...this.conceptCodeInclude.where,
...q
};
}

let conceptCodeIncluded = this.isConceptCodeInclude();

if (conceptCodeIncluded) {
conceptCodeIncluded = this.conceptCodeInclude;
} else {
this.addQuery({});
let currentModel = this.isModelIncluded();
currentModel.include = currentModel.include ? currentModel.include : [];
conceptCodeIncluded = this.isConceptCodeInclude();
currentModel.include.push(this.conceptCodeInclude);
}
}
}

class VerifyingObserverQueryBuilder {
constructor(instanceQueryBuilder) {
/** @type {InstanceQueryBuilder} */
this.instanceQueryBuilder = instanceQueryBuilder;
}

isModelIncluded() {
return this.instanceQueryBuilder.includeQueries.find(v=> v.model.getTableName() === "VerifyingObserverSQ");
}

isPersonNameIncluded() {
let currentModel = this.isModelIncluded();
if (currentModel && currentModel.include) {
return currentModel.include.find( v => v.model.getTableName() === "PersonName");
}
return false;
}

getName(value) {
let q = this.instanceQueryBuilder.getPersonNameQuery(dictionary.keyword.VerifyingObserverName, value);
this.addQuery({});
let currentModel = this.isModelIncluded();
currentModel.include = currentModel.include ? currentModel.include : [];
let personNameIncluded = this.isPersonNameIncluded();
if (personNameIncluded) {
personNameIncluded.where = {
...personNameIncluded.where,
...q.query
};
} else {
currentModel.include.push({
model: PersonNameModel,
where: {
...q.query
},
attributes: []
});
}
}

getDateTime(value) {
let q = this.instanceQueryBuilder.getDateQuery(dictionary.keyword.VerificationDateTime, value);
this.addQuery(q);
}

getOrganization(value) {
let q = this.instanceQueryBuilder.getStringQuery(dictionary.keyword.VerifyingOrganization, value);
this.addQuery(q);
}

addQuery(q) {
let currentModel = this.isModelIncluded();
if (currentModel) {
currentModel.where = {
...currentModel.where,
...q
};
} else {
this.instanceQueryBuilder.includeQueries.push({
model: VerifyIngObserverSqModel,
where: {
...q
},
attributes: []
});
}
}
}

module.exports.InstanceQueryBuilder = InstanceQueryBuilder;
27 changes: 26 additions & 1 deletion models/sql/models/instance.mode.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
const { Sequelize, DataTypes, Model } = require("sequelize");
const _ = require("lodash");
const sequelizeInstance = require("@models/sql/instance");
const { vrTypeMapping } = require("../vrTypeMapping");
const { InstanceQueryBuilder } = require("@root/api-sql/dicom-web/controller/QIDO-RS/service/instanceQueryBuilder");
const { dictionary } = require("@models/DICOM/dicom-tags-dic");

class InstanceModel extends Model { };

Expand Down Expand Up @@ -51,7 +54,29 @@ InstanceModel.init({
});

InstanceModel.getDicomJson = async function(queryOptions) {
//TODO
let queryBuilder = new InstanceQueryBuilder(queryOptions);
let q = queryBuilder.build();
let seriesArray = await InstanceModel.findAll({
...q,
attributes: ["json", "x0020000D", "x0020000E", "x00080018"],
limit: queryOptions.limit,
offset: queryOptions.skip
});

return await Promise.all(seriesArray.map(async series => {
let { json } = series.toJSON();
// Set Retrieve URL
let studyInstanceUID = _.get(json, "0020000D.Value.0");
let seriesInstanceUID = _.get(json, "0020000E.Value.0");
let sopInstanceUID = _.get(json, "00080018.Value.0");
_.set(json, dictionary.keyword.RetrieveURL, {
vr: dictionary.tagVR[dictionary.keyword.RetrieveURL].vr,
Value: [
`${queryOptions.retrieveBaseUrl}/${studyInstanceUID}/series/${seriesInstanceUID}/instances/${sopInstanceUID}`
]
});
return json;
}));
};

module.exports.InstanceModel = InstanceModel;

0 comments on commit bbfe1d8

Please sign in to comment.