This commit is contained in:
Dr Masroor Ehsan 2024-12-28 22:28:39 +06:00
parent 4c26b8a473
commit 3d6eb1543e
4 changed files with 156 additions and 68 deletions

View File

@ -8,8 +8,8 @@ enum StudyAccessFlags: int
{
use BitmaskFunctionality;
case None = 0;
case ViewPatientInfo = 1;
case ViewDicom = 1 << 1;
case ViewReports = 1 << 2;
case Forbidden = 0;
case ViewPatientInfo = 1 << 1;
case ViewDicom = 1 << 2;
case ViewReports = 1 << 3;
}

View File

@ -41,4 +41,9 @@ public function getStudies(): array
return json_decode($response->getBody()->getContents(), true);
}
public function getStudiesIds(): array
{
return json_decode($this->getClient()->get('/studies')->getBody()->getContents(), true);
}
}

View File

@ -5,73 +5,156 @@
use App\Models\Enums\StudyLevelStatus;
use App\Models\Study;
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 import(array $studies): void
{
foreach ($studies as $study) {
$orthanc_uid = strtolower($study['ID']);
$row = Study::where(compact('orthanc_uid'))->first();
if ($row != null) {
if ($row->study_status < StudyLevelStatus::StudyArrived) {
// todo: update study
}
return;
}
$inst_name = data_get($study, 'MainDicomTags.InstitutionName');
$inst_id = InstituteMapper::map($inst_name);
$data = [
'orthanc_uid' => $orthanc_uid,
'is_locked' => false,
'is_active' => true,
'institution_name' => $inst_name,
'institute_id' => $inst_id,
'patient_uuid' => $study['ParentPatient'],
'patient_id' => data_get($study, 'PatientMainDicomTags.PatientID'),
'patient_name' => data_get($study, 'PatientMainDicomTags.PatientName'),
'patient_sex' => data_get($study, 'PatientMainDicomTags.PatientSex'),
'accession_number' => data_get($study, 'MainDicomTags.AccessionNumber'),
'referring_physician_name' => data_get($study, 'MainDicomTags.ReferringPhysicianName'),
'study_id' => data_get($study, 'MainDicomTags.StudyID'),
'study_instance_uid' => data_get($study, 'MainDicomTags.StudyInstanceUID'),
'study_date' => DicomUtils::dateTimeToCarbon($study['MainDicomTags']['StudyDate'], $study['MainDicomTags']['StudyTime']),
'receive_date' => Carbon::parse($study['LastUpdate'], 'UTC'),
'study_modality' => data_get($study, 'RequestedTags.Modality'),
'body_part_examined' => data_get($study, 'RequestedTags.BodyPartExamined'),
'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_count' => count($study['Series']),
];
if ($study['IsStable']) {
$data['study_status'] = StudyLevelStatus::StudyArrived->value;
} else {
$data['study_status'] = StudyLevelStatus::Pending->value;
}
$dob = data_get($study, 'PatientMainDicomTags.PatientBirthDate');
if (trim($dob) != '') {
$data['patient_birthdate'] = Carbon::parse($dob);
}
$descr = data_get($study, 'MainDicomTags.StudyDescription');
if ($descr != null) {
$descr = data_get($study, 'RequestedTags.AcquisitionDeviceProcessingDescription');
}
$data['study_description'] = trim($descr);
Study::create($data);
$this->importStudy($study);
}
}
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;
}
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;
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 $study): array
{
$inst_name = data_get($study, 'MainDicomTags.InstitutionName');
$inst_id = InstituteMapper::map($inst_name);
$data = [
'orthanc_uid' => strtolower($study['ID']),
'is_locked' => false,
'is_active' => true,
'institution_name' => $inst_name,
'institute_id' => $inst_id,
'patient_uuid' => $study['ParentPatient'],
'patient_id' => data_get($study, 'PatientMainDicomTags.PatientID'),
'patient_name' => data_get($study, 'PatientMainDicomTags.PatientName'),
'patient_sex' => data_get($study, 'PatientMainDicomTags.PatientSex'),
'accession_number' => data_get($study, 'MainDicomTags.AccessionNumber'),
'referring_physician_name' => data_get($study, 'MainDicomTags.ReferringPhysicianName'),
'study_id' => data_get($study, 'MainDicomTags.StudyID'),
'study_instance_uid' => data_get($study, 'MainDicomTags.StudyInstanceUID'),
'study_date' => DicomUtils::dateTimeToCarbon($study['MainDicomTags']['StudyDate'], $study['MainDicomTags']['StudyTime']),
'receive_date' => Carbon::parse($study['LastUpdate'], 'UTC'),
'study_modality' => data_get($study, 'RequestedTags.Modality'),
'body_part_examined' => data_get($study, 'RequestedTags.BodyPartExamined'),
'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'),
'image_count' => data_get($study, 'Statistics.CountInstances'),
'series_count' => data_get($study, 'Statistics.CountSeries'),
'disk_size' => data_get($study, 'Statistics.DiskSize'),
];
if ($study['IsStable']) {
$data['study_status'] = StudyLevelStatus::StudyArrived->value;
} else {
$data['study_status'] = StudyLevelStatus::Pending->value;
}
$dob = data_get($study, 'PatientMainDicomTags.PatientBirthDate');
if (trim($dob) != '') {
$data['patient_birthdate'] = Carbon::parse($dob);
}
$descr = data_get($study, 'MainDicomTags.StudyDescription');
if ($descr != null) {
$descr = data_get($study, 'RequestedTags.AcquisitionDeviceProcessingDescription');
}
$data['study_description'] = trim($descr);
return $data;
}
public function updateStudy(int $row_id, mixed $study): void
{
$data = $this->prepareData($study);
unset($data['orthanc_uid']);
Study::where('id', $row_id)->update($data);
}
public function insertStudy(mixed $study): void
{
$data = $this->prepareData($study);
Study::create($data);
}
}

View File

@ -42,10 +42,10 @@ public function up(): void
$table->dateTime('receive_date');
$table->dateTime('report_date')->nullable();
$table->foreignIdFor(Institute::class)->constrained()->onDelete('cascade');
$table->unsignedTinyInteger('study_status')->default(StudyLevelStatus::None->value);
$table->unsignedTinyInteger('study_status')->default(StudyLevelStatus::Pending->value);
$table->unsignedTinyInteger('report_status')->default(ReportStatus::Pending->value);
$table->unsignedSmallInteger('image_count')->nullable(0);
$table->unsignedSmallInteger('image_count')->nullable();
$table->unsignedSmallInteger('series_count')->nullable();
$table->unsignedSmallInteger('disk_size')->nullable();
@ -54,7 +54,7 @@ public function up(): void
$table->foreignIdFor(User::class, 'referring_physician_id')->nullable()->constrained()->onDelete('set null');
$table->foreignIdFor(User::class, 'reading_physician_id')->nullable()->constrained()->onDelete('set null');
$table->unsignedTinyInteger('access_flags')->default(StudyAccessFlags::None->value);
$table->unsignedTinyInteger('access_flags')->default(StudyAccessFlags::Forbidden->value);
$table->string('access_password')->nullable();
$table->timestamps();