@@ -36,6 +36,10 @@
require_once(joinPaths(CHRIS_CONTROLLER_FOLDER,'mapper.class.php'));
// include chris data models
require_once (joinPaths(CHRIS_MODEL_FOLDER, 'data.model.php'));
// include chris data models
require_once (joinPaths(CHRIS_MODEL_FOLDER, 'study.model.php'));
// include chris data models
require_once (joinPaths(CHRIS_MODEL_FOLDER, 'data_study.model.php'));
// include chris patient models
require_once (joinPaths(CHRIS_MODEL_FOLDER, 'patient.model.php'));
// include chris user_data models
@@ -53,6 +57,9 @@
// open log file
$logFile = '';

// keep track of dataset received
$received = Array();

// move all files from this directory to centralized data
// 1 file of 1 serie at once
if ($handle = opendir($study_directory)) {
@@ -70,7 +77,7 @@
//get patient id
$logFile .= 'getting patient id...'.PHP_EOL;
$logFile .= 'PatientID exists...? -> '.array_key_exists('PatientID',$process_file).PHP_EOL;

$patient_chris_id = -1;
//$db, $process_file, $patient_chris_id
$p_success = PACS::AddPatient($db, $process_file, $patient_chris_id);
@@ -92,9 +99,27 @@
return;
}

// keep track of processed series
if(!in_array($data_chris_id, $received)){
array_push($received, $data_chris_id);
}

$logFile .= 'data success: '.$d_success.PHP_EOL;
$logFile .= 'data id: '.$data_chris_id.PHP_EOL;

// if doesnt exist, add data
$study_chris_id = -1;
$study_description = '';
$s_success = PACS::AddStudy($db, $process_file, $study_chris_id, $study_description);
if($s_success == 0){
echo $logFile;
return;
}

$logFile .= 'study success: '.$d_success.PHP_EOL;
$logFile .= 'study id: '.$study_chris_id.PHP_EOL;
$logFile .= 'study description: '.$study_description.PHP_EOL;

// MAP PATIENT TO DATA
$dataPatientMapper = new Mapper('Data_Patient');
$dataPatientMapper->filter('patient_id = (?)',$patient_chris_id);
@@ -114,6 +139,25 @@
$logFile .= 'Patient already mapped to data...'.PHP_EOL;
$logFile .= 'patient data id: '.$dataPatientResult['Data_Patient'][0]->id.PHP_EOL;
}

// MAP DATA TO STUDY
$dataStudyMapper = new Mapper('Data_Study');
$dataStudyMapper->filter('data_id = (?)',$data_chris_id);
$dataStudyMapper->filter('study_id = (?)',$study_chris_id);
$dataStudyResult = $dataStudyMapper->get();
if(count($dataStudyResult['Data_Study']) == 0)
{
$dataStudyObject = new Data_Study();
$dataStudyObject->data_id = $data_chris_id;
$dataStudyObject->study_id = $study_chris_id;
$mapping_data_study_id = Mapper::add($dataStudyObject);

$logFile .= 'data study id: '.$mapping_data_study_id.PHP_EOL;
}
else{
$logFile .= 'Study already mapped to data...'.PHP_EOL;
$logFile .= 'data study id: '.$dataStudyResult['Data_Study'][0]->id.PHP_EOL;
}

// FILESYSTEM Processing
//
@@ -130,6 +174,18 @@
$logFile .= $patientdirname.' already exists'.PHP_EOL;
}

//
// Create the study directory
//
$patientdirname .= '/'.$study_description.'-'.$study_chris_id;
if(!is_dir($patientdirname)){
mkdir($patientdirname);
$logFile .= 'MKDIR: '.$patientdirname.PHP_EOL;
}
else{
$logFile .= $patientdirname.' already exists'.PHP_EOL;
}

//
// Create the data directory
//
@@ -145,42 +201,35 @@
}

// move file at good location
// CHRIS_DATA/MRN-UID/SERIESDESC-UID/index.dcm
// CHRIS_DATA/MRN-UID/STUDYDESC-UID/SERIESDESC-UID/index.dcm
// cp file over if doesnt exist
// it happens than some dicom file have more than 1 instance number
// it appears to be 0 and the real instance number
//$intanceNumber = max($process_file['InstanceNumber']);
// different naming based on
// different naming based on
$intanceNumber = $process_file['SOPInstanceUID'][0];
$filename = $datadirname .'/'.$intanceNumber.'.dcm';
if(!is_file($filename)){
copy($study_directory.'/'.$entry.'/'.$sub_entry, $filename);
$logFile .= 'COPY: '.$filename.PHP_EOL;
// if file doesnt exist, +1 status
// +1 increase data status
//to do in pacs class (less sql queries)
$dataMapper = new Mapper('Data');
$dataMapper->filter('id = (?)',$data_chris_id);
$dataResult = $dataMapper->get();

$dataObject = new Data();
$dataObject->uid = $dataResult['Data'][0]->uid;
$dataObject->name = $dataResult['Data'][0]->name;
$dataObject->nb_files = $dataResult['Data'][0]->nb_files;
$dataObject->plugin = $dataResult['Data'][0]->plugin;
//
// update time if time == '0000-00-00 00:00:00'
//
if($dataResult['Data'][0]->time == '0000-00-00 00:00:00'){
$dataObject->time = PACS::getTime($process_file);
$dataResult['Data'][0]->time = PACS::getTime($process_file);
}
else{
$dataObject->time = $dataResult['Data'][0]->time;
}


// update status
$dataObject->status = $dataResult['Data'][0]->status +1;
$dataResult['Data'][0]->status += 1;
// Update database and get object
Mapper::update($dataObject, $data_chris_id);
Mapper::update($dataResult['Data'][0], $data_chris_id);

$logFile .= '+1 STATUS: '.$filename.PHP_EOL;
}
@@ -190,21 +239,42 @@

// delete file
$logFile .= 'delete: '.$study_directory.'/'.$entry.'/'.$sub_entry.PHP_EOL;
unlink($study_directory.'/'.$entry.'/'.$sub_entry);
//unlink($study_directory.'/'.$entry.'/'.$sub_entry);
}
}
closedir($sub_handle);
// delete directory
$logFile .= 'delete: '.$study_directory.'/'.$entry.PHP_EOL;
rmdir($study_directory.'/'.$entry);
//rmdir($study_directory.'/'.$entry);
}
}
}
}
closedir($handle);
// delete directory
$logFile .= 'delete: '.$study_directory.PHP_EOL;
rmdir($study_directory);
//rmdir($study_directory);
}

// add warning if we didn't receive all the expected files
// todo: all queries at once
foreach($received as $key => $value){
$dataMapper = new Mapper('Data');
$dataMapper->filter('id = (?)',$value);
$dataResult = $dataMapper->get();

if($dataResult['Data'][0]->status != $dataResult['Data'][0]->nb_files){
$logFile .= 'WARNING => DATA ID : '.$dataResult['Data'][0]->id.'('.$dataResult['Data'][0]->status.'/'.$dataResult['Data'][0]->nb_files.')'.$entry.PHP_EOL;
// update db to unlock plugin
if($dataResult['Data'][0]->nb_files == -1){
$dataResult['Data'][0]->nb_files = $dataResult['Data'][0]->status;
}
else{
$dataResult['Data'][0]->status = min($dataResult['Data'][0]->status, $dataResult['Data'][0]->nb_files);
$dataResult['Data'][0]->nb_files = $dataResult['Data'][0]->status;
}
Mapper::update($dataResult['Data'][0], $dataResult['Data'][0]->id);
}
}

echo $logFile;
@@ -101,7 +101,7 @@ class PACS implements PACSInterface {
* @var string $movescu
*/
private $movescu = null;

/**
* DCMTK EchoSCU binary location.
*
@@ -137,11 +137,11 @@ public function __construct($server_ip, $server_port, $user_aet = null) {
* @snippet test.pacs.class.php testPing()
*/
public function ping($timeout = 5){
$command = $this->echoscu.' -to '.$timeout.' ';
$command = $this->echoscu.' -to '.$timeout.' ';

$this->_finishCommand($command);
// execute the command, format it into a nice json and return it
return $this->_executeAndFormat($command);
$this->_finishCommand($command);
// execute the command, format it into a nice json and return it
return $this->_executeAndFormat($command);
}

/**
@@ -561,7 +561,7 @@ public function moveStudy(){

// execute query
$output[] = $query;

shell_exec($query);
}
return $output;
@@ -584,7 +584,7 @@ public function moveSeries(){
{
$command = $this->movescu.' -S';
$command .= ' --aetitle '.$this->user_aet;
$command .= ' --move '.$this->user_aet;
$command .= ' --move FNNDSC-CHRISDEV';
$command .= ' -k QueryRetrieveLevel=SERIES';
$command .= ' -k StudyInstanceUID='.$this->command_param['StudyInstanceUID'];
$command .= ' -k SeriesInstanceUID='.$this->command_param['SeriesInstanceUID'];
@@ -611,6 +611,13 @@ static public function process($filename){
$requiered_fields .= ' +P InstanceNumber';
$requiered_fields .= ' +P SeriesDescription';

// Study Information
$requiered_fields .= ' +P Modality';
$requiered_fields .= ' +P StudyDescription';
$requiered_fields .= ' +P StudyDate';
$requiered_fields .= ' +P StationName';
$requiered_fields .= ' +P PatientAge';

// Patient information
$requiered_fields .= ' +P PatientName';
$requiered_fields .= ' +P PatientBirthDate';
@@ -794,39 +801,53 @@ static public function AddData(&$db, &$process_file, &$data_chris_id, &$series_d
//
$dataObject->uid = $process_file['SeriesInstanceUID'][0];
//
// get data name (series description)
// get data name (protocol name)
//
if(array_key_exists('ProtocolName',$process_file))
{
$dataObject->name = sanitize($process_file['ProtocolName'][0]);
}
else{
$dataObject->name = 'NoProtocolName';
}
//
// get data description (series description)
//
if(array_key_exists('SeriesDescription',$process_file))
{
$dataObject->name = sanitize($process_file['SeriesDescription'][0]);
$dataObject->description = sanitize($process_file['SeriesDescription'][0]);
}
else{
$dataObject->name = 'NoSeriesDescription';
$dataObject->description = 'NoSeriesDescription';
}

$series_description = $dataObject->name;
//
// get data time ContentDate-ContentTime
//
// date
$dataObject->time = PACS::getTime($process_file);
// get nb of files in data - only accessible through
// findscu query
/* $pacs = new PACS(PACS_SERVER, PACS_PORT, CHRIS_AETITLE);
$pacs->addParameter('RetrieveAETitle', '');
$pacs->addParameter('StudyInstanceUID', $result['StudyInstanceUID'][0]);
$pacs->addParameter('SeriesInstanceUID', $result['SeriesInstanceUID'][0]);
$pacs->addParameter('NumberOfSeriesRelatedInstances', '');
$all_results = $pacs->querySeries();
$dataObject->nb_files = $all_results['NumberOfSeriesRelatedInstances'][0]; */
//
// add the data model to db and get its id
//
$data_chris_id = Mapper::add($dataObject);
}
else{
$data_chris_id = $dataResult['Data'][0]->id;
$series_description = $dataResult['Data'][0]->name;
// todo: update time and status here...!
if($dataResult['Data'][0]->name == ''){
if(array_key_exists('ProtocolName',$process_file))
{
$dataResult['Data'][0]->name = sanitize($process_file['ProtocolName'][0]);
}
else{
$dataResult['Data'][0]->name = 'NoProtocolName';
}
$series_description = $dataResult['Data'][0]->name;
Mapper::update($dataResult['Data'][0], $dataResult['Data'][0]->id);
}
else{
$series_description = $dataResult['Data'][0]->name;
}
$data_chris_id = $dataResult['Data'][0]->id;
}
}
else {
@@ -840,6 +861,107 @@ static public function AddData(&$db, &$process_file, &$data_chris_id, &$series_d
return 1;
}

static public function AddStudy(&$db, &$process_file, &$study_chris_id, &$study_description){
$db->lock('study', 'WRITE');
// Does data exist: SeriesInstanceUID
if (array_key_exists('StudyInstanceUID',$process_file))
{
// does study exist??
$studyMapper = new Mapper('Study');
$studyMapper->filter('uid = (?)',$process_file['StudyInstanceUID'][0] );
$studyResult = $studyMapper->get();

// if study doesn't exist, create it
if(count($studyResult['Study']) == 0)
{
// create object
// create data model
$studyObject = new Study();
//
// get data uid
//
$studyObject->uid = $process_file['StudyInstanceUID'][0];
//
// get data name (series description)
//
if(array_key_exists('StudyDescription',$process_file))
{
$studyObject->description = sanitize($process_file['StudyDescription'][0]);
}
else{
$studyObject->name = 'NoStudyDescription';
}

if(array_key_exists('Modality',$process_file))
{
$studyObject->modality = sanitize($process_file['Modality'][0]);
}
else{
$studyObject->modality = 'NoModality';
}

if(array_key_exists('StudyDate',$process_file))
{
$studyObject->date = PACS::getDate($process_file);
}

$study_description = $studyObject->date.'-'.$studyObject->description;

$studyObject->age = PACS::getAge($process_file);
$studyObject->location = PACS::getLocation($process_file);

$study_chris_id = Mapper::add($studyObject);
}
else{
// Content to be updated
if($studyResult['Study'][0]->age == ''){
//
// get data name (series description)
//
if(array_key_exists('StudyDescription',$process_file))
{
$studyResult['Study'][0]->description = sanitize($process_file['StudyDescription'][0]);
}
else{
$studyResult['Study'][0]->name = 'NoStudyDescription';
}

if(array_key_exists('Modality',$process_file))
{
$studyResult['Study'][0]->modality = sanitize($process_file['Modality'][0]);
}
else{
$studyResult['Study'][0]->modality = 'NoModality';
}

$studyResult['Study'][0]->date = PACS::getDate($process_file);

$study_description = $studyResult['Study'][0]->date.'-'.$studyResult['Study'][0]->description;
$study_chris_id = $studyResult['Study'][0]->id;

$studyResult['Study'][0]->age = PACS::getAge($process_file);
$studyResult['Study'][0]->location = PACS::getLocation($process_file);

Mapper::update($studyResult['Study'][0], $studyResult['Study'][0]->id);
}
// Content already up to date
else{
$study_chris_id = $studyResult['Study'][0]->id;
$study_description = $studyResult['Study'][0]->date.'-'.$studyResult['Study'][0]->description;
}
}
}
else {
echo 'Study UID not provided in DICOM file'.PHP_EOL;
// finish data table lock
$db->unlock();
return 0;
}
// finish data table lock
$db->unlock();
return 1;
}

static public function getTime($process_file){
$date = '';
if(array_key_exists('ContentDate',$process_file))
@@ -863,5 +985,46 @@ static public function getTime($process_file){

return $date.' '. $time;
}

static public function getDate($process_file){
$date = '';
if(array_key_exists('StudyDate',$process_file))
{
$raw_date = $process_file['StudyDate'][0];
$date .= substr($raw_date, 0, 4).'-'.substr($raw_date, 4, 2).'-'.substr($raw_date, 6, 2);
}
else{
$date .= '0000-00-00';
}

return $date;
}

static public function getAge($process_file){
// should we use PatientAge tag???
$age = '0';
if(array_key_exists('ContentDate',$process_file) && array_key_exists('PatientBirthDate',$process_file))
{
$raw_bdate = $process_file['PatientBirthDate'][0];
$bdate = new DateTime(substr($raw_bdate, 0, 4).'-'.substr($raw_bdate, 4, 2).'-'.substr($raw_bdate, 6, 2));

$raw_cdate = $process_file['ContentDate'][0];
$cdate = new DateTime(substr($raw_cdate, 0, 4).'-'.substr($raw_cdate, 4, 2).'-'.substr($raw_cdate, 6, 2));

$raw_age = $bdate->diff($cdate);
$age = $raw_age->days;
}

return $age;
}

static public function getLocation($process_file){
$location = 'unknown';
if(array_key_exists('StationName',$process_file))
{
$location = $process_file['StationName'][0];
}
return $location;
}
}
?>
?>
@@ -31,77 +31,34 @@ class PACS_Pull(Plugin):
Plugin.DESCRIPTION = 'Pull data from your PACS to our ChRIS server.'
Plugin.DOCUMENTATION = 'http://wiki'
Plugin.LICENSE = 'Opensource (MIT)'
Plugin.VERSION = '0.1'
Plugin.VERSION = '0.2'

def run(self, options):
current_dir = os.path.dirname(os.path.abspath(__file__))

print Plugin.TITLE
print Plugin.VERSION

# preprocess!!
print '======================================='
print d.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + ' ---> Start pre_processing...'
pre_command = current_dir + "/pre_process.php -u " + options.USER_ID + " -f " + options.FEED_ID + " -m " + options.MRN + " -s " + options.SERVER_IP + " -p " + options.SERVER_PORT + " -a " + options.USER_AET + " -o " + options.output
print 'command:'
print pre_command
pre_output = os.system(pre_command)
print 'output:'
print pre_output

# evaluate pre_processing step
if pre_output == 0:
print d.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + ' ---> Pre_processing: SUCCESS'
print '(see pre_process.log for more information)'
else:
print d.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + ' ---> Pre_processing: FAILURE'
print 'EXIT pacs_pull plugin NOW'
print '(see pre_process.log for more information)'
return

# should return 0, if not, an issue occured, do not go to the next step

# process
# process!!
print '======================================='
print d.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + ' ---> Start processing...'
pro_command = current_dir + "/process.php -m " + options.MRN + " -f " + options.FEED_ID + " -s " + options.SERVER_IP + " -p " + options.SERVER_PORT + " -a " + options.USER_AET + " -o " + options.output
command = current_dir + "/process.php -u " + options.USER_ID + " -f " + options.FEED_ID + " -m " + options.MRN + " -s " + options.SERVER_IP + " -p " + options.SERVER_PORT + " -a " + options.USER_AET + " -o " + options.output
print 'command:'
print pro_command
pro_output = os.system(pro_command)
print command
output = os.system(command)
print 'output:'
print pro_output
print output

# evaluate processing step
if pro_output == 0:
if output == 0:
print d.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + ' ---> Processing: SUCCESS'
print '(see process.log for more information)'
else:
print d.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + ' ---> Processing: FAILURE'
print 'EXIT pacs_pull plugin NOW'
print '(see process.log for more information)'
return

# postprocess
print '======================================='
print d.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + ' ---> Start post_processing...'
post_command = current_dir + "/post_process.php -f " + options.FEED_ID + " -o " + options.output
print 'command:'
print post_command
post_output = os.system(post_command)
print 'output:'
print post_output

# evaluate post_processing step
if post_output == 0:
print d.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + ' ---> Post_processing: SUCCESS'
print '(see post_process.log for more information)'
else:
print d.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + ' ---> Post_processing: FAILURE'
print 'EXIT pacs_pull plugin NOW'
print '(see post_process.log for more information)'
return



# ENTRYPOINT
if __name__ == "__main__":
plugin = PACS_Pull()

This file was deleted.

This file was deleted.

Large diffs are not rendered by default.

File renamed without changes.