This commit is contained in:
Dr Masroor Ehsan 2024-12-28 20:02:28 +06:00
parent 7bf6a94363
commit cf12ab27e4
9 changed files with 151 additions and 45 deletions

View File

@ -2,21 +2,29 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Services\Pacs; use App\Services\Pacs\OrthancRestClient;
class PacsController extends Controller class PacsController extends Controller
{ {
public function index() public function index()
{ {
$studies = (new Pacs)->getStudies(); $studies = (new OrthancRestClient)->getStudies();
dd($studies[0]);
return view('pacs.studies', compact('studies')); return view('pacs.studies', compact('studies'));
} }
public function show($id) public function show($id)
{ {
$study = (new Pacs)->getStudy($id); $study = (new OrthancRestClient)->getStudy($id);
return view('pacs.study', compact('study')); return view('pacs.study', compact('study'));
} }
public function import($id)
{
$studies = (new OrthancRestClient)->getStudies();
return redirect()->route('pacs.index');
}
} }

View File

@ -2,6 +2,16 @@
namespace App\Models; namespace App\Models;
use App\Models\Enums\ReportStatus;
use App\Models\Enums\StudyLevelStatus;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
class Study extends Model {} class Study extends Model
{
protected $casts = [
'is_locked' => 'boolean',
'is_active' => 'boolean',
'study_status' => StudyLevelStatus::class,
'report_status' => ReportStatus::class,
];
}

View File

@ -1,32 +0,0 @@
<?php
namespace App\Services;
use Carbon\Carbon;
use GuzzleHttp\Client;
final class Pacs
{
public function getClient(): Client
{
return new Client([
'base_uri' => config('pacs.api.endpoint'),
]);
}
public function getStudies(): array
{
$url = '/studies?'.http_build_query(['expand' => '1']);
$response = $this->getClient()->get($url);
$studies = json_decode($response->getBody()->getContents(), true);
$result = [];
foreach ($studies as $study) {
$study['ReceiveDateTime'] = Carbon::parse($study['LastUpdate'], 'UTC');
$study['StudyDateTime'] = Carbon::createFromFormat('Ymd His.u', $study['MainDicomTags']['StudyDate'].' '.$study['MainDicomTags']['StudyTime'], 'UTC');
$result[] = $study;
}
return $result;
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace App\Services\Pacs;
enum DicomTags: string
{
case AcquisitionDate = 'AcquisitionDate';
case AcquisitionTime = 'AcquisitionTime';
case AcquisitionDeviceProcessingDescription = 'AcquisitionDeviceProcessingDescription';
case BodyPartExamined = 'BodyPartExamined';
case Modality = 'Modality';
case SoftwareVersions = 'SoftwareVersions';
case StationName = 'StationName';
case Private10 = '0029,0010';
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Services\Pacs;
use Carbon\Carbon;
final class DicomUtils
{
public static function dateToCarbon(?string $datePart, string $timezone = 'UTC'): ?Carbon
{
if ($datePart === null) {
return null;
}
return Carbon::createFromFormat('Ymd', $datePart, $timezone);
}
public static function dateTimeToCarbon(?string $datePart, ?string $timePart, string $timezone = 'UTC'): ?Carbon
{
if ($datePart === null || $timePart === null) {
return null;
}
return Carbon::createFromFormat('YmdHis.u', $datePart.$timePart, $timezone);
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Services\Pacs;
use GuzzleHttp\Client;
final class OrthancRestClient
{
public function getClient(): Client
{
return new Client([
'base_uri' => config('pacs.api.endpoint'),
]);
}
public function getStudies(): array
{
$query = [
'expand' => '1',
'requested-tags' => implode(';', array_column(DicomTags::cases(), 'value')),
];
$url = '/studies?'.http_build_query($query);
$response = $this->getClient()->get($url);
$studies = json_decode($response->getBody()->getContents(), true);
return $studies;
}
}

View File

@ -0,0 +1,48 @@
<?php
namespace App\Services\Pacs;
use App\Models\Enums\StudyLevelStatus;
use App\Models\Study;
use Carbon\Carbon;
final class StudyImporter
{
public function import(array $studies): void
{
foreach ($studies as $study) {
$othanc_id = strtolower($study['ID']);
$row = Study::where('othanc_id', $othanc_id)->first();
if ($row && $row->study_status < StudyLevelStatus::StudyArrived) {
// todo: update study
return;
}
$data = [
'othanc_id' => $othanc_id,
'study_status' => StudyLevelStatus::StudyArrived,
'study_datetime' => $study['StudyDateTime'],
'receive_datetime' => $study['ReceiveDateTime'],
'is_locked' => false,
'is_active' => true,
'patient_id' => $study['PatientMainDicomTags']['PatientID'],
'patient_name' => $study['PatientMainDicomTags']['PatientName'],
'patient_sex' => $study['PatientMainDicomTags']['PatientSex'],
'patient_birthdate' => $study['PatientMainDicomTags']['PatientBirthDate'],
'institution_name' => $study['MainDicomTags']['InstitutionName'],
'accession_number' => $study['MainDicomTags']['AccessionNumber'],
'referring_physician_name' => $study['MainDicomTags']['ReferringPhysicianName'],
'study_id' => $study['MainDicomTags']['StudyID'],
'study_date' => DicomUtils::dateTimeToCarbon($study['MainDicomTags']['StudyDate'], $study['MainDicomTags']['StudyTime']),
'receive_date' => Carbon::parse($study['LastUpdate'], 'UTC'),
'study_modality' => $study['MainDicomTags']['Modality'],
'series_count' => count($study['Series']),
];
$row = Study::create($data);
}
}
}

14
composer.lock generated
View File

@ -7583,16 +7583,16 @@
}, },
{ {
"name": "barryvdh/reflection-docblock", "name": "barryvdh/reflection-docblock",
"version": "v2.1.3", "version": "v2.2.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/barryvdh/ReflectionDocBlock.git", "url": "https://github.com/barryvdh/ReflectionDocBlock.git",
"reference": "c6fad15f7c878be21650c51e1f841bca7e49752e" "reference": "db125e8df4329bd45f2da405aab007f502f38531"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/c6fad15f7c878be21650c51e1f841bca7e49752e", "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/db125e8df4329bd45f2da405aab007f502f38531",
"reference": "c6fad15f7c878be21650c51e1f841bca7e49752e", "reference": "db125e8df4329bd45f2da405aab007f502f38531",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -7608,7 +7608,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "2.0.x-dev" "dev-master": "2.2.x-dev"
} }
}, },
"autoload": { "autoload": {
@ -7629,9 +7629,9 @@
} }
], ],
"support": { "support": {
"source": "https://github.com/barryvdh/ReflectionDocBlock/tree/v2.1.3" "source": "https://github.com/barryvdh/ReflectionDocBlock/tree/v2.2.0"
}, },
"time": "2024-10-23T11:41:03+00:00" "time": "2024-12-28T10:00:03+00:00"
}, },
{ {
"name": "brianium/paratest", "name": "brianium/paratest",

View File

@ -21,12 +21,14 @@ public function up(): void
$table->unsignedTinyInteger('study_priority')->default(0); $table->unsignedTinyInteger('study_priority')->default(0);
$table->string('patient_id')->nullable(); $table->string('patient_id')->nullable();
$table->string('patient_name'); $table->string('patient_name');
$table->string('patient_sex'); $table->string('patient_sex')->nullable();
$table->date('patient_birthdate')->nullable(); $table->date('patient_birthdate')->nullable();
$table->string('study_instance_uid')->unique(); $table->string('study_instance_uid')->unique();
$table->string('institution_name'); $table->string('study_id')->nullable();
$table->string('institution_name')->nullable();
$table->string('accession_number')->nullable(); $table->string('accession_number')->nullable();
$table->string('study_description')->nullable(); $table->string('study_description')->nullable();
$table->string('referring_physician_name')->nullable();
$table->string('study_modality', 4)->nullable(); $table->string('study_modality', 4)->nullable();
$table->dateTime('study_date'); $table->dateTime('study_date');
$table->dateTime('receive_date'); $table->dateTime('receive_date');