Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
Chinlinlee committed Jan 24, 2024
2 parents b740097 + 73a4dc7 commit 7988265
Show file tree
Hide file tree
Showing 195 changed files with 8,561 additions and 8,567 deletions.
476 changes: 476 additions & 0 deletions .dependency-cruiser.js

Large diffs are not rendered by default.

14 changes: 12 additions & 2 deletions .env.template
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
# MongoDB
MONGODB_NAME="raccoon-dicom"
MONGODB_HOSTS=["mongodb"]
MONGODB_NAME="raccoon"
MONGODB_HOSTS=["127.0.0.1"]
MONGODB_PORTS=[27017]
MONGODB_USER="root"
MONGODB_PASSWORD="root"
MONGODB_AUTH_SOURCE="admin"
MONGODB_OPTIONS=""
MONGODB_IS_SHARDING_MODE=false

# SQL
SQL_HOST="127.0.0.1"
SQL_PORT="5432"
SQL_DB="raccoon"
SQL_TYPE="postgres"
SQL_USERNAME="postgres"
SQL_PASSWORD="postgres"
SQL_LOGGING=false
SQL_FORCE_SYNC=false

# Server
SERVER_PORT=8081
SERVER_SESSION_SECRET_KEY="secret-key"
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,7 @@
/config/logback.xml
/pm2log/**
!pm2log/.gitkeep
config/ae-prod.properties
config/ae-prod.properties

# ignore jscpd output report
/report
19 changes: 19 additions & 0 deletions .jscpd.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"threshold": 0.1,
"reporters": [
"html",
"console"
],
"ignore": [
"**/node_modules/**",
"models/DICOM/dcm4che/wrapper/**/*.ts",
"models/DICOM/dcm4che/wrapper/**/*.js",
"docs/**",
"test/**"
],
"ignorePattern": [
"const .* require.*"
],
"absolute": true,
"gitignore": true
}
44 changes: 23 additions & 21 deletions api/WADO-URI/controller/retrieveInstance.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,46 @@
const { WadoUriService, NotFoundInstanceError } = require("../service/WADO-URI.service");
const { WadoUriService } = require("@api/WADO-URI/service/WADO-URI.service");
const { Controller } = require("../../controller.class");
const { ApiLogger } = require("../../../utils/logs/api-logger");
const { ApiErrorArrayHandler } = require("@error/api-errors.handler");
const { EventOutcomeIndicator } = require("@models/DICOM/audit/auditUtils");

class RetrieveSingleInstanceController extends Controller {
constructor(req, res) {
super(req, res);
this.service = new WadoUriService(req, res);
this.logger = new ApiLogger(this.request, "WADO-URI");
this.service = new WadoUriService(req, res, this.logger);
}

async mainProcess() {
let {
let {
contentType
} = this.request.query;

if (!contentType) contentType = this.request.headers.accept;

try {

if (contentType === "application/dicom") {
this.service.getAndResponseDicomInstance();
if (contentType === "application/dicom" || !contentType) {

let dicomInstanceReadStream = await this.service.getDicomInstanceReadStream();
this.response.setHeader("Content-Type", "application/dicom");
dicomInstanceReadStream.pipe(this.response);
} else if (contentType === "image/jpeg") {
this.service.getAndResponseJpeg();
} else if (!contentType) {
this.service.getAndResponseDicomInstance();
let jpegBuffer = await this.service.handleRequestQueryAndGetJpeg();

this.response.setHeader("Content-Type", "image/jpeg");

this.response.end(jpegBuffer, "buffer");
}

} catch(e) {
let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e));
this.logger.error(errorStr);

this.response.writeHead(500, {
"Content-Type": "application/dicom+json"
});
this.response.end(JSON.stringify({
code: 500,
message: errorStr
}));
this.response.on("finish", () => this.service.auditInstanceTransferred());

} catch (e) {
this.service.auditInstanceTransferred(EventOutcomeIndicator.MajorFailure);
let apiErrorArrayHandler = new ApiErrorArrayHandler(this.response, this.logger, e);
return apiErrorArrayHandler.doErrorResponse();
}

}

}
Expand All @@ -48,7 +50,7 @@ class RetrieveSingleInstanceController extends Controller {
* @param {import("http").IncomingMessage} req
* @param {import("http").ServerResponse} res
*/
module.exports = async function(req, res) {
module.exports = async function (req, res) {
let controller = new RetrieveSingleInstanceController(req, res);

await controller.doPipeline();
Expand Down
147 changes: 40 additions & 107 deletions api/WADO-URI/service/WADO-URI.service.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
const mongoose = require("mongoose");
const fs = require("fs");
const _ = require("lodash");
const renderedService = require("../../dicom-web/controller/WADO-RS/service/rendered.service");
const { RenderedImageProcessParameterHandler, getInstanceFrameObj } = require("../../dicom-web/controller/WADO-RS/service/rendered.service");
const { Dcm2JpgExecutor } = require("../../../models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm2jpg/Dcm2JpgExecutor");
const { Dcm2JpgExecutor$Dcm2JpgOptions } = require("../../../models/DICOM/dcm4che/wrapper/org/github/chinlinlee/dcm2jpg/Dcm2JpgExecutor$Dcm2JpgOptions");
const sharp = require('sharp');
const imageSizeOf = require("image-size");
const { promisify } = require("util");
const imageSizeOfPromise = promisify(imageSizeOf);
const Magick = require("../../../models/magick");
const { NotFoundInstanceError, InvalidFrameNumberError, InstanceGoneError } = require("../../../error/dicom-instance");
const dicomModel = require("../../../models/mongodb/models/dicom");
const { InstanceModel } = require("@dbModels/instance.model");
const { AuditManager } = require("@models/DICOM/audit/auditManager");
const { EventType } = require("@models/DICOM/audit/eventType");
const { EventOutcomeIndicator } = require("@models/DICOM/audit/auditUtils");
const { DicomWebService } = require("@root/api/dicom-web/service/dicom-web.service");
const { ApiErrorArrayHandler } = require("@error/api-errors.handler");

class WadoUriService {

Expand All @@ -20,95 +23,17 @@ class WadoUriService {
* @param {import("http").IncomingMessage} req
* @param {import("http").ServerResponse} res
*/
constructor(req, res) {
constructor(req, res, apiLogger) {
this.request = req;
this.response = res;
this.apiLogger = apiLogger;
this.auditBeginTransferring();
}

async getAndResponseDicomInstance() {
try {

let dicomInstanceReadStream = await this.getDicomInstanceReadStream();

this.response.setHeader("Content-Type", "application/dicom");
dicomInstanceReadStream.pipe(this.response);
this.auditInstanceTransferred();

} catch (e) {
this.auditInstanceTransferred(EventOutcomeIndicator.MajorFailure);

if (e instanceof NotFoundInstanceError) {
this.response.writeHead(404, {
"Content-Type": "application/dicom+json"
});
return this.response.end();
} else if (e instanceof InstanceGoneError) {
this.response.writeHead(410, {
"Content-Type": "application/dicom+json"
});
return this.response.end(JSON.stringify({
Details: e.message,
HttpStatus: 410,
Message: "Image Gone",
Method: "GET"
}));
}

throw e;
}
let clonedRequestQuery = _.cloneDeep(this.request?.query);
_.set(clonedRequestQuery, "imageQuality", _.get(this.request.query, "imageQuality", ""));
this.renderedImageProcessParameterHandler = new RenderedImageProcessParameterHandler(clonedRequestQuery);
}

async getAndResponseJpeg() {
try {

let jpegBuffer = await this.handleRequestQueryAndGetJpeg();

this.response.setHeader("Content-Type", "image/jpeg");

this.response.end(jpegBuffer, "buffer");
this.auditInstanceTransferred();

} catch (e) {
this.auditInstanceTransferred(EventOutcomeIndicator.MajorFailure);

if (e instanceof NotFoundInstanceError) {

this.response.writeHead(404, {
"Content-Type": "application/dicom+json"
});
return this.response.end();

} else if (e instanceof InvalidFrameNumberError) {

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

return this.response.end(JSON.stringify({
Details: e.message,
HttpStatus: 400,
Message: "Bad request",
Method: "GET"
}));

} else if (e instanceof InstanceGoneError) {
this.response.writeHead(410, {
"Content-Type": "application/dicom+json"
});
return this.response.end(JSON.stringify({
Details: e.message,
HttpStatus: 410,
Message: "Image Gone",
Method: "GET"
}));
}

throw e;
}
}


/**
* @throws {NotFoundInstanceError}
* @returns {Promise<fs.ReadStream>}
Expand All @@ -127,7 +52,7 @@ class WadoUriService {
objectUID: instanceUID
} = this.request.query;

let imagePathObj = await dicomModel.getPathOfInstance({
let imagePathObj = await InstanceModel.getPathOfInstance({
studyUID,
seriesUID,
instanceUID
Expand All @@ -154,13 +79,13 @@ class WadoUriService {
async handleRequestQueryAndGetJpeg() {

let {
imageSharp,
imageSize,
magick
} = await this.handleFrameNumberAndGetImageObj();

await this.handleImageQuality(magick);
await this.handleRegion(this.request.query, imageSharp, magick);
await this.handleRowsAndColumns(this.request.query, imageSharp, magick);
await this.handleRegion(this.request.query, imageSize, magick);
await this.handleRowsAndColumns(this.request.query, imageSize, magick);
await this.handleImageICCProfile(this.request.query, magick, this.request.query.objectUID);

await magick.execCommand();
Expand All @@ -170,10 +95,7 @@ class WadoUriService {

async handleFrameNumberAndGetImageObj() {
let imagePathObj = await this.getDicomInstancePathObj();
let instanceFramesObj = await renderedService.getInstanceFrameObj(imagePathObj, {
"00281050": 1,
"00281051": 1
});
let instanceFramesObj = await getInstanceFrameObj(imagePathObj);
let instanceTotalFrameNumber = _.get(instanceFramesObj, "00280008.Value.0", 1);
let windowCenter = _.get(instanceFramesObj, "00281050.Value.0", "");
let windowWidth = _.get(instanceFramesObj, "00281051.Value.0", "");
Expand Down Expand Up @@ -206,7 +128,7 @@ class WadoUriService {
if (getFrameImageStatus.status) {

return {
imageSharp: sharp(jpegFile),
imageSize: await imageSizeOfPromise(jpegFile),
magick: new Magick(jpegFile)
};
}
Expand All @@ -215,17 +137,21 @@ class WadoUriService {
}

async handleImageQuality(magick) {
renderedService.handleImageQuality({
quality: _.get(this.request.query, "imageQuality", "")
}, magick);
this.renderedImageProcessParameterHandler.handleImageQuality(magick);
}

async handleRegion(param, imageSharp, magick) {
/**
*
* @param {Object} param
* @param {string} param.region
* @param {{height: number, width: number}} imageSize
* @param {Magick} magick
*/
async handleRegion(param, imageSize, magick) {
if (param.region) {
let [xMin, yMin, xMax, yMax] = param.region.split(",").map(v => parseFloat(v));
let imageMetadata = await imageSharp.metadata();
let imageWidth = imageMetadata.width;
let imageHeight = imageMetadata.height;
let imageWidth = imageSize.width;
let imageHeight = imageSize.height;
let extractLeft = imageWidth * xMin;
let extractTop = imageHeight * yMin;
let extractWidth = imageWidth * xMax - extractLeft;
Expand All @@ -234,21 +160,28 @@ class WadoUriService {
}
}

async handleRowsAndColumns(param, imageSharp, magick) {
let imageMetadata = await imageSharp.metadata();
/**
*
* @param {Object} param
* @param {string} param.rows
* @param {string} param.columns
* @param {{height: number, width: number}} imageSize
* @param {Magick} magick
*/
async handleRowsAndColumns(param, imageSize, magick) {
let rows = Number(param.rows);
let columns = Number(param.columns);
if (param.rows && param.columns) {
magick.resize(rows, columns);
} else if (param.rows) {
magick.resize(rows, imageMetadata.height);
magick.resize(rows, imageSize.height);
} else if (param.columns) {
magick.resize(imageMetadata.width, columns);
magick.resize(imageSize.width, columns);
}
}

async handleImageICCProfile(magick) {
await renderedService.handleImageICCProfile(this.request.query, magick, this.request.query.objectUID);
await this.renderedImageProcessParameterHandler.handleImageICCProfile(magick, this.request.query.objectUID);
}

async auditBeginTransferring() {
Expand Down
Loading

0 comments on commit 7988265

Please sign in to comment.