Compare commits
15 Commits
927c51e16d
...
a7228b8a61
Author | SHA1 | Date | |
---|---|---|---|
a7228b8a61 | |||
fac10cdc25 | |||
d4c6e58e30 | |||
9e969df68b | |||
a02adfc685 | |||
ce28bb1422 | |||
0fce30bd18 | |||
7a28b892f3 | |||
60f32a6468 | |||
4e2c1837ee | |||
ea341ca8f4 | |||
9f60cfb32e | |||
97481e8473 | |||
802f13c702 | |||
3a775c4f21 |
@ -50,4 +50,19 @@ public static function worklist_stats(int $days, int $workflow_level)
|
|||||||
|
|
||||||
return $rows;
|
return $rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function activeRads(): array
|
||||||
|
{
|
||||||
|
return cache()
|
||||||
|
->remember('active_rads',
|
||||||
|
now()->addMinutes(5),
|
||||||
|
fn () => DB::table('users')
|
||||||
|
->join('model_has_roles', 'users.id', '=', 'model_has_roles.model_id')
|
||||||
|
->join('roles', 'model_has_roles.role_id', '=', 'roles.id')
|
||||||
|
->where('roles.name', Role::Radiologist->value)
|
||||||
|
->where('users.is_active', true)
|
||||||
|
->orderBy('users.display_name')
|
||||||
|
->pluck('users.display_name', 'users.id')
|
||||||
|
->toArray());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ interface IUserStudyLister
|
|||||||
{
|
{
|
||||||
public function setRadiologist(int $radiologist_id): self;
|
public function setRadiologist(int $radiologist_id): self;
|
||||||
|
|
||||||
public function setWorkflowLevel(WorkflowLevel $status): self;
|
public function setWorkflowLevel(WorkflowLevel $level): self;
|
||||||
|
|
||||||
public function setPerPage(int $size): self;
|
public function setPerPage(int $size): self;
|
||||||
|
|
||||||
|
@ -48,9 +48,9 @@ public function setRadiologist(int $radiologist_id): self
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setWorkflowLevel(WorkflowLevel $status): self
|
public function setWorkflowLevel(WorkflowLevel $level): self
|
||||||
{
|
{
|
||||||
$this->workflowLevel = $status;
|
$this->workflowLevel = $level;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -71,7 +71,6 @@ public function get(?int $user_id = null): LengthAwarePaginator
|
|||||||
$query = $this->applySearch($query);
|
$query = $this->applySearch($query);
|
||||||
$query = $this->applyRadiologist($query);
|
$query = $this->applyRadiologist($query);
|
||||||
$query = $this->applyWorkflowLevel($query);
|
$query = $this->applyWorkflowLevel($query);
|
||||||
$query = $this->applyReportStatus($query);
|
|
||||||
$query = $this->applyArchived($query);
|
$query = $this->applyArchived($query);
|
||||||
$query = $this->applyLocked($query);
|
$query = $this->applyLocked($query);
|
||||||
$query = $this->applyDateFilters($query);
|
$query = $this->applyDateFilters($query);
|
||||||
|
@ -13,22 +13,27 @@
|
|||||||
use Filament\Tables\Columns\IconColumn;
|
use Filament\Tables\Columns\IconColumn;
|
||||||
use Filament\Tables\Columns\TextColumn;
|
use Filament\Tables\Columns\TextColumn;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
use Ramsey\Uuid\Uuid;
|
use Ramsey\Uuid\Uuid;
|
||||||
|
|
||||||
class DepartmentResource extends Resource
|
class DepartmentResource extends Resource
|
||||||
{
|
{
|
||||||
protected static ?string $model = Department::class;
|
protected static ?string $model = Department::class;
|
||||||
|
|
||||||
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
|
protected static ?string $navigationIcon = 'heroicon-o-building-storefront';
|
||||||
|
|
||||||
public static function form(Form $form): Form
|
public static function form(Form $form): Form
|
||||||
{
|
{
|
||||||
return $form
|
return $form
|
||||||
->schema([
|
->schema([
|
||||||
TextInput::make('guid')
|
TextInput::make('guid')
|
||||||
|
->label('Unique ID')
|
||||||
|
->default(sprintf('DEP-%s', Str::of(Uuid::uuid4())->lower()))
|
||||||
|
->disabled()
|
||||||
|
->dehydrated()
|
||||||
->required()
|
->required()
|
||||||
->maxLength(40)
|
->maxLength(40)
|
||||||
->default(sprintf('FAC-%s', Uuid::uuid4())),
|
->unique(ignoreRecord: true),
|
||||||
Toggle::make('is_active')
|
Toggle::make('is_active')
|
||||||
->required(),
|
->required(),
|
||||||
Select::make('organization_id')
|
Select::make('organization_id')
|
||||||
@ -44,15 +49,10 @@ public static function table(Table $table): Table
|
|||||||
{
|
{
|
||||||
return $table
|
return $table
|
||||||
->columns([
|
->columns([
|
||||||
TextColumn::make('guid')
|
IconColumn::make('is_active')->boolean(),
|
||||||
->searchable(),
|
TextColumn::make('name')->searchable(),
|
||||||
IconColumn::make('is_active')
|
TextColumn::make('organization.name')->sortable(),
|
||||||
->boolean(),
|
TextColumn::make('guid')->searchable(),
|
||||||
TextColumn::make('organization.name')
|
|
||||||
->numeric()
|
|
||||||
->sortable(),
|
|
||||||
TextColumn::make('name')
|
|
||||||
->searchable(),
|
|
||||||
TextColumn::make('created_at')
|
TextColumn::make('created_at')
|
||||||
->dateTime()
|
->dateTime()
|
||||||
->sortable()
|
->sortable()
|
||||||
|
126
app/Filament/Resources/DicomRoutingRuleResource.php
Normal file
126
app/Filament/Resources/DicomRoutingRuleResource.php
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources;
|
||||||
|
|
||||||
|
use App\DAL\Radiologists;
|
||||||
|
use App\Filament\Resources\DicomRoutingRuleResource\Pages;
|
||||||
|
use App\Models\DicomRoutingRule;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
|
use Filament\Forms\Components\Textarea;
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Forms\Components\Toggle;
|
||||||
|
use Filament\Forms\Form;
|
||||||
|
use Filament\Resources\Resource;
|
||||||
|
use Filament\Tables;
|
||||||
|
use Filament\Tables\Columns\IconColumn;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
|
||||||
|
class DicomRoutingRuleResource extends Resource
|
||||||
|
{
|
||||||
|
protected static ?string $model = DicomRoutingRule::class;
|
||||||
|
protected static ?string $modelLabel = 'Routing Rules';
|
||||||
|
protected static ?string $navigationIcon = 'heroicon-o-academic-cap';
|
||||||
|
|
||||||
|
public static function form(Form $form): Form
|
||||||
|
{
|
||||||
|
return $form
|
||||||
|
->schema([
|
||||||
|
Toggle::make('is_active')
|
||||||
|
->required(),
|
||||||
|
Select::make('organization_id')
|
||||||
|
->label('Organization')
|
||||||
|
->relationship('organization', 'name')
|
||||||
|
->required(),
|
||||||
|
Select::make('department_id')
|
||||||
|
->label('Department')
|
||||||
|
->relationship('department', 'name'),
|
||||||
|
Select::make('user_id')
|
||||||
|
->label('Radiologist')
|
||||||
|
->relationship('radiologist', 'display_name')
|
||||||
|
->options(Radiologists::activeRads()),
|
||||||
|
Select::make('assignment_panel_id')
|
||||||
|
->label('Panel')
|
||||||
|
->relationship('panel', 'name'),
|
||||||
|
TextInput::make('name')
|
||||||
|
->maxLength(255),
|
||||||
|
Textarea::make('condition')
|
||||||
|
->columnSpanFull()
|
||||||
|
->required(),
|
||||||
|
TextInput::make('priority')
|
||||||
|
->required()
|
||||||
|
->numeric()
|
||||||
|
->default(0),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->columns([
|
||||||
|
IconColumn::make('is_active')
|
||||||
|
->label('')
|
||||||
|
->boolean(),
|
||||||
|
TextColumn::make('name')
|
||||||
|
->searchable(),
|
||||||
|
TextColumn::make('condition')
|
||||||
|
->label('Rule')
|
||||||
|
->limit(20)
|
||||||
|
->searchable(),
|
||||||
|
TextColumn::make('priority')
|
||||||
|
->numeric()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('organization.name')
|
||||||
|
->label('Org')
|
||||||
|
->numeric()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('department.name')
|
||||||
|
->label('Dept')
|
||||||
|
->numeric()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('user_id')
|
||||||
|
->label('Rad')
|
||||||
|
->numeric()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('assignment_panel_id')
|
||||||
|
->label('Panel')
|
||||||
|
->numeric()
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('created_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
TextColumn::make('updated_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
//
|
||||||
|
])
|
||||||
|
->actions([
|
||||||
|
Tables\Actions\EditAction::make(),
|
||||||
|
])
|
||||||
|
->bulkActions([
|
||||||
|
Tables\Actions\BulkActionGroup::make([
|
||||||
|
Tables\Actions\DeleteBulkAction::make(),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRelations(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getPages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'index' => Pages\ListDicomRoutingRules::route('/'),
|
||||||
|
'create' => Pages\CreateDicomRoutingRule::route('/create'),
|
||||||
|
'edit' => Pages\EditDicomRoutingRule::route('/{record}/edit'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\DicomRoutingRuleResource\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\DicomRoutingRuleResource;
|
||||||
|
use Filament\Resources\Pages\CreateRecord;
|
||||||
|
|
||||||
|
class CreateDicomRoutingRule extends CreateRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = DicomRoutingRuleResource::class;
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\DicomRoutingRuleResource\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\DicomRoutingRuleResource;
|
||||||
|
use Filament\Actions;
|
||||||
|
use Filament\Resources\Pages\EditRecord;
|
||||||
|
|
||||||
|
class EditDicomRoutingRule extends EditRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = DicomRoutingRuleResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Actions\DeleteAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\DicomRoutingRuleResource\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\DicomRoutingRuleResource;
|
||||||
|
use Filament\Actions;
|
||||||
|
use Filament\Resources\Pages\ListRecords;
|
||||||
|
|
||||||
|
class ListDicomRoutingRules extends ListRecords
|
||||||
|
{
|
||||||
|
protected static string $resource = DicomRoutingRuleResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Actions\CreateAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -3,12 +3,14 @@
|
|||||||
namespace App\Filament\Resources;
|
namespace App\Filament\Resources;
|
||||||
|
|
||||||
use App\Filament\Resources\DicomServerResource\Pages;
|
use App\Filament\Resources\DicomServerResource\Pages;
|
||||||
|
use App\Models\Department;
|
||||||
use App\Models\DicomServer;
|
use App\Models\DicomServer;
|
||||||
use App\Services\GeoLocation;
|
use App\Services\GeoLocation;
|
||||||
use Filament\Forms\Components\Select;
|
use Filament\Forms\Components\Select;
|
||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
use Filament\Forms\Components\Toggle;
|
use Filament\Forms\Components\Toggle;
|
||||||
use Filament\Forms\Form;
|
use Filament\Forms\Form;
|
||||||
|
use Filament\Forms\Get;
|
||||||
use Filament\Resources\Resource;
|
use Filament\Resources\Resource;
|
||||||
use Filament\Tables;
|
use Filament\Tables;
|
||||||
use Filament\Tables\Columns\IconColumn;
|
use Filament\Tables\Columns\IconColumn;
|
||||||
@ -30,6 +32,7 @@ public static function form(Form $form): Form
|
|||||||
->required(),
|
->required(),
|
||||||
TextInput::make('server_name')
|
TextInput::make('server_name')
|
||||||
->required()
|
->required()
|
||||||
|
->unique(ignoreRecord: true)
|
||||||
->maxLength(255),
|
->maxLength(255),
|
||||||
Select::make('geo_code')
|
Select::make('geo_code')
|
||||||
->required()
|
->required()
|
||||||
@ -48,12 +51,11 @@ public static function form(Form $form): Form
|
|||||||
->required()
|
->required()
|
||||||
->maxLength(255),
|
->maxLength(255),
|
||||||
TextInput::make('ae_title')
|
TextInput::make('ae_title')
|
||||||
->maxLength(255),
|
->maxLength(24),
|
||||||
TextInput::make('username')
|
TextInput::make('username')
|
||||||
->maxLength(255),
|
->maxLength(40),
|
||||||
TextInput::make('password')
|
TextInput::make('password')
|
||||||
->password()
|
->maxLength(40),
|
||||||
->maxLength(255),
|
|
||||||
TextInput::make('wado_path')
|
TextInput::make('wado_path')
|
||||||
->maxLength(255),
|
->maxLength(255),
|
||||||
TextInput::make('stone_viewer_path')
|
TextInput::make('stone_viewer_path')
|
||||||
@ -62,10 +64,21 @@ public static function form(Form $form): Form
|
|||||||
->maxLength(255),
|
->maxLength(255),
|
||||||
TextInput::make('meddream_viewer_path')
|
TextInput::make('meddream_viewer_path')
|
||||||
->maxLength(255),
|
->maxLength(255),
|
||||||
TextInput::make('organization_id')
|
Select::make('organization_id')
|
||||||
->numeric(),
|
->label('Organization')
|
||||||
TextInput::make('department_id')
|
->live()
|
||||||
->numeric(),
|
->relationship('organization', 'name'),
|
||||||
|
Select::make('department_id')
|
||||||
|
->label('Department')
|
||||||
|
->options(function (Get $get) {
|
||||||
|
$organization_id = $get('organization_id');
|
||||||
|
$result = [];
|
||||||
|
if ($organization_id != null) {
|
||||||
|
$result = Department::active()->organization($organization_id)->pluck('name', 'id')->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,12 +101,13 @@ public static function table(Table $table): Table
|
|||||||
->label('WADO'),
|
->label('WADO'),
|
||||||
TextColumn::make('dicom_port')
|
TextColumn::make('dicom_port')
|
||||||
->label('DICOM'),
|
->label('DICOM'),
|
||||||
TextColumn::make('rest_api_endpoint')
|
|
||||||
->label('REST')
|
|
||||||
->searchable(),
|
|
||||||
TextColumn::make('ae_title')
|
TextColumn::make('ae_title')
|
||||||
->label('AET')
|
->label('AET')
|
||||||
->searchable(),
|
->searchable(),
|
||||||
|
TextColumn::make('rest_api_endpoint')
|
||||||
|
->label('REST')
|
||||||
|
->url(fn (DicomServer $srv) => $srv->rest_api_endpoint, shouldOpenInNewTab: true)
|
||||||
|
->searchable(),
|
||||||
TextColumn::make('created_at')
|
TextColumn::make('created_at')
|
||||||
->dateTime()
|
->dateTime()
|
||||||
->sortable()
|
->sortable()
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
use App\Filament\Resources\OrganizationResource\Pages;
|
use App\Filament\Resources\OrganizationResource\Pages;
|
||||||
use App\Models\Organization;
|
use App\Models\Organization;
|
||||||
|
use Filament\Forms\Components\Textarea;
|
||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
use Filament\Forms\Components\Toggle;
|
use Filament\Forms\Components\Toggle;
|
||||||
use Filament\Forms\Form;
|
use Filament\Forms\Form;
|
||||||
@ -14,28 +15,34 @@
|
|||||||
use Filament\Tables\Columns\IconColumn;
|
use Filament\Tables\Columns\IconColumn;
|
||||||
use Filament\Tables\Columns\TextColumn;
|
use Filament\Tables\Columns\TextColumn;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
use Ramsey\Uuid\Uuid;
|
use Ramsey\Uuid\Uuid;
|
||||||
|
|
||||||
class OrganizationResource extends Resource
|
class OrganizationResource extends Resource
|
||||||
{
|
{
|
||||||
protected static ?string $model = Organization::class;
|
protected static ?string $model = Organization::class;
|
||||||
|
|
||||||
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
|
protected static ?string $navigationIcon = 'heroicon-o-building-office-2';
|
||||||
|
|
||||||
public static function form(Form $form): Form
|
public static function form(Form $form): Form
|
||||||
{
|
{
|
||||||
return $form
|
return $form
|
||||||
->schema([
|
->schema([
|
||||||
TextInput::make('guid')
|
TextInput::make('guid')
|
||||||
|
->label('Unique ID')
|
||||||
|
->default(sprintf('ORG-%s', Str::of(Uuid::uuid4())->lower()))
|
||||||
|
->disabled()
|
||||||
|
->dehydrated()
|
||||||
->required()
|
->required()
|
||||||
->maxLength(40)
|
->maxLength(40)
|
||||||
->default(sprintf('INS-%s', Uuid::uuid4())),
|
->unique(ignoreRecord: true),
|
||||||
TextInput::make('name')
|
|
||||||
->required()
|
|
||||||
->maxLength(255),
|
|
||||||
Toggle::make('is_active')
|
Toggle::make('is_active')
|
||||||
->required(),
|
->required(),
|
||||||
TextInput::make('address')
|
TextInput::make('name')
|
||||||
|
->required()
|
||||||
|
->unique(ignoreRecord: true)
|
||||||
|
->maxLength(255),
|
||||||
|
TextArea::make('address')
|
||||||
->maxLength(255),
|
->maxLength(255),
|
||||||
TextInput::make('logo_path')
|
TextInput::make('logo_path')
|
||||||
->maxLength(255),
|
->maxLength(255),
|
||||||
@ -46,16 +53,9 @@ public static function table(Table $table): Table
|
|||||||
{
|
{
|
||||||
return $table
|
return $table
|
||||||
->columns([
|
->columns([
|
||||||
TextColumn::make('guid')
|
IconColumn::make('is_active')->boolean(),
|
||||||
->searchable(),
|
TextColumn::make('name')->searchable(),
|
||||||
TextColumn::make('name')
|
TextColumn::make('guid')->searchable(),
|
||||||
->searchable(),
|
|
||||||
IconColumn::make('is_active')
|
|
||||||
->boolean(),
|
|
||||||
TextColumn::make('address')
|
|
||||||
->searchable(),
|
|
||||||
TextColumn::make('logo_path')
|
|
||||||
->searchable(),
|
|
||||||
TextColumn::make('created_at')
|
TextColumn::make('created_at')
|
||||||
->dateTime()
|
->dateTime()
|
||||||
->sortable()
|
->sortable()
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Services\ACL\RoleService;
|
use App\Services\ACL\RoleService;
|
||||||
use App\Services\TimezoneList;
|
use App\Services\TimezoneList;
|
||||||
use Filament\Forms;
|
|
||||||
use Filament\Forms\Components\DateTimePicker;
|
use Filament\Forms\Components\DateTimePicker;
|
||||||
use Filament\Forms\Components\FileUpload;
|
use Filament\Forms\Components\FileUpload;
|
||||||
use Filament\Forms\Components\Select;
|
use Filament\Forms\Components\Select;
|
||||||
@ -41,7 +40,7 @@ public static function form(Form $form): Form
|
|||||||
|
|
||||||
return $form
|
return $form
|
||||||
->schema([
|
->schema([
|
||||||
Forms\Components\TextInput::make('guid')
|
TextInput::make('guid')
|
||||||
->label('Unique ID')
|
->label('Unique ID')
|
||||||
->default(sprintf('USR-%s', Str::of(Uuid::uuid4())->lower()))
|
->default(sprintf('USR-%s', Str::of(Uuid::uuid4())->lower()))
|
||||||
->disabled()
|
->disabled()
|
||||||
|
@ -21,7 +21,7 @@ public function rules(): array
|
|||||||
'referring_physician_name' => ['nullable'],
|
'referring_physician_name' => ['nullable'],
|
||||||
'institution_name' => ['nullable'],
|
'institution_name' => ['nullable'],
|
||||||
'priority' => ['required', 'integer'],
|
'priority' => ['required', 'integer'],
|
||||||
'cancel_read' => ['nullable', 'boolean'],
|
'cancel_read' => ['accepted'],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
'referring_physician_id' => ['nullable', 'exists:users,id'],
|
'referring_physician_id' => ['nullable', 'exists:users,id'],
|
||||||
|
@ -4,9 +4,8 @@
|
|||||||
|
|
||||||
use App\Models\Traits\Active;
|
use App\Models\Traits\Active;
|
||||||
use App\Models\Traits\HasOrganization;
|
use App\Models\Traits\HasOrganization;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
|
|
||||||
class Department extends Model
|
class Department extends BaseModel
|
||||||
{
|
{
|
||||||
use Active;
|
use Active;
|
||||||
use HasOrganization;
|
use HasOrganization;
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
use App\Models\Traits\Active;
|
use App\Models\Traits\Active;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
|
||||||
|
|
||||||
class DicomRoutingRule extends BaseModel
|
class DicomRoutingRule extends BaseModel
|
||||||
{
|
{
|
||||||
@ -20,14 +19,14 @@ public function department(): BelongsTo
|
|||||||
return $this->belongsTo(Department::class);
|
return $this->belongsTo(Department::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function panel(): HasOne
|
public function panel(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->hasOne(AssignmentPanel::class);
|
return $this->belongsTo(AssignmentPanel::class, 'assignment_panel_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function radiologist(): HasOne
|
public function radiologist(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->hasOne(User::class);
|
return $this->belongsTo(User::class, 'user_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function casts(): array
|
protected function casts(): array
|
||||||
|
@ -10,12 +10,12 @@ class DicomServer extends BaseModel
|
|||||||
{
|
{
|
||||||
use Active;
|
use Active;
|
||||||
|
|
||||||
public function institute(): BelongsTo
|
public function organization(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Organization::class);
|
return $this->belongsTo(Organization::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function facility(): BelongsTo
|
public function department(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Department::class);
|
return $this->belongsTo(Department::class);
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,8 @@
|
|||||||
|
|
||||||
final class StudiesSync
|
final class StudiesSync
|
||||||
{
|
{
|
||||||
public const string SYNC_AGENT = '$$_pacs_agent_$$';
|
public const SYNC_AGENT = '$$_pacs_agent_$$';
|
||||||
|
|
||||||
private Collection $study_ids;
|
private Collection $study_ids;
|
||||||
|
|
||||||
private Collection $insert_queue;
|
private Collection $insert_queue;
|
||||||
@ -147,6 +148,29 @@ public function transformData(mixed $orthanc_src): array
|
|||||||
|
|
||||||
$inst_name = data_get($orthanc_src, 'MainDicomTags.InstitutionName');
|
$inst_name = data_get($orthanc_src, 'MainDicomTags.InstitutionName');
|
||||||
$patient_name = data_get($orthanc_src, 'PatientMainDicomTags.PatientName');
|
$patient_name = data_get($orthanc_src, 'PatientMainDicomTags.PatientName');
|
||||||
|
$name_parts = tokenizeString($patient_name);
|
||||||
|
$patient_age = data_get($orthanc_src, 'RequestedTags.PatientAge');
|
||||||
|
if (blank($patient_age) && ! empty($name_parts)) {
|
||||||
|
// try to get age from last part of patient name
|
||||||
|
$last = end($name_parts);
|
||||||
|
if (preg_match('/^\d+[YMD]$/i', $last)) {
|
||||||
|
$patient_age = $last;
|
||||||
|
/*
|
||||||
|
// sanitize patient name
|
||||||
|
array_pop($name_parts);
|
||||||
|
$patient_name = implode(' ', $name_parts);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($patient_age !== null) {
|
||||||
|
$age = strtoupper(ltrim($patient_age, '0'));
|
||||||
|
if (strlen($age) > 1) {
|
||||||
|
$patient_age = $age;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// $patient_name = trim($patient_name, '.^ ');
|
||||||
|
|
||||||
$descr = $this->getStudyDescription($orthanc_src);
|
$descr = $this->getStudyDescription($orthanc_src);
|
||||||
$study = [
|
$study = [
|
||||||
@ -160,7 +184,7 @@ public function transformData(mixed $orthanc_src): array
|
|||||||
'patient_id' => data_get($orthanc_src, 'PatientMainDicomTags.PatientID'),
|
'patient_id' => data_get($orthanc_src, 'PatientMainDicomTags.PatientID'),
|
||||||
'patient_name' => $patient_name,
|
'patient_name' => $patient_name,
|
||||||
'patient_sex' => data_get($orthanc_src, 'PatientMainDicomTags.PatientSex'),
|
'patient_sex' => data_get($orthanc_src, 'PatientMainDicomTags.PatientSex'),
|
||||||
'patient_age' => data_get($orthanc_src, 'RequestedTags.PatientAge'),
|
'patient_age' => $patient_age,
|
||||||
|
|
||||||
'accession_number' => data_get($orthanc_src, 'MainDicomTags.AccessionNumber'),
|
'accession_number' => data_get($orthanc_src, 'MainDicomTags.AccessionNumber'),
|
||||||
'referring_physician_name' => data_get($orthanc_src, 'MainDicomTags.ReferringPhysicianName'),
|
'referring_physician_name' => data_get($orthanc_src, 'MainDicomTags.ReferringPhysicianName'),
|
||||||
@ -182,16 +206,40 @@ public function transformData(mixed $orthanc_src): array
|
|||||||
$study['workflow_level'] = $stable_study
|
$study['workflow_level'] = $stable_study
|
||||||
? WorkflowLevel::Unassigned->value
|
? WorkflowLevel::Unassigned->value
|
||||||
: WorkflowLevel::Received->value;
|
: WorkflowLevel::Received->value;
|
||||||
$study['patient_birthdate'] = null;
|
|
||||||
|
$patient_birthdate = null;
|
||||||
$dob = data_get($orthanc_src, 'PatientMainDicomTags.PatientBirthDate');
|
$dob = data_get($orthanc_src, 'PatientMainDicomTags.PatientBirthDate');
|
||||||
if (filled($dob)) {
|
if (filled($dob)) {
|
||||||
try {
|
try {
|
||||||
$study['patient_birthdate'] = Carbon::parse($dob);
|
$patient_birthdate = Carbon::parse($dob);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error('Failed to parse PatientMainDicomTags.PatientBirthDate: {dob}', ['dob' => $dob, 'exception' => $e->getMessage()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($patient_birthdate == null && $patient_age !== null) {
|
||||||
|
try {
|
||||||
|
// $age = (int) preg_replace('/[^0-9]/', '', $patient_age);
|
||||||
|
$age_num = (int) filter_var($patient_age, FILTER_SANITIZE_NUMBER_INT);
|
||||||
|
$now = now();
|
||||||
|
switch (strtoupper(substr($patient_age, -1))) {
|
||||||
|
case 'Y':
|
||||||
|
$patient_birthdate = $now->subYears($age_num);
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
$patient_birthdate = $now->subMonths($age_num);
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
$patient_birthdate = $now->subDays($age_num);
|
||||||
|
break;
|
||||||
|
}
|
||||||
} catch (Exception) {
|
} catch (Exception) {
|
||||||
Log::error('Failed to parse PatientMainDicomTags.PatientBirthDate: {dob}', ['dob' => $dob]);
|
Log::error('Failed to parse patient_age: {age}', ['age' => $patient_age]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$study['patient_birthdate'] = $patient_birthdate;
|
||||||
|
|
||||||
// check for priority in patient name or description
|
// check for priority in patient name or description
|
||||||
if (preg_match('/\b(urgent|stat)\b/i', implode(' ', [$descr, $patient_name]))) {
|
if (preg_match('/\b(urgent|stat)\b/i', implode(' ', [$descr, $patient_name]))) {
|
||||||
$this->setValue($study, 'priority', Priority::Stat->value);
|
$this->setValue($study, 'priority', Priority::Stat->value);
|
||||||
@ -283,7 +331,9 @@ private function getStudyDicomTags(string $study_uuid): array
|
|||||||
}
|
}
|
||||||
|
|
||||||
// randomly sample few instances for tags collection
|
// randomly sample few instances for tags collection
|
||||||
$selectedInstances = count($instances) <= $this->maxInstances ? $instances : array_rand(array_flip($instances), $this->maxInstances);
|
$selectedInstances = count($instances) <= $this->maxInstances
|
||||||
|
? $instances
|
||||||
|
: array_intersect_key($instances, array_flip(array_rand($instances, $this->maxInstances)));
|
||||||
|
|
||||||
$tags = collect();
|
$tags = collect();
|
||||||
foreach ($selectedInstances as $instance) {
|
foreach ($selectedInstances as $instance) {
|
||||||
|
@ -123,11 +123,13 @@ function me(User|int|null $user = null): User
|
|||||||
{
|
{
|
||||||
if (is_int($user) && $user > 0) {
|
if (is_int($user) && $user > 0) {
|
||||||
return User::find($user);
|
return User::find($user);
|
||||||
} elseif ($user instanceof User) {
|
|
||||||
return $user;
|
|
||||||
} else {
|
|
||||||
return auth()->user();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($user instanceof User) {
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return auth()->user();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,3 +143,10 @@ function formatTitle(string $str): string
|
|||||||
->toString();
|
->toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (! function_exists('tokenizeString')) {
|
||||||
|
function tokenizeString(string $str): array
|
||||||
|
{
|
||||||
|
return preg_split('/\W+/', trim($str), -1, PREG_SPLIT_NO_EMPTY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -10,7 +10,7 @@ public function up(): void
|
|||||||
{
|
{
|
||||||
Schema::create('organizations', static function (Blueprint $table) {
|
Schema::create('organizations', static function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->string('guid', 40)->unique()->index()->default(DB::raw("concat('INS-', gen_random_uuid())"));
|
$table->string('guid', 40)->unique()->index()->default(DB::raw("concat('ORG-', gen_random_uuid())"));
|
||||||
$table->string('name')->unique();
|
$table->string('name')->unique();
|
||||||
$table->boolean('is_active')->default(false);
|
$table->boolean('is_active')->default(false);
|
||||||
$table->string('address')->nullable();
|
$table->string('address')->nullable();
|
||||||
|
@ -11,7 +11,7 @@ public function up(): void
|
|||||||
{
|
{
|
||||||
Schema::create('departments', static function (Blueprint $table) {
|
Schema::create('departments', static function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->string('guid', 40)->unique()->index()->default(DB::raw("concat('FAC-', gen_random_uuid())"));
|
$table->string('guid', 40)->unique()->index()->default(DB::raw("concat('DEP-', gen_random_uuid())"));
|
||||||
$table->boolean('is_active')->default(false)->index();
|
$table->boolean('is_active')->default(false)->index();
|
||||||
$table->foreignIdFor(Organization::class)->constrained()->cascadeOnDelete();
|
$table->foreignIdFor(Organization::class)->constrained()->cascadeOnDelete();
|
||||||
$table->string('name');
|
$table->string('name');
|
||||||
|
@ -32,13 +32,25 @@
|
|||||||
<h5 class="mb-0">Patient Information</h5>
|
<h5 class="mb-0">Patient Information</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@include('staff.meta.partials._text', ['name' => 'patient_id', 'label' => 'Patient ID', 'value' => $study->patient_id])
|
|
||||||
@include('staff.meta.partials._text', ['name' => 'patient_name', 'label' => 'Patient Name', 'value' => $study->patient_name])
|
@include('staff.meta.partials._text', ['name' => 'patient_name', 'label' => 'Patient Name', 'value' => $study->patient_name])
|
||||||
@include('staff.meta.partials._text', ['name' => 'patient_sex', 'label' => 'Sex', 'value' => $study->patient_sex])
|
<div class="row">
|
||||||
@include('staff.meta.partials._date', ['name' => 'patient_birthdate', 'label' => 'Birth Date', 'value' => $study->patient_birthdate?->toDateString()])
|
<div class="col-6">
|
||||||
|
@include('staff.meta.partials._text', ['name' => 'patient_id', 'label' => 'Patient ID', 'value' => $study->patient_id])
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
@include('staff.meta.partials._text', ['name' => 'accession_number', 'label' => 'Accession number', 'value' => $study->accession_number])
|
@include('staff.meta.partials._text', ['name' => 'accession_number', 'label' => 'Accession number', 'value' => $study->accession_number])
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
@include('staff.meta.partials._text', ['name' => 'patient_sex', 'label' => 'Sex', 'value' => $study->patient_sex])
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
@include('staff.meta.partials._date', ['name' => 'patient_birthdate', 'label' => 'Birth Date', 'value' => $study->patient_birthdate?->toDateString()])
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-xl">
|
<div class="col-xl">
|
||||||
@ -47,10 +59,24 @@
|
|||||||
<h5 class="mb-0">Study Information</h5>
|
<h5 class="mb-0">Study Information</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
@include('staff.meta.partials._text', ['name' => 'study_description', 'label' => 'Study Description', 'value' => $study->study_description])
|
@include('staff.meta.partials._text', ['name' => 'study_description', 'label' => 'Study Description', 'value' => $study->study_description])
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
@include('staff.meta.partials._text', ['name' => 'body_part_examined', 'label' => 'Body Part Examined', 'value' => $study->body_part_examined])
|
@include('staff.meta.partials._text', ['name' => 'body_part_examined', 'label' => 'Body Part Examined', 'value' => $study->body_part_examined])
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
@include('staff.meta.partials._text', ['name' => 'institution_name', 'label' => 'Institution', 'value' => $study->institution_name])
|
@include('staff.meta.partials._text', ['name' => 'institution_name', 'label' => 'Institution', 'value' => $study->institution_name])
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
@include('staff.meta.partials._text', ['name' => 'referring_physician_name', 'label' => 'Referring Physician', 'value' => $study->referring_physician_name])
|
@include('staff.meta.partials._text', ['name' => 'referring_physician_name', 'label' => 'Referring Physician', 'value' => $study->referring_physician_name])
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<label class="form-check-label">Priority</label>
|
<label class="form-check-label">Priority</label>
|
||||||
<div class="col my-2">
|
<div class="col my-2">
|
||||||
|
Loading…
Reference in New Issue
Block a user