radfusion/app/Services/Pacs/Sync/StudiesSync.php
2025-01-07 16:54:38 +06:00

224 lines
8.8 KiB
PHP

<?php
namespace App\Services\Pacs\Sync;
use App\Domain\Study\StudyLevelStatus;
use App\Services\Pacs\DicomUtils;
use App\Services\Pacs\InstituteMapper;
use App\Services\Pacs\OrthancRestClient;
use Carbon\Carbon;
use Exception;
use Illuminate\Pipeline\Pipeline;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
class StudiesSync
{
private Collection $study_ids;
private Collection $insert_queue;
private Collection $update_queue;
private Collection $archive_queue;
private OrthancRestClient $client;
public function __construct(?OrthancRestClient $client = null)
{
$this->study_ids = collect();
$this->client = $client ?? new OrthancRestClient;
$this->resetQueues();
}
public function execute(): void
{
app(Pipeline::class)
->send($this)
->through([
Pipes\ScanStudies::class,
Pipes\FilterStudies::class,
Pipes\InsertStudies::class,
Pipes\UpdateStudies::class,
Pipes\ArchiveStudies::class,
])
->thenReturn();
}
public function getClient(): OrthancRestClient
{
return $this->client;
}
public function getStudyIds(): Collection
{
return $this->study_ids;
}
public function setStudyIds(array $study_ids): void
{
$this->study_ids = collect($study_ids);
}
public function resetQueues()
{
$this->insert_queue = collect();
$this->update_queue = collect();
$this->archive_queue = collect();
}
public function getInsertQueue(): Collection
{
return $this->insert_queue;
}
public function getUpdateQueue(): Collection
{
return $this->update_queue;
}
public function getArchiveQueue(): Collection
{
return $this->archive_queue;
}
public function fetchStudyDetails(string $orthanc_uuid): ?array
{
$study = $this->client->getStudyDetails($orthanc_uuid);
if ($study == null) {
return null;
}
$stats = $this->client->getStudyStatistics($orthanc_uuid);
$study['Statistics'] = $stats;
$series = $this->client->getStudySeries($orthanc_uuid);
$study['Series'] = $series;
return $study;
}
public function transformData(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']),
'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'),
'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']) ?? Carbon::createFromTimestamp(0),
'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;
}
}
}