-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(UPS-RS): add create and get workitem APIs
- Loading branch information
1 parent
2695ccd
commit 4d55216
Showing
13 changed files
with
715 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
const _ = require("lodash"); | ||
const { | ||
CreateWorkItemService | ||
} = require("./service/create-workItem.service"); | ||
const { ApiLogger } = require("../../../../utils/logs/api-logger"); | ||
const { Controller } = require("../../../controller.class"); | ||
const { DicomWebServiceError } = require("@error/dicom-web-service"); | ||
|
||
class CreateWorkItemController extends Controller { | ||
constructor(req, res) { | ||
super(req, res); | ||
} | ||
|
||
async mainProcess() { | ||
let apiLogger = new ApiLogger(this.request, "UPS-RS"); | ||
|
||
apiLogger.addTokenValue(); | ||
apiLogger.logger.info("Create workItem"); | ||
|
||
try { | ||
let workItemService = new CreateWorkItemService(this.request, this.response); | ||
let workItem = await workItemService.createUps(); | ||
apiLogger.logger.info(`Create workItem ${workItem.upsInstanceUID} successful`); | ||
return this.response.status(201).send(); | ||
} catch (e) { | ||
let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); | ||
apiLogger.logger.error(errorStr); | ||
|
||
if (e instanceof DicomWebServiceError) { | ||
return this.response.status(e.code).send({ | ||
status: e.status, | ||
message: e.message | ||
}); | ||
} | ||
|
||
this.response.writeHead(500, { | ||
"Content-Type": "application/dicom+json" | ||
}); | ||
this.response.end(JSON.stringify({ | ||
code: 500, | ||
message: "An Server Exception Occurred" | ||
})); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* | ||
* @param {import('express').Request} req | ||
* @param {import('express').Response} res | ||
*/ | ||
module.exports = async function (req, res) { | ||
let controller = new CreateWorkItemController(req, res); | ||
|
||
await controller.doPipeline(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
const { | ||
GetWorkItemService | ||
} = require("./service/get-workItem.service"); | ||
const { ApiLogger } = require("../../../../utils/logs/api-logger"); | ||
const { Controller } = require("../../../controller.class"); | ||
|
||
class GetWorkItemController extends Controller { | ||
constructor(req, res) { | ||
super(req, res); | ||
} | ||
|
||
async mainProcess() { | ||
let apiLogger = new ApiLogger(this.request, "UPS-RS"); | ||
|
||
apiLogger.addTokenValue(); | ||
apiLogger.logger.info(`Get workItem, query: ${this.queryToString()}`); | ||
|
||
try { | ||
let getWorkItemService = new GetWorkItemService(this.request, this.response); | ||
let workItems = await getWorkItemService.getUps(); | ||
return this.response.set("Content-Type", "application/dicom+json").status(200).json(workItems); | ||
} catch (e) { | ||
let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); | ||
apiLogger.logger.error(errorStr); | ||
|
||
this.response.writeHead(500, { | ||
"Content-Type": "application/dicom+json" | ||
}); | ||
this.response.end(JSON.stringify({ | ||
code: 500, | ||
message: errorStr | ||
})); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* | ||
* @param {import('express').Request} req | ||
* @param {import('express').Response} res | ||
*/ | ||
module.exports = async function (req, res) { | ||
let controller = new GetWorkItemController(req, res); | ||
|
||
await controller.doPipeline(); | ||
}; |
92 changes: 92 additions & 0 deletions
92
api/dicom-web/controller/UPS-RS/service/create-workItem.service.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
const _ = require("lodash"); | ||
const workItemModel = require("@models/mongodb/models/workItems"); | ||
const patientModel = require("@models/mongodb/models/patient"); | ||
const { UIDUtils } = require("@dcm4che/util/UIDUtils"); | ||
const { | ||
DicomWebServiceError, | ||
DicomWebStatusCodes | ||
} = require("@error/dicom-web-service"); | ||
const { DicomJsonModel } = require("@models/DICOM/dicom-json-model"); | ||
|
||
class CreateWorkItemService { | ||
constructor(req, res) { | ||
this.request = req; | ||
this.response = res; | ||
this.requestWorkItem = /** @type {Object[]} */(this.request.body).pop(); | ||
/** @type {DicomJsonModel} */ | ||
this.requestWorkItem = new DicomJsonModel(this.requestWorkItem); | ||
} | ||
|
||
async createUps() { | ||
let uid = _.get(this.request, "query.workitem", | ||
await UIDUtils.createUID() | ||
); | ||
_.set(this.requestWorkItem.dicomJson, "upsInstanceUID", uid); | ||
_.set(this.requestWorkItem.dicomJson, "00080018", { | ||
vr: "UI", | ||
Value: [ | ||
uid | ||
] | ||
}); | ||
let workListLabel = this.requestWorkItem.getString("00741202"); | ||
if (!workListLabel) { | ||
_.set(this.requestWorkItem, "00741202", { | ||
vr: "LO", | ||
Value: [ | ||
"RACCOON" | ||
] | ||
}); | ||
} | ||
|
||
if (this.requestWorkItem.getString("00741000") !== "SCHEDULED") { | ||
throw new DicomWebServiceError( | ||
DicomWebStatusCodes.UPSNotScheduled, | ||
`Refused: The provided value of UPS State was not "SCHEDULED"`, | ||
400 | ||
); | ||
} | ||
|
||
let patient = await this.findOneOrCreatePatient(); | ||
|
||
let workItem = new workItemModel(this.requestWorkItem.dicomJson); | ||
|
||
if (await this.isUpsExist(uid)) { | ||
throw new DicomWebServiceError( | ||
DicomWebStatusCodes.DuplicateSOPinstance, | ||
`SOP Instance UID that was already allocated to another SOP Instance`, | ||
400 | ||
); | ||
} | ||
await workItem.save(); | ||
|
||
|
||
//TODO: subscription | ||
return workItem; | ||
} | ||
|
||
async findOneOrCreatePatient() { | ||
let patientId = this.requestWorkItem.getString("00100020"); | ||
_.set(this.requestWorkItem.dicomJson, "patientID", patientId); | ||
|
||
/** @type {patientModel | null} */ | ||
let patient = await patientModel.findOne({ | ||
"00100020.Value": patientId | ||
}); | ||
|
||
if (!patient) { | ||
/** @type {patientModel} */ | ||
let patientObj = new patientModel(this.requestWorkItem.dicomJson); | ||
patient = await patientObj.save(); | ||
} | ||
|
||
return patient; | ||
} | ||
|
||
async isUpsExist(uid) { | ||
return await workItemModel.findOne({ | ||
upsInstanceUID: uid | ||
}); | ||
} | ||
} | ||
|
||
module.exports.CreateWorkItemService = CreateWorkItemService; |
58 changes: 58 additions & 0 deletions
58
api/dicom-web/controller/UPS-RS/service/get-workItem.service.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
const _ = require("lodash"); | ||
const workItemsModel = require("@models/mongodb/models/workItems"); | ||
const { | ||
convertAllQueryToDICOMTag, | ||
convertRequestQueryToMongoQuery | ||
} = require("../../QIDO-RS/service/QIDO-RS.service"); | ||
|
||
class GetWorkItemService { | ||
constructor(req, res) { | ||
this.request = req; | ||
this.response = res; | ||
this.query = {}; | ||
|
||
/** | ||
* @private | ||
*/ | ||
this.limit_ = parseInt(this.request.query.limit) || 100; | ||
delete this.request.query["limit"]; | ||
|
||
/** | ||
* @private | ||
*/ | ||
this.skip_ = parseInt(this.request.query.offset) || 0; | ||
delete this.request.query["offset"]; | ||
|
||
|
||
this.initQuery_(); | ||
} | ||
|
||
async getUps() { | ||
let mongoQuery = (await convertRequestQueryToMongoQuery(this.query)).$match; | ||
|
||
let queryOptions = { | ||
query: mongoQuery, | ||
skip: this.skip_, | ||
limit: this.limit_, | ||
requestParams: this.request.params | ||
}; | ||
|
||
let docs = await workItemsModel.getDicomJson(queryOptions); | ||
|
||
return docs; | ||
} | ||
|
||
initQuery_() { | ||
let query = _.cloneDeep(this.request.query); | ||
let queryKeys = Object.keys(query).sort(); | ||
for (let i = 0; i < queryKeys.length; i++) { | ||
let queryKey = queryKeys[i]; | ||
if (!query[queryKey]) delete query[queryKey]; | ||
} | ||
|
||
this.query = convertAllQueryToDICOMTag(query); | ||
} | ||
|
||
} | ||
|
||
module.exports.GetWorkItemService = GetWorkItemService; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/** | ||
* https://dicom.nema.org/medical/dicom/current/output/html/part18.html#sect_11 | ||
* https://dicom.nema.org/medical/dicom/2019a/output/chtml/part18/sect_6.9.html | ||
* @author Chin-Lin, Lee <[email protected]> | ||
*/ | ||
const express = require("express"); | ||
const Joi = require("joi"); | ||
const { validateParams, intArrayJoi, validateByJoi } = require("../validator"); | ||
const router = express(); | ||
const GLOBAL_SUBSCRIPTION_UIDS = [ | ||
"1.2.840.10008.5.1.4.34.5", | ||
"1.2.840.10008.5.1.4.34.5.1" | ||
]; | ||
|
||
//#region UPS-RS | ||
|
||
/** | ||
* @openapi | ||
* /dicom-web/workitems: | ||
* post: | ||
* tags: | ||
* - UPS-RS | ||
* description: > | ||
* This transaction creates a Workitem on the target Worklist. It corresponds to the UPS DIMSE N-CREATE operation. | ||
* See [Create Workitem Transaction](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#sect_11.4) | ||
* parameters: | ||
* - $ref: "#/components/parameters/workitem" | ||
* responses: | ||
* "201": | ||
* description: The workitem create successfully | ||
*/ | ||
router.post("/workitems", | ||
validateParams({ | ||
workitem: Joi.string().invalid(...GLOBAL_SUBSCRIPTION_UIDS).optional() | ||
}, "query", { | ||
allowUnknown: false | ||
}), | ||
validateByJoi(Joi.array().items(Joi.object()).min(1).max(1), "body"), | ||
require("./controller/UPS-RS/create-workItems") | ||
); | ||
|
||
|
||
/** | ||
* @openapi | ||
* /dicom-web/workitems: | ||
* get: | ||
* tags: | ||
* - UPS-RS | ||
* description: > | ||
* This transaction retrieves a Workitem. It corresponds to the UPS DIMSE N-GET operation. | ||
* See [Retrieve Workitem Transaction](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#sect_11.5) | ||
* responses: | ||
* "200": | ||
* description: Query successfully | ||
* content: | ||
* "application/dicom+json": | ||
* schema: | ||
* type: array | ||
*/ | ||
router.get("/workitems", | ||
require("./controller/UPS-RS/get-workItem") | ||
); | ||
|
||
//#endregion | ||
|
||
module.exports = router; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
components: | ||
parameters: | ||
"workitem": | ||
name: "workitem" | ||
description: workitem instance UID | ||
in: query | ||
schema: | ||
type: string |
Oops, something went wrong.