-
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.
Merge branch 'sql-refactoring' into dev
- Loading branch information
Showing
163 changed files
with
9,960 additions
and
483 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
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 |
---|---|---|
|
@@ -26,4 +26,6 @@ | |
config/ae-prod.properties | ||
|
||
# ignore jscpd output report | ||
/report | ||
/report | ||
|
||
/jsconfig.json |
Large diffs are not rendered by default.
Oops, something went wrong.
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 |
---|---|---|
@@ -1,29 +1,72 @@ | ||
# raccoon-only-dicom | ||
Another raccoon focus on dicom. The original raccoon combine the FHIR and DICOM together in MongoDB, I think it will cause performance and maintenance issues. So, here start a new project only for DICOM. | ||
# raccoon-dicom | ||
|
||
<p align="center"> | ||
<img src="https://user-images.githubusercontent.com/49154622/236814496-a87eb89f-9cbe-4898-a7bf-aa2b27d97596.svg" alt="logo" width="10%"/> | ||
</p> | ||
<br/> | ||
|
||
Another Raccoon focus on DICOM. | ||
|
||
[English](README.md) | [繁體中文](README.zh-TW.md) | ||
|
||
--- | ||
|
||
**Raccoon-DICOM** is a noSQL-based medical image archive designed for managing DICOM images, utilizing MongoDB to store and manage the images while providing RESTful APIs that support [DICOMweb](https://www.dicomstandard.org/dicomweb/") protocols for querying, retrieving, and managing DICOM images. | ||
|
||
# Installation | ||
- [Installation](https://github.com/Chinlinlee/raccoon-dicom/wiki/Installation) | ||
- Step by Step guide to installing Raccoon-DICOM - Windows (WIP🚧) | ||
- Step by Step guide to installing Raccoon-DICOM - Ubuntu (WIP🚧) | ||
|
||
# Troubleshooting on linux | ||
- `Unknown VR: Tag not found in data dictionary` when using `STOW-RS` | ||
- You need set the `DCMDICTPATH` environment variable | ||
- The `dicom.dic` can find in the `/usr/share/libdcmtk{version}` or `./models/DICOM/dcmtk/dicom.dic` | ||
> The {version} corresponds to dcmtk version, e.g. 3.6.5 => libdcmtk15 | ||
- Set `DCMDICTPATH` environment variable using command or you can add the command to profile file(`~/.bashrc`,`~/.profile` etc.), example **with dcmtk 3.6.5**: | ||
```sh | ||
export DCMDICTPATH=/usr/share/libdcmtk15/dicom.dic | ||
``` | ||
- Check the environment variable | ||
```sh | ||
echo $DCMDICTPATH | ||
``` | ||
|
||
# Features | ||
The features implemented here: | ||
- [QIDO-RS](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#sect_10.6) | ||
- [STOW-RS](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#sect_10.5) | ||
- Convert DICOM to ImagingStudy, Endpoint, Patient of FHIR resources and sync FHIR resources to FHIR server | ||
- [WADO-RS](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#sect_10.4.1.1.1) | ||
- [Retrieve Transaction Instance Resources](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#table_10.4.1-1) | ||
- [Retrieve Transaction Metadata Resources](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#table_10.4.1-2) | ||
- [Retrieve Transaction Rendered Resources](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#table_10.4.1-3) | ||
- [Retrieve Transaction Bulkdata Resources](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#table_10.4.1.5-1) | ||
|
||
# Environment Requirements | ||
- node.js >= 16 | ||
- Java JDK >= 11 | ||
|
||
> **Note** | ||
> - You should copy opencv_java library to JDK's lib directory | ||
> - In windows, copy `opencv_java.dll` | ||
> - In linux, copy `libclib_jiio.so` and `libopencv_java.so` | ||
## [QIDO-RS](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#sect_10.6) | ||
### Support Format (Media Types) | ||
|
||
Format | Support | | ||
---------|----------| | ||
application/dicom+json | ✅ | | ||
multipart/related; type="application/dicom+xml | ❌ | | ||
|
||
### Support Query Parameter | ||
|
||
Query Parameter | Support | | ||
---------|----------| | ||
fuzzymatching | ❌ | | ||
includefield | ✅ | | ||
limit | ✅ | | ||
offset | ✅ | | ||
|
||
|
||
## [STOW-RS](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#sect_10.5) | ||
- You can set `SYCN_TO_FHIR_SERVER=true` in .env to convert DICOM to ImagingStudy, Endpoint, Patient of FHIR resources and sync FHIR resources to FHIR server | ||
## [WADO-RS](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#sect_10.4.1.1.1) | ||
- [Retrieve Transaction Instance Resources](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#table_10.4.1-1) | ||
- [Retrieve Transaction Metadata Resources](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#table_10.4.1-2) | ||
- [Retrieve Transaction Rendered Resources](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#table_10.4.1-3) | ||
- [Retrieve Transaction Thumbnail Resources](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#table_10.4.1-4) | ||
- [Retrieve Transaction Bulkdata Resources](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#table_10.4.1.5-1) | ||
|
||
|
||
# API Documentation | ||
- raccoon-dicom uses swagger ui hosting openapi.json to generate documentation | ||
- [API Documentation](https://chinlinlee.github.io/raccoon-dicom/) | ||
|
||
|
||
# Wiki | ||
Our [wiki](https://github.com/Chinlinlee/raccoon-dicom/wiki) includes a lot of information about raccoon-dicom, we heavily encourage you to take a look!! | ||
|
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,72 @@ | ||
# raccoon-dicom | ||
|
||
<p align="center"> | ||
<img src="https://user-images.githubusercontent.com/49154622/236814496-a87eb89f-9cbe-4898-a7bf-aa2b27d97596.svg" alt="logo" width="10%"/> | ||
</p> | ||
<br/> | ||
|
||
Another Raccoon focus on DICOM. | ||
|
||
[English](README.md) | [繁體中文](README.zh-TW.md) | ||
|
||
--- | ||
|
||
**Raccoon-DICOM** 是使用 no-SQL 資料庫實作的醫學影像儲存系統(DICOMweb PACS),其使用 MongoDB 管理 DICOM 影像並提供 [DICOMweb](https://www.dicomstandard.org/dicomweb/") RESTful API 功能進行儲存、查詢以及調閱 | ||
|
||
|
||
# 安裝 | ||
- [安裝手冊](https://github.com/Chinlinlee/raccoon-dicom/wiki/Installation.zh-TW) | ||
- [從 0 開始部屬 Raccoon - Windows](https://github.com/Chinlinlee/raccoon-dicom/wiki/From-zero-to-deploy.zh-TW): 在 Windows 上,一步一步從安裝到部屬 | ||
- 從 0 開始部屬 Raccoon - Ubuntu (WIP🚧) | ||
|
||
# Troubleshooting on linux | ||
- `Unknown VR: Tag not found in data dictionary` when using `STOW-RS` | ||
- 您必須設定 `DCMDICTPATH` 環境變數 | ||
- `dicom.dic` 檔案可以在`/usr/share/libdcmtk{version}`或 `./models/DICOM/dcmtk/dicom.dic`找到 | ||
> {version} 對應到dcmtk的版本, e.g. 3.6.5 => libdcmtk15 | ||
- 使用指令設定 `DCMDICTPATH` 或者您可以將指令加入到profile檔案中(`~/.bashrc`,`~/.profile` etc.), example **with dcmtk 3.6.5**: | ||
```sh | ||
export DCMDICTPATH=/usr/share/libdcmtk15/dicom.dic | ||
``` | ||
- 檢查環境變數 | ||
```sh | ||
echo $DCMDICTPATH | ||
``` | ||
|
||
# 提供之功能 | ||
目前以實作的功能如下: | ||
## [QIDO-RS](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#sect_10.6) | ||
### 支援的格式 (Media Types) | ||
|
||
Format | Support | | ||
---------|----------| | ||
application/dicom+json | ✅ | | ||
multipart/related; type="application/dicom+xml | ❌ | | ||
|
||
### 支援的查詢參數 | ||
|
||
Query Parameter | Support | | ||
---------|----------| | ||
fuzzymatching | ❌ | | ||
includefield | ✅ | | ||
limit | ✅ | | ||
offset | ✅ | | ||
|
||
|
||
## [STOW-RS](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#sect_10.5) | ||
- 您可以在 .env 設定 `SYCN_TO_FHIR_SERVER=true` 以將 DICOM 轉換為 FHIR ImagingStudy, Endpoint 以及 Patient,並同步這些 Resources 至 FHIR server | ||
## [WADO-RS](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#sect_10.4.1.1.1) | ||
- [Retrieve Transaction Instance Resources](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#table_10.4.1-1) | ||
- [Retrieve Transaction Metadata Resources](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#table_10.4.1-2) | ||
- [Retrieve Transaction Rendered Resources](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#table_10.4.1-3) | ||
- [Retrieve Transaction Thumbnail Resources](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#table_10.4.1-4) | ||
- [Retrieve Transaction Bulkdata Resources](https://dicom.nema.org/medical/dicom/current/output/html/part18.html#table_10.4.1.5-1) | ||
|
||
|
||
# API Documentation | ||
- raccoon-dicom uses swagger ui hosting openapi.json to generate documentation | ||
- [API Documentation](https://chinlinlee.github.io/raccoon-dicom/) | ||
|
||
# Wiki | ||
我們的[Wiki](https://github.com/Chinlinlee/raccoon-dicom/wiki)含有更多與 Raccoon-DICOM 更多的資訊,非常建議您閱讀一下 |
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,99 @@ | ||
const fs = require("fs"); | ||
const _ = require("lodash"); | ||
const renderedService = 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 Magick = require("@models/magick"); | ||
const { NotFoundInstanceError, InvalidFrameNumberError, InstanceGoneError } = require("@error/dicom-instance"); | ||
const { WadoUriService } = require("@root/api/WADO-URI/service/WADO-URI.service"); | ||
const { InstanceModel } = require("@models/sql/models/instance.model"); | ||
const { ApiLogger } = require("@root/utils/logs/api-logger"); | ||
class SqlWadoUriService extends WadoUriService{ | ||
|
||
/** | ||
* | ||
* @param {import("http").IncomingMessage} req | ||
* @param {import("http").ServerResponse} res | ||
*/ | ||
constructor(req, res, apiLogger) { | ||
super(req, res); | ||
this.apiLogger = apiLogger; | ||
} | ||
|
||
async getDicomInstancePathObj() { | ||
let { | ||
studyUID, | ||
seriesUID, | ||
objectUID: instanceUID | ||
} = this.request.query; | ||
|
||
let imagePathObj = await InstanceModel.getPathOfInstance({ | ||
studyUID, | ||
seriesUID, | ||
instanceUID | ||
}); | ||
|
||
if (imagePathObj) { | ||
|
||
try { | ||
await fs.promises.access(imagePathObj.instancePath, fs.constants.F_OK); | ||
} catch(e) { | ||
console.error(e); | ||
throw new InstanceGoneError("The image is deleted permanently, but meta data remain"); | ||
} | ||
|
||
return imagePathObj; | ||
} | ||
|
||
throw new NotFoundInstanceError("Not found instance"); | ||
} | ||
|
||
async handleFrameNumberAndGetImageObj() { | ||
let imagePathObj = await this.getDicomInstancePathObj(); | ||
let instanceFramesObj = await renderedService.getInstanceFrameObj(imagePathObj); | ||
let instanceTotalFrameNumber = _.get(instanceFramesObj, "x00280008") ? _.get(instanceFramesObj, "x00280008") : 1; | ||
|
||
let windowCenter = _.get(instanceFramesObj, "x00281050.0", ""); | ||
let windowWidth = _.get(instanceFramesObj, "x00281051.0", ""); | ||
|
||
let transferSyntax = _.get(instanceFramesObj, "x00020010"); | ||
let frameNumber = parseInt(_.get(this.request.query, "frameNumber", 1)); | ||
|
||
if (frameNumber > instanceTotalFrameNumber) { | ||
throw new InvalidFrameNumberError(`Invalid Frame Number, total ${instanceTotalFrameNumber}, but requested ${frameNumber}`); | ||
} | ||
|
||
/** @type {Dcm2JpgExecutor$Dcm2JpgOptions} */ | ||
let options = await Dcm2JpgExecutor$Dcm2JpgOptions.newInstanceAsync(); | ||
options.frameNumber = frameNumber; | ||
|
||
if (windowCenter && windowWidth) { | ||
options.windowCenter = windowCenter; | ||
options.windowWidth = windowWidth; | ||
} | ||
|
||
let dicomFilename = instanceFramesObj.instancePath; | ||
let jpegFile = dicomFilename.replace(/\.dcm\b/gi , `.${frameNumber-1}.jpg`); | ||
|
||
let getFrameImageStatus = await Dcm2JpgExecutor.convertDcmToJpgFromFilename( | ||
dicomFilename, | ||
jpegFile, | ||
options | ||
); | ||
|
||
if (getFrameImageStatus.status) { | ||
|
||
return { | ||
imageSharp: sharp(jpegFile), | ||
magick: new Magick(jpegFile) | ||
}; | ||
} | ||
|
||
throw new NotFoundInstanceError("Not found DICOM Instance's Jpeg, may convert error"); | ||
} | ||
|
||
} | ||
|
||
module.exports.WadoUriService = SqlWadoUriService; | ||
module.exports.NotFoundInstanceError = NotFoundInstanceError; |
52 changes: 52 additions & 0 deletions
52
api-sql/dicom-web/controller/MWL-RS/service/change-filtered-mwlItem-status.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,52 @@ | ||
const { MwlItemModel } = require("@models/sql/models/mwlitems.model"); | ||
const { ChangeFilteredMwlItemStatusService } = require("@root/api/dicom-web/controller/MWL-RS/service/change-filtered-mwlItem-status"); | ||
const { cloneDeep, set } = require("lodash"); | ||
const { convertAllQueryToDicomTag } = require("@root/api/dicom-web/service/base-query.service"); | ||
const { MwlQueryBuilder } = require("./query/mwlQueryBuilder"); | ||
const { DicomWebServiceError, DicomWebStatusCodes } = require("@error/dicom-web-service"); | ||
const { dictionary } = require("@models/DICOM/dicom-tags-dic"); | ||
|
||
class SqlChangeFilteredMwlItemStatusService extends ChangeFilteredMwlItemStatusService { | ||
constructor(req, res) { | ||
super(req, res); | ||
} | ||
|
||
async changeMwlItemsStatus() { | ||
let { status } = this.request.params; | ||
let mwlItems = await this.getMwlItems(); | ||
|
||
if (mwlItems.length === 0) { | ||
throw new DicomWebServiceError(DicomWebStatusCodes.NoSuchObjectInstance, "Can not found any MWL item from query", 404); | ||
} | ||
|
||
for (let mwlItem of mwlItems) { | ||
mwlItem.status = status; | ||
set(mwlItem.json, `${dictionary.keyword.ScheduledProcedureStepSequence}.Value.0.${dictionary.keyword.ScheduledProcedureStepStatus}.Value.0`, status); | ||
mwlItem.changed("json", true); | ||
await mwlItem.save(); | ||
} | ||
|
||
return mwlItems.length; | ||
} | ||
|
||
async getMwlItems() { | ||
let queryBuilder = new MwlQueryBuilder({ | ||
query: this.query | ||
}); | ||
let q = queryBuilder.build(); | ||
return await MwlItemModel.findAll(q); | ||
} | ||
|
||
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, false); | ||
} | ||
} | ||
|
||
module.exports.ChangeFilteredMwlItemStatusService = SqlChangeFilteredMwlItemStatusService; |
44 changes: 44 additions & 0 deletions
44
api-sql/dicom-web/controller/MWL-RS/service/change-mwlItem-status.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,44 @@ | ||
const _ = require("lodash"); | ||
const { DicomWebServiceError, DicomWebStatusCodes } = require("@error/dicom-web-service"); | ||
const { dictionary } = require("@models/DICOM/dicom-tags-dic"); | ||
const { ChangeMwlItemStatusService } = require("@root/api/dicom-web/controller/MWL-RS/service/change-mwlItem-status"); | ||
const { MwlItemModel } = require("@models/sql/models/mwlitems.model"); | ||
const { Op } = require("sequelize"); | ||
|
||
class SqlChangeMwlItemStatusService extends ChangeMwlItemStatusService { | ||
constructor(req, res) { | ||
super(req, res); | ||
} | ||
|
||
async changeMwlItemsStatus() { | ||
let { status } = this.request.params; | ||
let mwlItem = await this.getMwlItemByStudyUIDAndSpsID(); | ||
if (!mwlItem) { | ||
throw new DicomWebServiceError(DicomWebStatusCodes.NoSuchObjectInstance, "No such object instance", 404); | ||
} | ||
|
||
mwlItem.sps_status = status; | ||
_.set(mwlItem.json, `${dictionary.keyword.ScheduledProcedureStepSequence}.Value.0.${dictionary.keyword.ScheduledProcedureStepStatus}.Value.0`, status); | ||
mwlItem.changed("json", true); | ||
await mwlItem.save(); | ||
|
||
return mwlItem.json; | ||
} | ||
|
||
async getMwlItemByStudyUIDAndSpsID() { | ||
return await MwlItemModel.findOne({ | ||
where: { | ||
[Op.and]: [ | ||
{ | ||
sps_id: this.request.params.spsID | ||
}, | ||
{ | ||
study_instance_uid: this.request.params.studyUID | ||
} | ||
] | ||
} | ||
}); | ||
} | ||
} | ||
|
||
module.exports.ChangeMwlItemStatusService = SqlChangeMwlItemStatusService; |
Oops, something went wrong.