Skip to content

Commit

Permalink
refactor: move SQL out of plugins and integration
Browse files Browse the repository at this point in the history
  • Loading branch information
Chinlinlee committed Jul 31, 2023
1 parent fe6b794 commit 09bbc38
Show file tree
Hide file tree
Showing 16 changed files with 332 additions and 67 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
const fs = require("fs");
const { Dcm2JpgExecutor$Dcm2JpgOptions } = require("../../../../../models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm2jpg/Dcm2JpgExecutor$Dcm2JpgOptions");
const colorette = require("colorette");
const { DicomJpegGenerator } = require("@root/api/dicom-web/controller/STOW-RS/service/dicom-jpeg-generator");
/**
* @typedef JsDcm2JpegTask
* @property {Dcm2JpgExecutor$Dcm2JpgOptions} jsDcm2Jpeg
* @property {string} jpegFilename
*/

class SqlDicomJpegGenerator extends DicomJpegGenerator {
/**
*
* @param {import("../../../../../models/DICOM/dicom-json-model").DicomJsonModel} dicomJsonModel
* @param {string} dicomInstanceFilename
*/
constructor(dicomJsonModel, dicomInstanceFilename) {
super(dicomJsonModel, dicomInstanceFilename);
}



/**
* @private
*/
async insertStartTask_() {
let startTaskObj = {
studyUID: this.dicomJsonModel.uidObj.studyUID,
seriesUID: this.dicomJsonModel.uidObj.seriesUID,
instanceUID: this.dicomJsonModel.uidObj.sopInstanceUID,
status: false,
message: "processing",
taskTime: new Date(),
finishedTime: null,
fileSize: `${(fs.statSync(this.dicomInstanceFilename).size / 1024 / 1024).toFixed(3)}MB`
};


}

/**
* @private
*/
async insertEndTask_() {
let endTaskObj = {
studyUID: this.dicomJsonModel.uidObj.studyUID,
seriesUID: this.dicomJsonModel.uidObj.seriesUID,
instanceUID: this.dicomJsonModel.uidObj.sopInstanceUID,
status: true,
message: "generated",
finishedTime: new Date()
};

}

/**
* @private
* @param {string} message
*/
async insertErrorTask_(message) {
let errorTaskObj = {
studyUID: this.dicomJsonModel.uidObj.studyUID,
seriesUID: this.dicomJsonModel.uidObj.seriesUID,
instanceUID: this.dicomJsonModel.uidObj.sopInstanceUID,
status: false,
message: message,
finishedTime: new Date()
};

}

}

module.exports.SqlDicomJpegGenerator = SqlDicomJpegGenerator;
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const formidable = require("formidable");
const path = require("path");
const _ = require("lodash");

class StowRsRequestMultipartParser {
/**
* @param {import('express').Request} req
*/
constructor(req) {
this.request = req;
}

/**
*
* @return {Promise<import('../../../../../utils/typeDef/STOW-RS/STOW-RS.def').MultipartParseResult>}
*/
async parse() {
return new Promise((resolve, reject) => {
new formidable.IncomingForm({
uploadDir: path.join(process.cwd(), "/tempUploadFiles"),
maxFileSize: 100 * 1024 * 1024 * 1024,
multiples: true,
isGetBoundaryInData: true
}).parse(this.request, async (err, fields, files) => {

if (err) {
return reject(err);
}

let fileField = Object.keys(files).pop();
let uploadFiles = files[fileField];
if (!_.isArray(uploadFiles)) uploadFiles = [uploadFiles];

return resolve({
status: true,
multipart: {
fields: fields,
files: uploadFiles
}
});

});
});
}
}

module.exports.StowRsRequestMultipartParser = StowRsRequestMultipartParser;
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ const _ = require("lodash");
const { DicomJsonParser } = require("@models/DICOM/dicom-json-parser");
const { StowRsService } = require("@root/api/dicom-web/controller/STOW-RS/service/stow-rs.service");
const { DicomFileSaver } = require("@root/api/dicom-web/controller/STOW-RS/service/dicom-file-saver");
const { SqlDicomJsonModel: DicomJsonModel } = require("../../sql/models/dicom-json-model");
const { SqlDicomJsonModel: DicomJsonModel } = require("@models/sql/dicom-json-model");
const { SqlDicomJpegGenerator: DicomJpegGenerator } = require("./dicom-jpeg-generator");

class SqlStowRsService extends StowRsService {
/**
Expand All @@ -14,6 +15,35 @@ class SqlStowRsService extends StowRsService {
super(req, uploadFiles);
}

async storeInstances() {
for (let i = 0; i < this.uploadFiles.length; i++) {

let currentFile = this.uploadFiles[i];

let {
dicomJsonModel,
dicomFileSaveInfo
} = await this.storeInstance(currentFile);


//sync DICOM to FHIR
// if (isSyncToFhir) {
// let dicomFhirService = new DicomFhirService(this.request, dicomJsonModel);
// await dicomFhirService.initDicomFhirConverter();
// await dicomFhirService.postDicomToFhirServerAndStoreLog();
// }

//generate JPEG
// let dicomJpegGenerator = new DicomJpegGenerator(dicomJsonModel, dicomFileSaveInfo.instancePath);
// dicomJpegGenerator.generateAllFrames();
}

return {
code: this.responseCode,
responseMessage: this.responseMessage
};
}

/**
*
* @param {import("formidable").File} file
Expand Down
68 changes: 68 additions & 0 deletions api-sql/dicom-web/controller/STOW-RS/storeInstance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const { performance } = require("node:perf_hooks");
const errorResponseMessage = require("@root/utils/errorResponse/errorResponseMessage");
const { ApiLogger } = require("@root/utils/logs/api-logger");
const { Controller } = require("@root/api/controller.class");
const { StowRsRequestMultipartParser } = require("./service/request-multipart-parser");
const { SqlStowRsService: StowRsService } = require("./service/stow-rs.service");

class StoreInstanceController extends Controller {
constructor(req, res) {
super(req, res);
}

async mainProcess() {
let startSTOWTime = performance.now();
let retCode;
let storeMessage;
let apiLogger = new ApiLogger(this.request, "STOW-RS");
apiLogger.addTokenValue();

try {
let requestMultipartParser = new StowRsRequestMultipartParser(this.request);
let multipartParseResult = await requestMultipartParser.parse();

if (multipartParseResult.status) {
let stowRsService = new StowRsService(this.request, multipartParseResult.multipart.files);
let storeInstancesResult = await stowRsService.storeInstances();

retCode = storeInstancesResult.code;
storeMessage = storeInstancesResult.responseMessage;
}
let endSTOWTime = performance.now();
let elapsedTime = (endSTOWTime - startSTOWTime).toFixed(3);
apiLogger.logger.info(`Finished STOW-RS, elapsed time: ${elapsedTime} ms`);

this.response.writeHead(retCode, {
"Content-Type": "application/dicom"
});

return this.response.end(JSON.stringify(storeMessage));
} catch (e) {
let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e));
apiLogger.logger.error(errorStr);

let errorMessage =
errorResponseMessage.getInternalServerErrorMessage(errorStr);
this.response.writeHead(500, {
"Content-Type": "application/dicom+json"
});
return this.response.end(JSON.stringify(errorMessage));
}
}
}


/**
* To store DICOM instance
* 1. we parse multipart request to get file info that user upload
* 2. parse DICOM to JSON and store DICOM file from step 1
* 3. parse DICOM json model to FHIR (Patient, Endpoint, ImagingStudy)
* 4. upload FHIR to FHIR server
* @param {import('express').Request} req
* @param {import('express').Response} res
*/
module.exports = async function (req, res) {
let controller = new StoreInstanceController(req, res);

await controller.doPipeline();
};
35 changes: 35 additions & 0 deletions api-sql/dicom-web/stow-rs.route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const express = require("express");
const Joi = require("joi");
const { validateParams, intArrayJoi } = require("@root/api/validator");
const router = express();

//#region STOW-RS

/**
* @openapi
* /dicom-web/studies:
* post:
* tags:
* - STOW-RS
* description: store DICOM instance
* requestBody:
* content:
* multipart/related:
* schema:
* type: object
* properties:
* file:
* type: string
* format: binary
* encoding:
* file:
* contentType: application/dicom;
* responses:
* "200":
* description: The DICOM instance store successfully
*/
router.post("/studies", require("./controller/STOW-RS/storeInstance"));

//#endregion

module.exports = router;
2 changes: 1 addition & 1 deletion app.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// const mongodb = require("./models/mongodb/index");
require("./plugins/sql/init");
require("./models/sql/init");
const express = require("express");
const { createServer } = require("http");
const app = express();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const _ = require("lodash");

const { DicomJsonModel } = require("@models/DICOM/dicom-json-model");
const { PatientPersistentObject } = require("./po/patient.po");


class SqlDicomJsonModel extends DicomJsonModel {
Expand Down Expand Up @@ -32,8 +33,8 @@ class SqlDicomJsonModel extends DicomJsonModel {
}

async storePatientCollection(dicomJson) {
console.log(dicomJson);
console.log("TODO: Store Patient");
let patientPo = new PatientPersistentObject(dicomJson);
let patient = await patientPo.createPatient();
}
}

Expand Down
2 changes: 1 addition & 1 deletion plugins/sql/init.js → models/sql/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ async function init() {
foreignKey: "x00100010"
});

await sequelizeInstance.sync({});
await sequelizeInstance.sync({force: true});
} catch (e) {
console.error('Unable to connect to the database:', e);
process.exit(1);
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { Sequelize, DataTypes, Model } = require("sequelize");
const sequelizeInstance = require("@root/plugins/sql/instance");
const sequelizeInstance = require("@models/sql/instance");
const { vrTypeMapping } = require("../vrTypeMapping");

class PatientModel extends Model {};
Expand All @@ -23,9 +23,6 @@ PatientModel.init({
"x00100040": {
type: vrTypeMapping.CS
},
"x00101001": {
type: vrTypeMapping.PN
},
"x00102160": {
type: vrTypeMapping.SH
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { Sequelize, DataTypes, Model } = require("sequelize");
const sequelizeInstance = require("@root/plugins/sql/instance");
const sequelizeInstance = require("@models/sql/instance");


class PersonNameModel extends Model {}
Expand Down
Loading

0 comments on commit 09bbc38

Please sign in to comment.