diff --git a/app/Http/Controllers/System/SyncOrthancController.php b/app/Http/Controllers/System/SyncOrthancController.php index 0ad1aaf..64b1f5d 100644 --- a/app/Http/Controllers/System/SyncOrthancController.php +++ b/app/Http/Controllers/System/SyncOrthancController.php @@ -3,16 +3,13 @@ namespace App\Http\Controllers\System; use App\Http\Controllers\Controller; -use App\Services\Pacs\StudyImporter; +use App\Services\Pacs\Sync\StudiesSync; class SyncOrthancController extends Controller { public function __invoke() { - $svc = (new StudyImporter); - $svc->scanStudies(); - $svc->filterStudies(); - $svc->importStudies(); + (new StudiesSync)->execute(); return redirect()->route('studies.index'); } diff --git a/app/Services/Pacs/StudyImporter.php b/app/Services/Pacs/StudyImporter.php deleted file mode 100644 index 76b5f4f..0000000 --- a/app/Services/Pacs/StudyImporter.php +++ /dev/null @@ -1,237 +0,0 @@ -client = $client ?? new OrthancRestClient; - } - - public function scanStudies() - { - $this->study_ids = $this->client->getStudiesIds(); - } - - private function checkUpdate(string $orthanc_uuid): void - { - $row = DB::table('studies')->where('orthanc_uuid', $orthanc_uuid)->first(['id', 'study_status']); - - if ($row === null) { - $this->insert_queue[] = $orthanc_uuid; - - return; - } - - if ($row->study_status < StudyLevelStatus::StudyArrived->value) { - $this->update_queue[$orthanc_uuid] = $row->id; - } - } - - public function filterStudies() - { - $this->insert_queue = []; - $this->update_queue = []; - foreach ($this->study_ids as $study_id) { - $this->checkUpdate($study_id); - } - } - - private function fetchStudyDetails(string $orthanc_uid): ?array - { - $study = $this->client->getStudyDetails($orthanc_uid); - if ($study == null) { - return null; - } - - $stats = $this->client->getStudyStatistics($orthanc_uid); - $study['Statistics'] = $stats; - - $series = $this->client->getStudySeries($orthanc_uid); - $study['Series'] = $series; - - return $study; - } - - public function importStudies() - { - foreach ($this->update_queue as $orthanc_uuid => $row_id) { - $study = $this->fetchStudyDetails($orthanc_uuid); - if ($study == null) { - continue; - } - $this->updateStudy($row_id, $study); - } - - foreach ($this->insert_queue as $orthanc_uuid) { - $study = $this->fetchStudyDetails($orthanc_uuid); - if ($study == null) { - continue; - } - $this->insertStudy($study); - } - } - - private function prepareData(mixed $orthanc_src): array - { - $inst_name = data_get($orthanc_src, 'MainDicomTags.InstitutionName'); - $inst_id = InstituteMapper::map($inst_name); - - $study = [ - 'orthanc_uuid' => strtolower($orthanc_src['ID']), - 'is_locked' => false, - 'is_active' => true, - - 'institution_name' => $inst_name, - 'institute_id' => $inst_id, - - 'patient_uuid' => strtolower($orthanc_src['ParentPatient']), - 'patient_id' => data_get($orthanc_src, 'PatientMainDicomTags.PatientID'), - 'patient_name' => data_get($orthanc_src, 'PatientMainDicomTags.PatientName'), - 'patient_sex' => data_get($orthanc_src, 'PatientMainDicomTags.PatientSex'), - - 'accession_number' => data_get($orthanc_src, 'MainDicomTags.AccessionNumber'), - 'referring_physician_name' => data_get($orthanc_src, 'MainDicomTags.ReferringPhysicianName'), - 'study_id' => data_get($orthanc_src, 'MainDicomTags.StudyID'), - 'study_instance_uid' => data_get($orthanc_src, 'MainDicomTags.StudyInstanceUID'), - 'study_modality' => data_get($orthanc_src, 'RequestedTags.Modality'), - 'body_part_examined' => data_get($orthanc_src, 'RequestedTags.BodyPartExamined'), - - 'study_date' => DicomUtils::dateTimeToCarbon($orthanc_src['MainDicomTags']['StudyDate'], $orthanc_src['MainDicomTags']['StudyTime']), - 'received_at' => Carbon::parse($orthanc_src['LastUpdate'], 'UTC'), - - 'image_count' => data_get($orthanc_src, 'Statistics.CountInstances'), - 'series_count' => data_get($orthanc_src, 'Statistics.CountSeries'), - 'disk_size' => data_get($orthanc_src, 'Statistics.DiskSize'), - ]; - - if ((bool) data_get($orthanc_src, 'IsStable', false)) { - $study['study_status'] = StudyLevelStatus::StudyArrived->value; - } else { - $study['study_status'] = StudyLevelStatus::Pending->value; - } - - $dob = data_get($orthanc_src, 'PatientMainDicomTags.PatientBirthDate'); - if (filled($dob)) { - try { - $study['patient_birthdate'] = Carbon::parse($dob); - } catch (\Exception) { - Log::error('Failed to parse PatientMainDicomTags.PatientBirthDate: {dob}', ['dob' => $dob]); - } - } - - $descr = data_get($orthanc_src, 'MainDicomTags.StudyDescription'); - if (blank($descr)) { - $descr = data_get($orthanc_src, 'RequestedTags.AcquisitionDeviceProcessingDescription'); - } - if (blank($descr)) { - $descr = data_get($orthanc_src, 'MainDicomTags.AcquisitionDeviceProcessingDescription'); - } - $this->setValue($study, 'study_description', trim($descr)); - - $properties = [ - 'other_patient_names' => data_get($orthanc_src, 'RequestedTags.OtherPatientNames'), - 'other_patient_ids' => data_get($orthanc_src, 'RequestedTags.OtherPatientIDs'), - 'software_versions' => data_get($orthanc_src, 'RequestedTags.SoftwareVersions'), - 'station_name' => data_get($orthanc_src, 'RequestedTags.StationName'), - 'operators_name' => data_get($orthanc_src, 'RequestedTags.OperatorsName'), - 'manufacturer' => data_get($orthanc_src, 'RequestedTags.Manufacturer'), - 'manufacturer_model_name' => data_get($orthanc_src, 'RequestedTags.ManufacturerModelName'), - 'acquisition_date' => DicomUtils::dateTimeToCarbon(data_get($orthanc_src, 'RequestedTags.AcquisitionDate'), data_get($orthanc_src, 'RequestedTags.AcquisitionTime')), - ]; - $properties = array_purge($properties); - if (empty($properties)) { - $properties = [ - 'other_patient_names' => data_get($orthanc_src, 'MainDicomTags.OtherPatientNames'), - 'other_patient_ids' => data_get($orthanc_src, 'MainDicomTags.OtherPatientIDs'), - 'software_versions' => data_get($orthanc_src, 'MainDicomTags.SoftwareVersions'), - 'station_name' => data_get($orthanc_src, 'MainDicomTags.StationName'), - 'operators_name' => data_get($orthanc_src, 'MainDicomTags.OperatorsName'), - 'manufacturer' => data_get($orthanc_src, 'MainDicomTags.Manufacturer'), - 'manufacturer_model_name' => data_get($orthanc_src, 'MainDicomTags.ManufacturerModelName'), - 'acquisition_date' => DicomUtils::dateTimeToCarbon(data_get($orthanc_src, 'MainDicomTags.AcquisitionDate'), data_get($orthanc_src, 'MainDicomTags.AcquisitionTime')), - ]; - $properties = array_purge($properties); - } - - $series = []; - foreach (data_get($orthanc_src, 'Series', []) as $ser) { - $params = [ - 'orthanc_uuid' => strtolower($ser['ID']), - 'series_instance_uid' => data_get($ser, 'MainDicomTags.SeriesInstanceUID'), - 'series_date' => DicomUtils::dateTimeToCarbon(data_get($ser, 'MainDicomTags.SeriesDate'), data_get($ser, 'MainDicomTags.SeriesTime')), - 'series_number' => data_get($ser, 'MainDicomTags.SeriesNumber'), - 'series_description' => data_get($ser, 'MainDicomTags.SeriesDescription'), - 'protocol_name' => data_get($ser, 'MainDicomTags.ProtocolName'), - 'modality' => data_get($ser, 'MainDicomTags.Modality'), - 'body_part_examined' => data_get($ser, 'MainDicomTags.BodyPartExamined'), - 'performed_procedure_step_description' => data_get($ser, 'MainDicomTags.PerformedProcedureStepDescription'), - 'sequence_name' => data_get($ser, 'MainDicomTags.SequenceName'), - ]; - $params['num_instances'] = count(data_get($ser, 'Instances', [])); - $params = array_purge($params); - if (! empty($params)) { - $series[] = $params; - } - } - - if (empty($series)) { - $series = null; - } else { - // $series = array_multisort(array_column($series, 'series_number'), SORT_ASC, $series); - usort($series, fn ($a, $b): int => (int) $a['series_number'] <=> (int) $b['series_number']); - } - if (empty($properties)) { - $properties = null; - } - $details = compact('properties', 'series'); - $details = array_purge($details); - $study = array_purge($study); - - return compact('study', 'details'); - } - - private function setValue(array &$array, string $key, mixed $value): void - { - if (filled($value)) { - $array[$key] = $value; - } - } - - private function updateStudy(int $row_id, mixed $study): void - { - $payload = $this->prepareData($study); - unset($payload['study']['orthanc_uuid']); - $payload['study']['updated_at'] = now(); - DB::table('studies')->where('id', $row_id)->update($payload['study']); - - if (! empty($payload['details'])) { - $payload['details']['updated_at'] = now(); - DB::table('study_details')->where('study_id', $row_id)->update($payload['details']); - } - } - - private function insertStudy(mixed $study): void - { - $payload = $this->prepareData($study); - $row = Study::create($payload['study']); - $payload['details']['study_id'] = $row->id; - StudyDetails::create($payload['details']); - } -}