radfusion/app/Services/StudyRouter/DicomStudyRouter.php

120 lines
3.6 KiB
PHP

<?php
namespace App\Services\StudyRouter;
use App\Domain\ACL\Role;
use App\Models\AssignmentPanel;
use App\Models\DicomRoutingRule;
use App\Models\Organization;
use App\Models\User;
use Exception;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
final class DicomStudyRouter
{
private static ?Collection $rules = null;
private static ?Collection $activeRads = null;
private static int $catchAll = -1;
const int CACHE_TTL = 15;
public static function matchStudy(array $dicomHeaders): array
{
self::initialize();
if (blank($dicomHeaders)) {
return self::fallbackRouting();
}
$study = json_decode(json_encode($dicomHeaders)); // convert to object
$expression = new ExpressionLanguage;
foreach (self::$rules as $rule) {
try {
$matches = (bool) $expression->evaluate($rule->condition, ['study' => $study]);
} catch (Exception $exc) {
Log::error('Error evaluating rule expression', [
'rule_id' => $rule->id,
'condition' => $rule->condition,
'error' => $exc->getMessage(),
]);
return self::fallbackRouting();
}
if ($matches) {
return [
'organization_id' => $rule->organization_id,
'department_id' => $rule->department_id,
'rule_id' => $rule->id,
'rule_name' => $rule->name,
'radiologists' => self::getRadiologists($rule),
];
}
}
return self::fallbackRouting();
}
private static function fallbackRouting(): array
{
return [
'organization_id' => self::$catchAll,
'department_id' => null,
'rule_id' => null,
'radiologists' => null,
];
}
private static function initialize(): void
{
if (is_null(self::$rules)) {
self::$rules = Cache::remember('dicom.rules',
now()->addMinutes(self::CACHE_TTL),
fn () => DicomRoutingRule::active()
->orderByDesc('priority')
->get()
);
}
if (is_null(self::$activeRads)) {
self::$activeRads = Cache::remember('dicom.rads',
now()->addMinutes(self::CACHE_TTL),
fn () => User::active()
->role(Role::Radiologist)
->pluck('id')
);
}
if (self::$catchAll < 0) {
self::$catchAll = Organization::where('name', 'CATCH-ALL')
->first('id')
->id;
}
}
private static function getRadiologists(DicomRoutingRule $rule): array
{
if (! is_null($rule->assignment_panel_id)) {
$panel = AssignmentPanel::active()
->with('radiologists:id')
->find($rule->assignment_panel_id);
if ($panel) {
$rads = $panel->radiologists->pluck('id')->toArray();
if (! empty($rads)) {
// return only existing id from self::$activeRads
return array_intersect($rads, self::$activeRads->toArray());
}
}
} elseif (! is_null($rule->user_id)) {
if (self::$activeRads->contains($rule->user_id)) {
return [$rule->user_id];
}
}
return [];
}
}