diff --git a/models/mongodb/deleteSchedule.js b/models/mongodb/deleteSchedule.js index ee6ce2f5..18704fcb 100644 --- a/models/mongodb/deleteSchedule.js +++ b/models/mongodb/deleteSchedule.js @@ -44,7 +44,7 @@ async function deleteExpireStudies() { deletedStudy.delete() ]); - await deletedStudy.deleteStudyFolder(); + await deletedStudy.deleteDicomInstances(); } } } @@ -74,7 +74,7 @@ async function deleteExpireSeries() { aDeletedSeries.delete() ]); - await aDeletedSeries.deleteSeriesFolder(); + await aDeletedSeries.deleteDicomInstances(); } } } @@ -94,8 +94,8 @@ async function deleteExpireInstances() { let diff = now.diff(updateAtDate, "days"); if (diff >= 30) { logger.info("delete expired instance: " + instanceUID); - await deletedInstance.deleteInstance(); await deletedInstance.delete(); + await deletedInstance.deleteDicomInstances(); } } } \ No newline at end of file diff --git a/models/mongodb/models/dicom.js b/models/mongodb/models/dicom.js index af9feed8..8ed118c9 100644 --- a/models/mongodb/models/dicom.js +++ b/models/mongodb/models/dicom.js @@ -12,12 +12,8 @@ const { getStoreDicomFullPath, IncludeFieldsFactory } = require("../service"); const { logger } = require("../../../utils/logs/log"); const { raccoonConfig } = require("@root/config-class"); const { dictionary } = require("@models/DICOM/dicom-tags-dic"); +const { DicomSchemaOptionsFactory, InstanceDocDicomJsonHandler } = require("../schema/dicom.schema"); -let Common; -if (raccoonConfig.dicomDimseConfig.enableDimse) { - require("@models/DICOM/dcm4che/java-instance"); - Common = require("@java-wrapper/org/github/chinlinlee/dcm777/net/common/Common").Common; -} let verifyingObserverSchema = new mongoose.Schema( { @@ -32,112 +28,12 @@ let verifyingObserverSchema = new mongoose.Schema( } ); -/** - * @constructs dicomModelSchema - */ -let dicomModelSchema = new mongoose.Schema( - { - "studyUID": { - type: String, - default: void 0, - index: true, - required: true - }, - "seriesUID": { - type: String, - default: void 0, - index: true, - required: true - }, - "instanceUID": { - type: String, - default: void 0, - index: true, - required: true - }, - "deleteStatus": { - type: Number, - default: 0 - }, - "00080020": new mongoose.Schema(dicomJsonAttributeDASchema, { - _id: false, - id: false, - toObject: { - getters: true - } - }), - "00080030": getVRSchema("TM"), - "00080050": { - ...dicomJsonAttributeSchema, - Value: [mongoose.SchemaTypes.String] - }, - "00080061": { - ...dicomJsonAttributeSchema, - Value: [mongoose.SchemaTypes.String] - }, - "00080090": getVRSchema("PN"), - "00100010": getVRSchema("PN"), - "00100020": { - ...dicomJsonAttributeSchema, - Value: [mongoose.SchemaTypes.String] - }, - "0020000D": { - ...dicomJsonAttributeSchema, - Value: [mongoose.SchemaTypes.String] - }, - "00200010": { - ...dicomJsonAttributeSchema, - Value: [mongoose.SchemaTypes.String] - }, - "00080060": { - ...dicomJsonAttributeSchema, - Value: [mongoose.SchemaTypes.String] - }, - "0020000E": { - ...dicomJsonAttributeSchema, - Value: [mongoose.SchemaTypes.String] - }, - "00200011": { - ...dicomJsonAttributeSchema, - Value: [mongoose.SchemaTypes.String] - }, - "00400244": new mongoose.Schema(dicomJsonAttributeDASchema, { - _id: false, - id: false, - toObject: { - getters: true - } - }), - "00400275": dicomJsonAttributeSchema, - "0040A073": { - ...dicomJsonAttributeSchema, - Value: [verifyingObserverSchema] - }, - "00080016": { - ...dicomJsonAttributeSchema, - Value: [mongoose.SchemaTypes.String] - }, - "00080018": { - ...dicomJsonAttributeSchema, - Value: [mongoose.SchemaTypes.String] - }, - "00200013": { - ...dicomJsonAttributeSchema, - Value: [mongoose.SchemaTypes.String] - } - }, +let dicomSchemaOptions = _.merge( + DicomSchemaOptionsFactory.get("instance", InstanceDocDicomJsonHandler), { strict: false, - versionKey: false, - toObject: { - getters: true - }, methods: { - async incrementDeleteStatus() { - this.deleteStatus = this.deleteStatus + 1; - await this.save(); - }, - async deleteInstance() { + deleteDicomInstances: async function () { let instancePath = this.instancePath; try { logger.warn("Permanently delete instance: " + instancePath); @@ -151,23 +47,9 @@ let dicomModelSchema = new mongoose.Schema( } catch (e) { console.error(e); } - }, - getAttributes: async function () { - let study = this.toObject(); - delete study._id; - delete study.id; - - let jsonStr = JSON.stringify(study); - return await Common.getAttributesFromJsonString(jsonStr); } }, statics: { - getDimseResultCursor: async (query, keys) => { - return mongoose.model("dicom").find(query, keys).setOptions({ - strictQuery: false - }) - .cursor(); - }, getAuditInstancesInfoFromStudyUID: async (studyUID) => { let instances = await mongoose.model("dicom").find({ studyUID }).exec(); @@ -194,60 +76,9 @@ let dicomModelSchema = new mongoose.Schema( return instanceInfos; }, - /** - * - * @param {import("../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions} queryOptions - * @returns - */ - getDicomJson: async function (queryOptions) { - + getDicomJsonProjection: function (queryOptions) { let includeFieldsFactory = new IncludeFieldsFactory(queryOptions.includeFields); - let instanceFields = includeFieldsFactory.getInstanceLevelFields(); - - try { - - let docs = await mongoose - .model("dicom") - .find({ - ...queryOptions.query, - deleteStatus: { - $eq: 0 - } - }, { - ...instanceFields - }) - .setOptions({ - strictQuery: false - }) - .limit(queryOptions.limit) - .skip(queryOptions.skip) - .exec(); - - let instanceDicomJson = docs.map(v => { - let obj = v.toObject(); - delete obj._id; - delete obj.id; - obj["00081190"] = { - vr: "UR", - Value: [ - `${queryOptions.retrieveBaseUrl}/${obj["0020000D"]["Value"][0]}/series/${obj["0020000E"]["Value"][0]}/instances/${obj["00080018"]["Value"][0]}` - ] - }; - - _.set(obj, dictionary.keyword.RetrieveAETitle, { - ...dictionary.tagVR[dictionary.keyword.RetrieveAETitle], - Value: [raccoonConfig.aeTitle] - }); - - return obj; - }); - - return instanceDicomJson; - - } catch (e) { - throw e; - } - + return includeFieldsFactory.getInstanceLevelFields(); }, /** * @@ -257,29 +88,9 @@ let dicomModelSchema = new mongoose.Schema( * @param {string} iParam.instanceUID */ getPathOfInstance: async function (iParam) { - let { studyUID, seriesUID, instanceUID } = iParam; - try { - let query = { - $and: [ - { - studyUID: studyUID - }, - { - seriesUID: seriesUID - }, - { - instanceUID: instanceUID - }, - { - deleteStatus: { - $eq: 0 - } - } - ] - }; - let doc = await mongoose.model("dicom").findOne(query, { + let doc = await mongoose.model("dicom").findOne(mongoose.model("dicom").getPathGroupQuery(iParam), { studyUID: 1, seriesUID: 1, instanceUID: 1, @@ -299,6 +110,27 @@ let dicomModelSchema = new mongoose.Schema( throw e; } }, + getPathGroupQuery: function (iParam) { + let { studyUID, seriesUID, instanceUID } = iParam; + return { + $and: [ + { + studyUID: studyUID + }, + { + seriesUID: seriesUID + }, + { + instanceUID: instanceUID + }, + { + deleteStatus: { + $eq: 0 + } + } + ] + }; + }, /** * * @param {string} studyUID @@ -332,11 +164,107 @@ let dicomModelSchema = new mongoose.Schema( .limit(1) .exec(); } - }, - timestamps: true + } } ); +/** + * @constructs dicomModelSchema + */ +let dicomModelSchema = new mongoose.Schema( + { + "studyUID": { + type: String, + default: void 0, + index: true, + required: true + }, + "seriesUID": { + type: String, + default: void 0, + index: true, + required: true + }, + "instanceUID": { + type: String, + default: void 0, + index: true, + required: true + }, + "deleteStatus": { + type: Number, + default: 0 + }, + "00080020": new mongoose.Schema(dicomJsonAttributeDASchema, { + _id: false, + id: false, + toObject: { + getters: true + } + }), + "00080030": getVRSchema("TM"), + "00080050": { + ...dicomJsonAttributeSchema, + Value: [mongoose.SchemaTypes.String] + }, + "00080061": { + ...dicomJsonAttributeSchema, + Value: [mongoose.SchemaTypes.String] + }, + "00080090": getVRSchema("PN"), + "00100010": getVRSchema("PN"), + "00100020": { + ...dicomJsonAttributeSchema, + Value: [mongoose.SchemaTypes.String] + }, + "0020000D": { + ...dicomJsonAttributeSchema, + Value: [mongoose.SchemaTypes.String] + }, + "00200010": { + ...dicomJsonAttributeSchema, + Value: [mongoose.SchemaTypes.String] + }, + "00080060": { + ...dicomJsonAttributeSchema, + Value: [mongoose.SchemaTypes.String] + }, + "0020000E": { + ...dicomJsonAttributeSchema, + Value: [mongoose.SchemaTypes.String] + }, + "00200011": { + ...dicomJsonAttributeSchema, + Value: [mongoose.SchemaTypes.String] + }, + "00400244": new mongoose.Schema(dicomJsonAttributeDASchema, { + _id: false, + id: false, + toObject: { + getters: true + } + }), + "00400275": dicomJsonAttributeSchema, + "0040A073": { + ...dicomJsonAttributeSchema, + Value: [verifyingObserverSchema] + }, + "00080016": { + ...dicomJsonAttributeSchema, + Value: [mongoose.SchemaTypes.String] + }, + "00080018": { + ...dicomJsonAttributeSchema, + Value: [mongoose.SchemaTypes.String] + }, + "00200013": { + ...dicomJsonAttributeSchema, + Value: [mongoose.SchemaTypes.String] + } + }, + dicomSchemaOptions +); + dicomModelSchema.index({ "0020000D": 1 }); diff --git a/models/mongodb/models/dicomSeries.js b/models/mongodb/models/dicomSeries.js index 06224f69..6529cf20 100644 --- a/models/mongodb/models/dicomSeries.js +++ b/models/mongodb/models/dicomSeries.js @@ -4,179 +4,73 @@ const mongoose = require("mongoose"); const _ = require("lodash"); const { tagsNeedStore } = require("../../DICOM/dicom-tags-mapping"); const { getVRSchema } = require("../schema/dicomJsonAttribute"); -const { getStoreDicomFullPathGroup, IncludeFieldsFactory } = require("../service"); -const { dictionary } = require("@models/DICOM/dicom-tags-dic"); +const { IncludeFieldsFactory } = require("../service"); const { raccoonConfig } = require("@root/config-class"); const { logger } = require("@root/utils/logs/log"); +const { BaseDicomSchemaDef, DicomSchemaOptionsFactory, SeriesDocDicomJsonHandler } = require("../schema/dicom.schema"); -let Common; -if (raccoonConfig.dicomDimseConfig.enableDimse) { - require("@models/DICOM/dcm4che/java-instance"); - Common = require("@java-wrapper/org/github/chinlinlee/dcm777/net/common/Common").Common; -} - -let dicomSeriesSchema = new mongoose.Schema( - { - studyUID: { - type: String, - default: void 0, - index: true, - required: true - }, - seriesUID: { - type: String, - default: void 0, - index: true, - required: true - }, - seriesPath: { - type: String, - default: void 0 - }, - deleteStatus: { - type: Number, - default: 0 - } - }, +let dicomSeriesSchemaOptions = _.merge( + DicomSchemaOptionsFactory.get("series", SeriesDocDicomJsonHandler), { - strict: true, - versionKey: false, - toObject: { - getters: true - }, methods: { - async incrementDeleteStatus() { - this.deleteStatus = this.deleteStatus + 1; - await this.save(); - }, - async deleteSeriesFolder() { + deleteDicomInstances: async function () { let seriesPath = this.seriesPath; logger.warn("Permanently delete series folder: " + seriesPath); await fsP.rm(path.join(raccoonConfig.dicomWebConfig.storeRootPath, seriesPath), { force: true, recursive: true }); - }, - getAttributes: async function () { - let series = this.toObject(); - delete series._id; - delete series.id; - - let jsonStr = JSON.stringify(series); - return await Common.getAttributesFromJsonString(jsonStr); } }, statics: { - getDimseResultCursor: async function (query, keys) { - return mongoose.model("dicomSeries").find(query, keys).setOptions({ - strictQuery: false - }) - .cursor(); - }, /** * - * @param {import("../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions} queryOptions + * @param {import("@root/utils/typeDef/dicom").DicomJsonMongoQueryOptions} queryOptions * @returns */ - getDicomJson: async function (queryOptions) { + getDicomJsonProjection: function (queryOptions) { let includeFieldsFactory = new IncludeFieldsFactory(queryOptions.includeFields); - let seriesFields = includeFieldsFactory.getSeriesLevelFields(); - - try { - let docs = await mongoose - .model("dicomSeries") - .find({ - ...queryOptions.query, - deleteStatus: { - $eq: 0 - } - }, { - ...seriesFields - }) - .setOptions({ - strictQuery: false - }) - .limit(queryOptions.limit) - .skip(queryOptions.skip) - .exec(); - - - let seriesDicomJson = docs.map((v) => { - let obj = v.toObject(); - delete obj._id; - delete obj.id; - obj["00081190"] = { - vr: "UR", - Value: [ - `${queryOptions.retrieveBaseUrl}/${obj["0020000D"]["Value"][0]}/series/${obj["0020000E"]["Value"][0]}` - ] - }; - - _.set(obj, dictionary.keyword.RetrieveAETitle, { - ...dictionary.tagVR[dictionary.keyword.RetrieveAETitle], - Value: [raccoonConfig.aeTitle] - }); - - return obj; - }); - - return seriesDicomJson; - - } catch (e) { - throw e; - } + return includeFieldsFactory.getSeriesLevelFields(); }, - /** - * - * @param {object} iParam - * @param {string} iParam.studyUID - * @param {string} iParam.seriesUID - */ - getPathGroupOfInstances: async function (iParam) { + getPathGroupQuery: function (iParam) { let { studyUID, seriesUID } = iParam; - try { - let query = [ - { - $match: { - $and: [ - { - seriesUID: seriesUID - }, - { - studyUID: studyUID - } - ] + return { + $match: { + $and: [ + { + seriesUID: seriesUID + }, + { + studyUID: studyUID } - }, - { - $group: { - _id: "$seriesUID", - pathList: { - $addToSet: { - studyUID: "$studyUID", - seriesUID: "$seriesUID", - instanceUID: "$instanceUID", - instancePath: "$instancePath" - } - } - } - } - ]; - let docs = await mongoose.model("dicom").aggregate(query); - let pathGroup = _.get(docs, "0.pathList", []); - - let fullPathGroup = getStoreDicomFullPathGroup(pathGroup); - - return fullPathGroup; - } catch (e) { - throw e; - } + ] + } + }; } - }, - timestamps: true + } } ); +let dicomSeriesSchema = new mongoose.Schema( + { + ...BaseDicomSchemaDef + }, + dicomSeriesSchemaOptions +); + +dicomSeriesSchema.add({ + seriesUID: { + type: String, + default: void 0, + index: true, + required: true + }, + seriesPath: { + type: String, + default: void 0 + } +}); + for (let tag in tagsNeedStore.Study) { let vr = tagsNeedStore.Study[tag].vr; let tagSchema = getVRSchema(vr); diff --git a/models/mongodb/models/dicomStudy.js b/models/mongodb/models/dicomStudy.js index d4788c77..6bc5fcca 100644 --- a/models/mongodb/models/dicomStudy.js +++ b/models/mongodb/models/dicomStudy.js @@ -1,168 +1,64 @@ +const _ = require("lodash"); const fsP = require("fs/promises"); const path = require("path"); +const { raccoonConfig } = require("@root/config-class"); const mongoose = require("mongoose"); -const _ = require("lodash"); const { tagsNeedStore } = require("../../DICOM/dicom-tags-mapping"); const { getVRSchema } = require("../schema/dicomJsonAttribute"); -const { - getStoreDicomFullPathGroup, - IncludeFieldsFactory -} = require("../service"); -const { - tagsOfRequiredMatching -} = require("../../DICOM/dicom-tags-mapping"); -const { raccoonConfig } = require("../../../config-class"); -const { dictionary } = require("@models/DICOM/dicom-tags-dic"); +const { BaseDicomSchemaDef, DicomSchemaOptionsFactory, StudyDocDicomJsonHandler } = require("../schema/dicom.schema"); const { logger } = require("@root/utils/logs/log"); +const { IncludeFieldsFactory } = require("../service"); -let Common; -if (raccoonConfig.dicomDimseConfig.enableDimse) { - require("@models/DICOM/dcm4che/java-instance"); - Common = require("@java-wrapper/org/github/chinlinlee/dcm777/net/common/Common").Common; -} - -let dicomStudySchema = new mongoose.Schema( - { - studyUID: { - type: String, - default: void 0, - index: true, - required: true - }, - studyPath: { - type: String, - default: void 0 - }, - deleteStatus: { - type: Number, - default: 0 - } - }, +let dicomStudySchemaOptions = _.merge( + DicomSchemaOptionsFactory.get("study", StudyDocDicomJsonHandler), { - strict: true, - versionKey: false, - toObject: { - getters: true - }, methods: { - async incrementDeleteStatus() { - this.deleteStatus = this.deleteStatus + 1; - await this.save(); - }, - async deleteStudyFolder() { + deleteDicomInstances: async function () { + let studyPath = this.studyPath; logger.warn("Permanently delete study folder: " + studyPath); await fsP.rm(path.join(raccoonConfig.dicomWebConfig.storeRootPath, studyPath), { force: true, recursive: true }); - }, - getAttributes: async function () { - let study = this.toObject(); - delete study._id; - delete study.id; - - let jsonStr = JSON.stringify(study); - return await Common.getAttributesFromJsonString(jsonStr); } }, statics: { - getDimseResultCursor: async function (query, keys) { - return mongoose.model("dicomStudy").find(query, keys).setOptions({ - strictQuery: false - }) - .cursor(); - }, /** * - * @param {import("../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions} queryOptions + * @param {import("@root/utils/typeDef/dicom").DicomJsonMongoQueryOptions} queryOptions * @returns */ - getDicomJson: async function (queryOptions) { + getDicomJsonProjection: function (queryOptions) { let includeFieldsFactory = new IncludeFieldsFactory(queryOptions.includeFields); - let studyFields = includeFieldsFactory.getStudyLevelFields(); - - try { - let docs = await mongoose.model("dicomStudy").find({ - ...queryOptions.query, - deleteStatus: { - $eq: 0 - } - }, studyFields) - .limit(queryOptions.limit) - .skip(queryOptions.skip) - .setOptions({ - strictQuery: false - }) - .exec(); - - let studyDicomJson = docs.map((v) => { - let obj = v.toObject(); - delete obj._id; - delete obj.id; - obj["00081190"] = { - vr: "UR", - Value: [`${queryOptions.retrieveBaseUrl}/${obj["0020000D"]["Value"][0]}`] - }; - - _.set(obj, dictionary.keyword.RetrieveAETitle, { - ...dictionary.tagVR[dictionary.keyword.RetrieveAETitle], - Value: [raccoonConfig.aeTitle] - }); - - return obj; - }); - - return studyDicomJson; - - } catch (e) { - throw e; - } + return includeFieldsFactory.getStudyLevelFields(); }, - /** - * - * @param {Object} iParam - * @param {string} iParam.studyUID - */ - getPathGroupOfInstances: async function (iParam) { + getPathGroupQuery: function (iParam) { let { studyUID } = iParam; - try { - let query = [ - { - $match: { - studyUID: studyUID - } - }, - { - $group: { - _id: "$studyUID", - pathList: { - $addToSet: { - studyUID: "$studyUID", - seriesUID: "$seriesUID", - instanceUID: "$instanceUID", - instancePath: "$instancePath" - } - } - } - } - ]; - let docs = await mongoose.model("dicom").aggregate(query).exec(); - let pathGroup = _.get(docs, "0.pathList", []); - - let fullPathGroup = getStoreDicomFullPathGroup(pathGroup); - - return fullPathGroup; - - } catch (e) { - throw e; - } + return { + $match: { + studyUID: studyUID + } + }; } - }, - timestamps: true + } } ); +let dicomStudySchema = new mongoose.Schema( + { + ...BaseDicomSchemaDef + }, + dicomStudySchemaOptions +); + +dicomStudySchema.add({ + studyPath: { + type: String, + default: void 0 + } +}); + for (let tag in tagsNeedStore.Study) { let vr = tagsNeedStore.Study[tag].vr; let tagSchema = getVRSchema(vr); diff --git a/models/mongodb/models/patient.js b/models/mongodb/models/patient.js index 4c945b5f..79a4b5f9 100644 --- a/models/mongodb/models/patient.js +++ b/models/mongodb/models/patient.js @@ -1,20 +1,40 @@ -const path = require("path"); 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 { raccoonConfig } = require("@root/config-class"); +const { DicomSchemaOptionsFactory, PatientDocDicomJsonHandler } = require("../schema/dicom.schema"); -let Common; -if (raccoonConfig.dicomDimseConfig.enableDimse) { - require("@models/DICOM/dcm4che/java-instance"); - Common = require("@java-wrapper/org/github/chinlinlee/dcm777/net/common/Common").Common; -} - +let patientSchemaOptions = _.merge( + DicomSchemaOptionsFactory.get("patient", PatientDocDicomJsonHandler), + { + statics: { + getPathGroupQuery: function (iParam) { + let { patientID } = iParam; + return { + $match: { + "00100020.Value": patientID + } + }; + }, + /** + * + * @param {import("../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions} queryOptions + * @returns + */ + getDicomJsonProjection: function (queryOptions) { + let fields = {}; + for (let tag in tagsOfRequiredMatching.Patient) { + fields[tag] = 1; + } + return fields; + } + } + } +); let patientSchema = new mongoose.Schema( { @@ -27,103 +47,13 @@ let patientSchema = new mongoose.Schema( studyPaths: { type: [String], default: void 0 - } - }, - { - strict: true, - versionKey: false, - toObject: { - getters: true }, - methods: { - getAttributes: async function () { - let patient = this.toObject(); - delete patient._id; - delete patient.id; - - let jsonStr = JSON.stringify(patient); - return await Common.getAttributesFromJsonString(jsonStr); - } - }, - statics: { - getDimseResultCursor: async function (query, keys) { - return mongoose.model("patient").find(query, keys).setOptions({ - strictQuery: false - }) - .cursor(); - }, - /** - * - * @param {import("../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions} queryOptions - * @returns - */ - getDicomJson: async function (queryOptions) { - let patientFields = getPatientLevelFields(); - - try { - let docs = await mongoose.model("patient").find(queryOptions.query, patientFields) - .limit(queryOptions.limit) - .skip(queryOptions.skip) - .setOptions({ - strictQuery: false - }) - .exec(); - - - let patientDicomJson = docs.map((v) => { - let obj = v.toObject(); - delete obj._id; - delete obj.id; - return obj; - }); - - return patientDicomJson; - - } catch (e) { - throw e; - } - }, - /** - * - * @param {Object} iParam - * @param {string} iParam.studyUID - */ - getPathGroupOfInstances: async function (iParam) { - let { patientID } = iParam; - try { - let query = [ - { - $match: { - "00100020.Value": patientID - } - }, - { - $group: { - _id: "$studyUID", - pathList: { - $addToSet: { - studyUID: "$studyUID", - seriesUID: "$seriesUID", - instanceUID: "$instanceUID", - instancePath: "$instancePath" - } - } - } - } - ]; - let docs = await mongoose.model("dicom").aggregate(query).exec(); - let pathGroup = _.get(docs, "0.pathList", []); - - let fullPathGroup = getStoreDicomFullPathGroup(pathGroup); - - return fullPathGroup; - - } catch (e) { - throw e; - } - } + deleteStatus: { + type: Number, + default: 0 } - } + }, + patientSchemaOptions ); for (let tag in tagsNeedStore.Patient) { @@ -142,15 +72,6 @@ patientSchema.index({ "00100020": 1 }); - -function getPatientLevelFields() { - let fields = {}; - for (let tag in tagsOfRequiredMatching.Patient) { - fields[tag] = 1; - } - return fields; -} - let patientModel = mongoose.model( "patient", patientSchema, @@ -158,5 +79,3 @@ let patientModel = mongoose.model( ); module.exports = patientModel; - -module.exports.getPatientLevelFields = getPatientLevelFields; diff --git a/models/mongodb/schema/dicom.schema.js b/models/mongodb/schema/dicom.schema.js new file mode 100644 index 00000000..24ac0ecb --- /dev/null +++ b/models/mongodb/schema/dicom.schema.js @@ -0,0 +1,277 @@ +const fsP = require("fs/promises"); +const path = require("path"); +const mongoose = require("mongoose"); +const _ = require("lodash"); +const { raccoonConfig } = require("@root/config-class"); +const { logger } = require("@root/utils/logs/log"); +const { IncludeFieldsFactory, getStoreDicomFullPath, getStoreDicomFullPathGroup } = require("../service"); +const { dictionary } = require("@models/DICOM/dicom-tags-dic"); + +let Common; +if (raccoonConfig.dicomDimseConfig.enableDimse) { + require("@models/DICOM/dcm4che/java-instance"); + Common = require("@java-wrapper/org/github/chinlinlee/dcm777/net/common/Common").Common; +} + +const BaseDicomSchemaDef = { + studyUID: { + type: String, + default: void 0, + index: true, + required: true + }, + deleteStatus: { + type: Number, + default: 0 + } +}; + +class DicomSchemaOptionsFactory { + constructor() {} + + /** + * + * @param {"patient" |"study" | "series" | "instance"} level + * @param {typeof DocDicomJsonHandler} docDicomJsonHandlerType + * @returns + */ + static get(level, docDicomJsonHandlerType) { + return { + strict: true, + versionKey: false, + toObject: { + getters: true + }, + methods: { + incrementDeleteStatus: async function () { + this.deleteStatus = this.deleteStatus + 1; + await this.save(); + }, + deleteDicomInstances: async function() { + throw new Error("Not Implemented"); + }, + getAttributes: async function () { + let doc = this.toObject(); + delete doc._id; + delete doc.id; + + let jsonStr = JSON.stringify(doc); + return await Common.getAttributesFromJsonString(jsonStr); + } + }, + statics: { + getDimseResultCursor: async function (query, keys) { + return mongoose.model(DicomModelNames[level]).find(query, keys).setOptions({ + strictQuery: false + }) + .cursor(); + }, + /** + * + * @param {import("../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions} queryOptions + * @returns + */ + getDicomJson: async function (queryOptions) { + let projection = mongoose.model(DicomModelNames[level]).getDicomJsonProjection(queryOptions); + + try { + let docs = await mongoose.model(DicomModelNames[level]).find({ + ...queryOptions.query, + deleteStatus: { + $eq: 0 + } + }, projection) + .limit(queryOptions.limit) + .skip(queryOptions.skip) + .setOptions({ + strictQuery: false + }) + .exec(); + + let docDicomJsonHandler = new docDicomJsonHandlerType(docs, queryOptions); + let dicomJson = docDicomJsonHandler.get(); + + return dicomJson; + + } catch (e) { + throw e; + } + }, + /** + * + * @param {import("../../../utils/typeDef/dicom").DicomJsonMongoQueryOptions} queryOptions + * @returns + */ + getDicomJsonProjection: function (queryOptions) { + throw new Error("Not Implemented"); + }, + /** + * + * @param {Object} iParam + * @param {string} iParam.studyUID + */ + getPathGroupOfInstances: async function (iParam) { + try { + let query = [ + { + ...mongoose.model(DicomModelNames[level]).getPathGroupQuery(iParam) + }, + { + ...mongoose.model(DicomModelNames[level]).getPathGroupQueryOptions() + } + ]; + let docs = await mongoose.model("dicom").aggregate(query).exec(); + let pathGroup = _.get(docs, "0.pathList", []); + + let fullPathGroup = getStoreDicomFullPathGroup(pathGroup); + + return fullPathGroup; + + } catch (e) { + throw e; + } + }, + getPathGroupQueryOptions: function (level) { + return { + $group: { + _id: PathGroupIdField[level], + pathList: { + $addToSet: { + studyUID: "$studyUID", + seriesUID: "$seriesUID", + instanceUID: "$instanceUID", + instancePath: "$instancePath" + } + } + } + }; + }, + getPathGroupQuery: function (iParam) { + throw new Error("Not Implemented"); + } + }, + timestamps: true + }; + } +} + +const PathGroupIdField = Object.freeze({ + "patient": "$patientID", + "study": "$studyUID", + "series": "$seriesUID", + "instance": "$instanceUID" +}); + +const DicomModelNames = Object.freeze({ + "patient": "patient", + "study": "dicomStudy", + "series": "dicomSeries", + "instance": "dicom" +}); + +class DocDicomJsonHandler { + constructor(docs, queryOptions) { + this.docs = docs; + this.queryOptions = queryOptions; + } + + /** + * @private + * @param {any} obj + * @returns + */ + getPreprocessedDoc_(obj) { + let preProcessedDoc = obj.toObject(); + delete preProcessedDoc._id; + delete preProcessedDoc.id; + return preProcessedDoc; + } + + get() { + if (this.docs) { + return this.docs?.map((v) => { + let obj = this.getPreprocessedDoc_(v); + + this.setRetrieveUrl(obj); + this.setRetrieveAETitle(obj); + + return obj; + }); + } + return []; + } + + setRetrieveUrl(obj) { + _.set(obj, dictionary.keyword.RetrieveURL, { + ...dictionary.tagVR[dictionary.keyword.RetrieveURL], + Value: this.getRetrieveUrlValue(obj) + }); + } + + getRetrieveUrlValue(obj) { + throw new Error("Not Implemented"); + } + + setRetrieveAETitle(obj) { + _.set(obj, dictionary.keyword.RetrieveAETitle, { + ...dictionary.tagVR[dictionary.keyword.RetrieveAETitle], + Value: [raccoonConfig.aeTitle] + }); + } +} + +class PatientDocDicomJsonHandler extends DocDicomJsonHandler { + constructor (docs, queryOptions) { + super(docs, queryOptions); + } + + setRetrieveUrl(obj) { + return; + } + + setRetrieveAETitle(obj) { + return; + } +} + +class StudyDocDicomJsonHandler extends DocDicomJsonHandler { + constructor (docs, queryOptions) { + super(docs, queryOptions); + } + + getRetrieveUrlValue(obj) { + return [`${this.queryOptions.retrieveBaseUrl}/${obj["0020000D"]["Value"][0]}`]; + } +} + +class SeriesDocDicomJsonHandler extends DocDicomJsonHandler { + constructor (docs, queryOptions) { + super(docs, queryOptions); + } + + getRetrieveUrlValue(obj) { + return [ + `${this.queryOptions.retrieveBaseUrl}/${obj["0020000D"]["Value"][0]}/series/${obj["0020000E"]["Value"][0]}` + ]; + } +} + +class InstanceDocDicomJsonHandler extends DocDicomJsonHandler { + constructor (docs, queryOptions) { + super(docs, queryOptions); + } + + getRetrieveUrlValue(obj) { + return [ + `${this.queryOptions.retrieveBaseUrl}/${obj["0020000D"]["Value"][0]}/series/${obj["0020000E"]["Value"][0]}/instances/${obj["00080018"]["Value"][0]}` + ]; + } +} + + +module.exports.BaseDicomSchemaDef = BaseDicomSchemaDef; +module.exports.DicomSchemaOptionsFactory = DicomSchemaOptionsFactory; +module.exports.PatientDocDicomJsonHandler = PatientDocDicomJsonHandler; +module.exports.StudyDocDicomJsonHandler = StudyDocDicomJsonHandler; +module.exports.SeriesDocDicomJsonHandler = SeriesDocDicomJsonHandler; +module.exports.InstanceDocDicomJsonHandler = InstanceDocDicomJsonHandler; \ No newline at end of file