Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 26 additions & 25 deletions News
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,31 @@ GaelO
BugFix : dicomdir breaks dicom parsing

-- GaelO 1.1.0 29/04/20 --
Features :
- Visit group to manage multiple modalities (in which each modality will have it's visit type)
- User's data edition panel
- Docker :
- SMTP service for (mail() function)
- Cron services to automatize recurrent tasks
- Patient Visit Manager can be redifined through inheritance for custom visit creation workflow and status determination
- FTP/SFTP class to retrieve data from remote server (ex : automatic import of included patient list)
- Json Import for centers
- Possibility to declare patient code prefix per study at study creation (to avoid wrong patient import)
- Possibility to associate file upload with specific form

Enhancements :
- Tree filter in right click to filter visits according to some status (depending on current role)
- Anon profile, optional visit, local form needed, Qc needed, Review needed are now defined per visit type
- Monitors now recieve QC decision emails
- Supervisors are notified in case of a not done visit creation
- Email notification when visit review is awaiting adjudication or concluded
- Export button on DataTables now export only visible (filtered) data
- Replaced Jquery datepicker by Bootrastrp datepicker
- Simplified download interface for DICOM (supervisor)
- faster visit status determination in upload manager (supervisor)
- Add OHIF viewer for supervisors and investigators
- Reduced ZIP compression level of Dicom before upload to decrease CPU comsuption in the client
- Small memory usage reduction when zipping dicom

Features :
- Visit group to manage multiple modalities (in which each modality will have it's visit type)
- User's data edition panel
- Docker :
- SMTP service for (mail() function)
- Cron services to automatize recurrent tasks
- Patient Visit Manager can be redifined through inheritance for custom visit creation workflow and status determination
- FTP/SFTP class to retrieve data from remote server (ex : automatic import of included patient list)
- Json Import for centers
- Possibility to declare patient code prefix per study at study creation (to avoid wrong patient import)
- Possibility to associate file upload with specific form

Enhancements :
- Tree filter in right click to filter visits according to some status (depending on current role)
- Anon profile, optional visit, local form needed, Qc needed, Review needed are now defined per visit type
- Monitors now recieve QC decision emails
- Supervisors are notified in case of a not done visit creation
- Email notification when visit review is awaiting adjudication or concluded
- Export button on DataTables now export only visible (filtered) data
- Replaced Jquery datepicker by Bootrastrp datepicker
- Simplified download interface for DICOM (supervisor)
- faster visit status determination in upload manager (supervisor)
- Add OHIF viewer for supervisors and investigators
- Reduced ZIP compression level of Dicom before upload to decrease CPU comsuption in the client
- Small memory usage reduction when zipping dicom


83 changes: 37 additions & 46 deletions src/assets/javascripts/dicomupload/DicomFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,93 +129,84 @@ class DicomFile {
}
}

/**
* Get value of a dicom attribute
*/
get(id, dataSet = this.dataSet) {
id = id.toLowerCase();

// Recursively look for the dicom attribute
for (let elmtName in dataSet.elements) {
let elmt = dataSet.elements[elmtName];

// Check if this is a SQ element
if (elmt.items !== undefined) {
for (let it of elmt.items) {
// Get value of the dicom attribute in the dataset of the SQ
let got = this.get(id, it.dataSet);
if (got !== undefined) {
return got;
}
}
}

if (elmtName == `x${id}` && elmt.length > 0) {
// Return the value of the dicom attribute
return this.string(elmt);
}
getRadiopharmaceuticalTag(tagAddress){
try{
let elmt = this.dataSet.elements['x00540016']
let radioPharmElements = elmt.items[0].dataSet.elements
return this.string(radioPharmElements['x'+tagAddress])
}catch ( error ) {
console.log(error)
return undefined
}
}

getDicomTag(tagAddress){
let elmt = this.dataSet.elements['x'+tagAddress]
if ( elmt.length > 0) {
// Return the value of the dicom attribute
return this.string(elmt);
}
return undefined;
else return undefined
}

getAccessionNumber() {
return this.get("00080050");
return this.getDicomTag("00080050");
}
getAcquisitionDate() {
return this.get("00080020");
return this.getDicomTag("00080020");
}
getInstanceNumber() {
return this.get("00200013");
return this.getDicomTag("00200013");
}
getModality() {
return this.get("00080060");
return this.getDicomTag("00080060");
}
getPatientBirthDate() {
return this.get("00100030");
return this.getDicomTag("00100030");
}
getPatientID() {
return this.get("00100020");
return this.getDicomTag("00100020");
}
getPatientName() {
return this.get("00100010");
return this.getDicomTag("00100010");
}
getPatientSex() {
return this.get("00100040");
return this.getDicomTag("00100040");
}
getSeriesInstanceUID() {
return this.get("0020000E");
return this.getDicomTag("0020000e");
}
getSeriesDate() {
return this.get("00080021");
return this.getDicomTag("00080021");
}
getSeriesDescription() {
return this.get("0008103E");
return this.getDicomTag("0008103e");
}
getSOPInstanceUID() {
return this.get("00080018");
return this.getDicomTag("00080018");
}
getSOPClassUID() {
return this.get("00080016");
return this.getDicomTag("00080016");
}
//SK A TESTER : On ne pourrait utiliser que le 0002,0222
//SK A TESTER : On ne pourrait utiliser que le 0002,0002
//Ce tag est un duplicat de 00080016 cf https://stackoverflow.com/questions/32689446/is-it-true-that-dicom-media-storage-sop-instance-uid-sop-instance-uid-why
getMediaStorageSOP(){
return this.get("00020002")
return this.getDicomTag("00020002")
}
getSeriesNumber() {
return this.get("00200011");
return this.getDicomTag('00200011')
}
getStudyInstanceUID() {
return this.get("0020000D");
return this.getDicomTag('0020000d')
}
getStudyDate() {
return this.get("00080020");
return this.getDicomTag("00080020");
}
getStudyID() {
return this.get("00200010");
return this.getDicomTag("00200010");
}
getStudyDescription() {
return this.get("00081030");
return this.getDicomTag("00081030");
}

/**
Expand Down
34 changes: 8 additions & 26 deletions src/assets/javascripts/dicomupload/DicomUploadModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,6 @@ class DicomUploadModel {
// ~
}

// ~

getStudy(studyInstanceUID) {
for (let st of this.studies) {
if (st.studyInstanceUID == studyInstanceUID) {
Expand Down Expand Up @@ -93,8 +91,6 @@ class DicomUploadModel {
return null;
}

// ~

isKnownStudy(dicomFile) {
return this.getStudy(dicomFile.getStudyInstanceUID()) !== null;
}
Expand Down Expand Up @@ -141,8 +137,6 @@ class DicomUploadModel {
}
}

// ~

hasQueuedStudies() {
for (let st of this.queuedStudies) {
if (st.hasQueuedSeries()) {
Expand Down Expand Up @@ -184,8 +178,6 @@ class DicomUploadModel {
}
}

// ~

/**
* Register the study, serie, instance of a dicom file if not known yet
* @throws 'Secondary Capture Image Storage are not allowed.'
Expand Down Expand Up @@ -258,8 +250,6 @@ class DicomUploadModel {
}
}

// ~

checkStudies() {
for (let st of this.studies) {

Expand Down Expand Up @@ -299,8 +289,6 @@ class DicomUploadModel {
}
}

// ~

checkSeries(st) {
function isset(e) {
return !(e == 'null' || e === undefined || e == '');
Expand All @@ -314,29 +302,25 @@ class DicomUploadModel {
if (!isset(dicomFile.getModality())) {
sr.setWarning('missingTag00080060', 'Missing tag: Modality', true);
} else {
if (!isset(dicomFile.getStudyDate())) {
sr.setWarning('missingTag00080020', 'Missing tag: StudyDate', true);
if (!isset(dicomFile.getDicomTag('00080021')) && !isset(dicomFile.getDicomTag('00080022')) ) {
sr.setWarning('missingTag00080022', 'Missing tag: SeriesDate', true);
}
if (sr.modality == 'PT') {
if (!isset(dicomFile.get('00101030'))) {
if ( !isset(dicomFile.getDicomTag('00101030')) ) {
sr.setWarning('missingTag00101030', 'Missing tag: Patient Weight', true);
}
if (!isset(dicomFile.get('00080030'))) {
sr.setWarning('missingTag00101030', 'Missing tag: Modality', true);
if ( !isset(dicomFile.getDicomTag('00080031')) && !isset(dicomFile.getDicomTag('00080032')) ) {
sr.setWarning('missingTag00101031', 'Missing tag: Series Time', true);
}
if (!isset(dicomFile.get('00181074'))) {
if ( !isset(dicomFile.getRadiopharmaceuticalTag('00181074')) ) {
sr.setWarning('missingTag00181074', 'Missing tag: Radionuclide Total Dose', true);
}
if (!isset(dicomFile.get('00181072'))) {
if (!isset(dicomFile.getRadiopharmaceuticalTag('00181072')) && !isset(dicomFile.getRadiopharmaceuticalTag('00181078')) ) {
sr.setWarning('missingTag00181072', 'Missing tag: Radiopharmaceutical Start Time', true);
}
if (!isset(dicomFile.get('00181075'))) {
if ( !isset(dicomFile.getRadiopharmaceuticalTag('00181075')) ) {
sr.setWarning('missingTag00181075', 'Missing tag: Radionuclide Half Life', true);
}
/*
if (!isset(dicomFile.get('00181077'))) {
sr.setWarning('missingTag00181077', 'Missing tag: RadiopharmaceuticalSpecificActivity', true);
}*/
}
}

Expand All @@ -358,8 +342,6 @@ class DicomUploadModel {
}
}

// ~

findExpectedVisit(st) {
let thisP = st.getPatientName();

Expand Down
2 changes: 1 addition & 1 deletion src/includes/jsLibrairies.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@

<script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.1.0/papaparse.min.js"></script>

<?php $uploaderVersion="3.2"?>
<?php $uploaderVersion="3.4"?>
<!-- JavaScript CustomJS/CSS -->
<script type="text/javascript" src="assets/javascripts/dicomupload/_browserSupport.js?version=<?= $uploaderVersion?>" defer></script>
<script type="text/javascript" src="assets/javascripts/dicomupload/CheckPatientPanel.js?version=<?= $uploaderVersion?>" defer></script>
Expand Down
2 changes: 1 addition & 1 deletion src/includes/jsLibrairies_main.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@

<script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.1.0/papaparse.min.js"></script>

<?php $uploaderVersion="3.2"?>
<?php $uploaderVersion="3.4"?>
<!-- JavaScript CustomJS/CSS -->
<script type="text/javascript" src="assets/javascripts/dicomupload/_browserSupport.js?version=<?= $uploaderVersion?>" ></script>
<script type="text/javascript" src="assets/javascripts/dicomupload/CheckPatientPanel.js?version=<?= $uploaderVersion?>" ></script>
Expand Down
8 changes: 8 additions & 0 deletions src/models/Form_Processor_File.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ protected function getMaxSizeMb() : int{
return 5;
}

private function createEmptySpecificForm(){
$insertion = $this->linkpdo->prepare('INSERT INTO '.$this->specificTable.' (id_review) VALUES (:id_review)');
$insertion->execute(array(
'id_review' => $this->reviewObject->id_review )
);
}

/**
* Store or overwirte a file, each file is defined by a Key (visit specific)
*/
Expand All @@ -56,6 +63,7 @@ public function storeAssociatedFile(string $fileKey, string $mime, int $fileSize
//If first form upload create a draft form to insert file uploaded data
if (empty($this->reviewObject)) {
$this->createReview();
$this->createEmptySpecificForm();
}else {
//If review exist but validated throw exception
if ($this->reviewObject->validated) {
Expand Down