radfusion/app/Services/Pacs/StudyImporter.php
2024-12-30 19:53:44 +06:00

211 lines
7.6 KiB
PHP

<?php
namespace App\Services\Pacs;
use App\Models\Enums\StudyLevelStatus;
use App\Models\Study;
use App\Models\StudyDetails;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
final class StudyImporter
{
private array $study_ids = [];
private array $insert_queue = [];
private array $update_queue = [];
private OrthancRestClient $client;
public function __construct(?OrthancRestClient $client = null)
{
$this->client = $client ?? new OrthancRestClient;
}
public function scanStudies()
{
$this->study_ids = $this->client->getStudiesIds();
}
private function checkUpdate(string $orthanc_uid): void
{
$row = DB::table('studies')->where('orthanc_uid', $orthanc_uid)->first(['id', 'study_status']);
if ($row === null) {
$this->insert_queue[] = $orthanc_uid;
return;
}
if ($row->study_status < StudyLevelStatus::StudyArrived->value) {
$this->update_queue[$orthanc_uid] = $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_uid => $row_id) {
$study = $this->fetchStudyDetails($orthanc_uid);
if ($study == null) {
continue;
}
$this->updateStudy($row_id, $study);
}
foreach ($this->insert_queue as $orthanc_uid) {
$study = $this->fetchStudyDetails($orthanc_uid);
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_uid' => strtolower($orthanc_src['ID']),
'is_locked' => false,
'is_active' => true,
'institution_name' => $inst_name,
'institute_id' => $inst_id,
'patient_uid' => $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']),
'receive_date' => 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) $orthanc_src['IsStable']) {
$study['study_status'] = StudyLevelStatus::StudyArrived->value;
} else {
$study['study_status'] = StudyLevelStatus::Pending->value;
}
$dob = data_get($orthanc_src, 'PatientMainDicomTags.PatientBirthDate');
if (filled($dob)) {
$study['patient_birthdate'] = Carbon::parse($dob);
}
$descr = data_get($orthanc_src, 'MainDicomTags.StudyDescription');
if (blank($descr)) {
$descr = data_get($orthanc_src, 'RequestedTags.AcquisitionDeviceProcessingDescription');
}
$this->setValue($study, 'study_description', trim($descr));
$details = [
'software_versions' => data_get($study, 'RequestedTags.SoftwareVersions'),
'station_name' => data_get($study, 'RequestedTags.StationName'),
'operators_name' => data_get($study, 'RequestedTags.OperatorsName'),
'manufacturer' => data_get($study, 'RequestedTags.Manufacturer'),
'manufacturer_model_name' => data_get($study, 'RequestedTags.ManufacturerModelName'),
];
$series = [];
foreach (data_get($orthanc_src, 'Series', []) as $ser) {
$params = [
'orthanc_uid' => strtolower($ser['ID']),
'series_instance_uid' => data_get($ser, 'MainDicomTags.SeriesInstanceUID'),
'series_date' => DicomUtils::dateTimeToCarbon($ser['MainDicomTags']['SeriesDate'], $ser['MainDicomTags']['SeriesTime']),
'series_number' => data_get($ser, 'MainDicomTags.SeriesNumber'),
'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_filter($params, fn ($v) => filled($v));
if (! empty($params)) {
$series[] = array_filter($params, fn ($v) => filled($v));
}
}
$study = array_filter($study, fn ($v) => filled($v));
$details = array_filter($details, fn ($v) => filled($v));
return compact('study', 'details', 'series');
}
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_uid']);
DB::table('studies')->where('id', $row_id)->update($payload['study']);
if (! empty($payload['details'])) {
DB::table('study_details')->where('study_id', $row_id)->update($payload['details']);
}
foreach ($payload['series'] as $series) {
$series_guid = $series['orthanc_uid'];
unset($series['orthanc_uid']);
DB::table('study_series')->where('orthanc_uid', $series_guid)->update($series);
}
}
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']);
foreach ($payload['series'] as $series) {
$series['study_id'] = $row->id;
DB::table('study_series')->insert($series);
}
}
}