This commit is contained in:
Dr Masroor Ehsan 2025-01-05 12:49:53 +06:00
parent ea21a659d4
commit bebfb3ca7a
52 changed files with 292 additions and 328 deletions

View File

@ -2,8 +2,8 @@
namespace App\DAL; namespace App\DAL;
use Cache; use Illuminate\Support\Facades\Cache;
use DB; use Illuminate\Support\Facades\DB;
final readonly class Studies final readonly class Studies
{ {

View File

@ -39,6 +39,11 @@ abstract class WorklistBase implements IUserStudyLister
private ?string $reportDateTo = null; private ?string $reportDateTo = null;
protected static function reportCompleteQuery(Builder $query): Builder
{
return $query->where('report_status', '=', ReportStatus::Authorized->value);
}
public function setRadiologist(int $radiologist_id): self public function setRadiologist(int $radiologist_id): self
{ {
$this->radiologist_id = $radiologist_id; $this->radiologist_id = $radiologist_id;
@ -53,46 +58,6 @@ public function setStudyStatus(StudyLevelStatus $status): self
return $this; return $this;
} }
private function applyRadiologist(Builder $query): Builder
{
if ($this->radiologist_id != null) {
$rad = $this->radiologist_id;
$query = $query->where(function ($query) use ($rad) {
$query->Where('assigned_physician_id', '=', $rad);
$query->orWhere('reporting_physician_id', '=', $rad);
});
}
return $query;
}
protected function getStudiesQuery(): Builder
{
if ($this->archived === null) {
return Study::active();
}
return Study::query();
}
private function applyStudyStatus(Builder $query): Builder
{
if ($this->studyStatus != null) {
$query = $query->where('study_status', '=', $this->studyStatus->value);
}
return $query;
}
private function applyReportStatus(Builder $query): Builder
{
if ($this->reportStatus != null) {
$query = $query->where('report_status', '=', $this->reportStatus->value);
}
return $query;
}
public function setReportStatus(ReportStatus $status): self public function setReportStatus(ReportStatus $status): self
{ {
$this->reportStatus = $status; $this->reportStatus = $status;
@ -100,28 +65,6 @@ public function setReportStatus(ReportStatus $status): self
return $this; return $this;
} }
protected static function reportCompleteQuery(Builder $query): Builder
{
return $query->where('report_status', '=', ReportStatus::Authorized->value);
}
protected function applySort(Builder $query): Builder
{
if (! empty($this->sortColumns)) {
foreach ($this->sortColumns as $column => $dir) {
$query = $query->orderBy($column, $dir);
}
return $query;
}
return $query
->orderByDesc('priority')
->orderByDesc('received_at');
}
abstract protected function buildQuery(?int $user_id = null): Builder;
public function query(?int $user_id = null): Builder public function query(?int $user_id = null): Builder
{ {
$query = $this->buildQuery($user_id); $query = $this->buildQuery($user_id);
@ -189,6 +132,87 @@ public function setSearchTerm(string $search): self
return $this; return $this;
} }
public function setStudyDate(string $from, ?string $to = null): self
{
$this->studyDateFrom = $from;
$this->studyDateTo = $to;
return $this;
}
public function setReceiveDate(string $from, ?string $to = null): self
{
$this->receiveDateFrom = $from;
$this->receiveDateTo = $to;
return $this;
}
public function setReportDate(string $from, ?string $to = null): self
{
$this->reportDateFrom = $from;
$this->reportDateTo = $to;
return $this;
}
protected function getStudiesQuery(): Builder
{
if ($this->archived === null) {
return Study::active();
}
return Study::query();
}
protected function applySort(Builder $query): Builder
{
if (! empty($this->sortColumns)) {
foreach ($this->sortColumns as $column => $dir) {
$query = $query->orderBy($column, $dir);
}
return $query;
}
return $query
->orderByDesc('priority')
->orderByDesc('received_at');
}
abstract protected function buildQuery(?int $user_id = null): Builder;
private function applyRadiologist(Builder $query): Builder
{
if ($this->radiologist_id != null) {
$rad = $this->radiologist_id;
$query = $query->where(function ($query) use ($rad) {
$query->Where('assigned_physician_id', '=', $rad);
$query->orWhere('reporting_physician_id', '=', $rad);
});
}
return $query;
}
private function applyStudyStatus(Builder $query): Builder
{
if ($this->studyStatus != null) {
$query = $query->where('study_status', '=', $this->studyStatus->value);
}
return $query;
}
private function applyReportStatus(Builder $query): Builder
{
if ($this->reportStatus != null) {
$query = $query->where('report_status', '=', $this->reportStatus->value);
}
return $query;
}
private function getPageSize(?int $user_id = null): int private function getPageSize(?int $user_id = null): int
{ {
return $this->perPage ?? user_per_page($user_id); return $this->perPage ?? user_per_page($user_id);
@ -260,28 +284,4 @@ private function applyDateFilters(Builder $query): Builder
return $query; return $query;
} }
public function setStudyDate(string $from, ?string $to = null): self
{
$this->studyDateFrom = $from;
$this->studyDateTo = $to;
return $this;
}
public function setReceiveDate(string $from, ?string $to = null): self
{
$this->receiveDateFrom = $from;
$this->receiveDateTo = $to;
return $this;
}
public function setReportDate(string $from, ?string $to = null): self
{
$this->reportDateFrom = $from;
$this->reportDateTo = $to;
return $this;
}
} }

View File

@ -3,7 +3,7 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\User; use App\Models\User;
use Auth; use Illuminate\Support\Facades\Auth;
use Laravel\Socialite\Facades\Socialite; use Laravel\Socialite\Facades\Socialite;
class SocialLoginController extends Controller class SocialLoginController extends Controller

View File

@ -8,6 +8,16 @@
class StudyViewerController extends HashidControllerBase class StudyViewerController extends HashidControllerBase
{ {
public function stone()
{
return $this->loadViewer(fn (Study $study) => $study->getStoneLink());
}
public function ohif()
{
return $this->loadViewer(fn (Study $study) => $study->getOhifLink());
}
private function loadViewer(\Closure $callback) private function loadViewer(\Closure $callback)
{ {
$this->decodeKeys(); $this->decodeKeys();
@ -18,14 +28,4 @@ private function loadViewer(\Closure $callback)
return view('staff.studies.viewer', compact('url', 'title')); return view('staff.studies.viewer', compact('url', 'title'));
} }
public function stone()
{
return $this->loadViewer(fn (Study $study) => $study->getStoneLink());
}
public function ohif()
{
return $this->loadViewer(fn (Study $study) => $study->getOhifLink());
}
} }

View File

@ -23,14 +23,6 @@ function () use ($token) {
return $token; return $token;
} }
public function getTokenableAttribute()
{
return Cache::remember("PersonalAccessToken::{$this->id}::tokenable", 600,
function () {
return parent::tokenable()->first();
});
}
public static function boot() public static function boot()
{ {
parent::boot(); parent::boot();
@ -50,4 +42,12 @@ public static function boot()
return false; return false;
}); });
} }
public function getTokenableAttribute()
{
return Cache::remember("PersonalAccessToken::{$this->id}::tokenable", 600,
function () {
return parent::tokenable()->first();
});
}
} }

View File

@ -22,14 +22,6 @@ public function sender(): BelongsTo
return $this->belongsTo(User::class, 'sender_id'); return $this->belongsTo(User::class, 'sender_id');
} }
protected function casts(): array
{
return [
'expires_at' => 'datetime',
'access_flags' => StudyAccessFlags::class,
];
}
public function isPasswordProtected(): bool public function isPasswordProtected(): bool
{ {
return ! blank($this->access_password); return ! blank($this->access_password);
@ -52,4 +44,12 @@ public function hasExpired(): bool
return $this->expires_at->isPast(); return $this->expires_at->isPast();
} }
protected function casts(): array
{
return [
'expires_at' => 'datetime',
'access_flags' => StudyAccessFlags::class,
];
}
} }

View File

@ -18,21 +18,6 @@ class Study extends BaseModel
{ {
use HashableId; use HashableId;
protected function casts(): array
{
return [
'is_locked' => 'boolean',
'is_archived' => 'boolean',
'study_status' => StudyLevelStatus::class,
'report_status' => ReportStatus::class,
'priority' => Priority::class,
'received_at' => 'immutable_datetime',
'reported_at' => 'immutable_datetime',
'assigned_at' => 'immutable_datetime',
'study_date' => 'immutable_datetime',
];
}
public function details(): HasOne public function details(): HasOne
{ {
return $this->hasOne(StudyDetails::class); return $this->hasOne(StudyDetails::class);
@ -268,4 +253,19 @@ public function getPriorityIcon(): string
default => '', default => '',
}; };
} }
protected function casts(): array
{
return [
'is_locked' => 'boolean',
'is_archived' => 'boolean',
'study_status' => StudyLevelStatus::class,
'report_status' => ReportStatus::class,
'priority' => Priority::class,
'received_at' => 'immutable_datetime',
'reported_at' => 'immutable_datetime',
'assigned_at' => 'immutable_datetime',
'study_date' => 'immutable_datetime',
];
}
} }

View File

@ -7,15 +7,30 @@
class StudyDetails extends BaseModel class StudyDetails extends BaseModel
{ {
use HashableId;
protected $table = 'study_details'; protected $table = 'study_details';
use HashableId; public static function historyOnly(int $studyId): self
{
return self::where('study_id', $studyId)->select(['id', 'study_id', 'clinical_history', 'surgical_history', 'lab_results', 'clinical_diagnosis'])->firstOrFail();
}
public static function seriesOnly(int $studyId): self
{
return self::where('study_id', $studyId)->select(['id', 'study_id', 'series'])->firstOrFail();
}
public function study(): BelongsTo public function study(): BelongsTo
{ {
return $this->belongsTo(Study::class); return $this->belongsTo(Study::class);
} }
public function historyIcon(): string
{
return sprintf('<i class="fa-regular fa-file-prescription %s"></i>', blank($this->clinical_history) ? 'text-muted' : 'text-success');
}
/** /**
* @return array<string, string> * @return array<string, string>
*/ */
@ -27,19 +42,4 @@ protected function casts(): array
'assignment_log' => 'array', 'assignment_log' => 'array',
]; ];
} }
public static function historyOnly(int $studyId): self
{
return self::where('study_id', $studyId)->select(['id', 'study_id', 'clinical_history', 'surgical_history', 'lab_results', 'clinical_diagnosis'])->firstOrFail();
}
public static function seriesOnly(int $studyId): self
{
return self::where('study_id', $studyId)->select(['id', 'study_id', 'series'])->firstOrFail();
}
public function historyIcon(): string
{
return sprintf('<i class="fa-regular fa-file-prescription %s"></i>', blank($this->clinical_history) ? 'text-muted' : 'text-success');
}
} }

View File

@ -113,14 +113,14 @@ public static function on(int $value, self $bit): bool
return ($value & $bit->value) === $bit->value; return ($value & $bit->value) === $bit->value;
} }
public function toString(): string
{
return self::valueToString($this->value);
}
/** @param Int32BitMask $value */ /** @param Int32BitMask $value */
public static function valueToString(int $value): string public static function valueToString(int $value): string
{ {
return '0b' . substr(chunk_split(sprintf('%\'032b', $value), 4, '_'), 0, -1); return '0b' . substr(chunk_split(sprintf('%\'032b', $value), 4, '_'), 0, -1);
} }
public function toString(): string
{
return self::valueToString($this->value);
}
} }

View File

@ -18,6 +18,16 @@ public static function hashToId(string $hash): int
return unhash_it($hash); return unhash_it($hash);
} }
public static function byHashOrFail($hash): self
{
return self::query()->byHash($hash)->firstOrFail();
}
public static function byHash($hash): ?self
{
return self::query()->byHash($hash)->first();
}
/** /**
* Get HashId column name. * Get HashId column name.
*/ */
@ -48,14 +58,4 @@ public function resolveRouteBinding($value, $field = null)
return $this->byHash($value); return $this->byHash($value);
} }
public static function byHashOrFail($hash): self
{
return self::query()->byHash($hash)->firstOrFail();
}
public static function byHash($hash): ?self
{
return self::query()->byHash($hash)->first();
}
} }

View File

@ -75,21 +75,6 @@ class User extends Authenticatable
'last_seen', 'last_seen',
]; ];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'is_active' => 'bool',
'email_verified_at' => 'datetime',
'last_seen_at' => 'datetime',
'password' => 'hashed',
];
}
public function scopeActive($query) public function scopeActive($query)
{ {
return $query->where('is_active', true); return $query->where('is_active', true);
@ -151,4 +136,19 @@ public function avatar(bool $gravatar = false): string
return (new Avatar)->create($this->full_name)->toBase64(); return (new Avatar)->create($this->full_name)->toBase64();
} }
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'is_active' => 'bool',
'email_verified_at' => 'datetime',
'last_seen_at' => 'datetime',
'password' => 'hashed',
];
}
} }

View File

@ -3,8 +3,8 @@
namespace App\Services\AuditTrail; namespace App\Services\AuditTrail;
use App\Models\Study; use App\Models\Study;
use DB;
use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Support\Facades\DB;
class ActivityLogger class ActivityLogger
{ {

View File

@ -4,8 +4,8 @@
use App\Models\Enums\NameMatchModes; use App\Models\Enums\NameMatchModes;
use App\Services\InputMatcher; use App\Services\InputMatcher;
use Cache; use Illuminate\Support\Facades\Cache;
use DB; use Illuminate\Support\Facades\DB;
final class InstituteMapper final class InstituteMapper
{ {

View File

@ -23,6 +23,13 @@ class StudiesSync
private OrthancRestClient $client; private OrthancRestClient $client;
public function __construct(?OrthancRestClient $client = null)
{
$this->study_ids = collect();
$this->client = $client ?? new OrthancRestClient;
$this->resetQueues();
}
public function execute(): void public function execute(): void
{ {
app(Pipeline::class) app(Pipeline::class)
@ -37,13 +44,6 @@ public function execute(): void
->thenReturn(); ->thenReturn();
} }
public function __construct(?OrthancRestClient $client = null)
{
$this->study_ids = collect();
$this->client = $client ?? new OrthancRestClient;
$this->resetQueues();
}
public function getClient(): OrthancRestClient public function getClient(): OrthancRestClient
{ {
return $this->client; return $this->client;

View File

@ -7,11 +7,6 @@
final readonly class UserService final readonly class UserService
{ {
private static function lastSeenKey(int $userId): string
{
return sprintf('last_seen:%d', $userId);
}
public static function setLastSeen(int $userId, ?Carbon $seenAt = null): void public static function setLastSeen(int $userId, ?Carbon $seenAt = null): void
{ {
Redis::connection()->set(self::lastSeenKey($userId), ($seenAt ?? Carbon::now())->toISOString()); Redis::connection()->set(self::lastSeenKey($userId), ($seenAt ?? Carbon::now())->toISOString());
@ -23,4 +18,9 @@ public static function getLastSeen(int $userId): ?Carbon
return $lastSeen ? Carbon::parse($lastSeen) : null; return $lastSeen ? Carbon::parse($lastSeen) : null;
} }
private static function lastSeenKey(int $userId): string
{
return sprintf('last_seen:%d', $userId);
}
} }

View File

@ -8,9 +8,6 @@
return new class extends Migration return new class extends Migration
{ {
/**
* Run the migrations.
*/
public function up(): void public function up(): void
{ {
Schema::create('users', function (Blueprint $table) { Schema::create('users', function (Blueprint $table) {
@ -50,9 +47,6 @@ public function up(): void
}); });
} }
/**
* Reverse the migrations.
*/
public function down(): void public function down(): void
{ {
Schema::dropIfExists('users'); Schema::dropIfExists('users');

View File

@ -6,9 +6,6 @@
return new class extends Migration return new class extends Migration
{ {
/**
* Run the migrations.
*/
public function up(): void public function up(): void
{ {
Schema::create('cache', function (Blueprint $table) { Schema::create('cache', function (Blueprint $table) {
@ -24,9 +21,6 @@ public function up(): void
}); });
} }
/**
* Reverse the migrations.
*/
public function down(): void public function down(): void
{ {
Schema::dropIfExists('cache'); Schema::dropIfExists('cache');

View File

@ -6,9 +6,6 @@
return new class extends Migration return new class extends Migration
{ {
/**
* Run the migrations.
*/
public function up(): void public function up(): void
{ {
Schema::create('jobs', function (Blueprint $table) { Schema::create('jobs', function (Blueprint $table) {
@ -45,9 +42,6 @@ public function up(): void
}); });
} }
/**
* Reverse the migrations.
*/
public function down(): void public function down(): void
{ {
Schema::dropIfExists('jobs'); Schema::dropIfExists('jobs');

View File

@ -7,9 +7,6 @@
return new class extends Migration return new class extends Migration
{ {
/**
* Run the migrations.
*/
public function up(): void public function up(): void
{ {
Schema::table('users', function (Blueprint $table) { Schema::table('users', function (Blueprint $table) {
@ -29,9 +26,6 @@ public function up(): void
}); });
} }
/**
* Reverse the migrations.
*/
public function down(): void public function down(): void
{ {
Schema::table('users', function (Blueprint $table) { Schema::table('users', function (Blueprint $table) {

View File

@ -6,9 +6,6 @@
return new class extends Migration return new class extends Migration
{ {
/**
* Run the migrations.
*/
public function up(): void public function up(): void
{ {
Schema::create('personal_access_tokens', function (Blueprint $table) { Schema::create('personal_access_tokens', function (Blueprint $table) {
@ -23,9 +20,6 @@ public function up(): void
}); });
} }
/**
* Reverse the migrations.
*/
public function down(): void public function down(): void
{ {
Schema::dropIfExists('personal_access_tokens'); Schema::dropIfExists('personal_access_tokens');

View File

@ -6,9 +6,6 @@
return new class extends Migration return new class extends Migration
{ {
/**
* Run the migrations.
*/
public function up(): void public function up(): void
{ {
$teams = config('permission.teams'); $teams = config('permission.teams');
@ -120,9 +117,6 @@ public function up(): void
->forget(config('permission.cache.key')); ->forget(config('permission.cache.key'));
} }
/**
* Reverse the migrations.
*/
public function down(): void public function down(): void
{ {
$tableNames = config('permission.table_names'); $tableNames = config('permission.table_names');

View File

@ -3,6 +3,6 @@
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
Route::get('/user', function (Request $request) { Route::get('user', function (Request $request) {
return $request->user(); return $request->user();
})->middleware('auth:sanctum'); })->middleware('auth:sanctum');

View File

@ -27,7 +27,7 @@
config('jetstream.auth_session'), config('jetstream.auth_session'),
'verified', 'verified',
])->group(function () { ])->group(function () {
Route::get('/dashboard', function () { Route::get('dashboard', function () {
return view('dashboard'); return view('dashboard');
})->name('dashboard'); })->name('dashboard');
@ -36,7 +36,7 @@
}); });
Route::group(['prefix' => 'radiologist', 'as' => 'radiologist.'], function () { Route::group(['prefix' => 'radiologist', 'as' => 'radiologist.'], function () {
Route::get('/report-write/{id}', ReportWriteController::class)->name('report-write'); Route::get('report-write/{id}', ReportWriteController::class)->name('report-write');
}); });
Route::group(['prefix' => 'viewer', 'as' => 'viewer.'], function () { Route::group(['prefix' => 'viewer', 'as' => 'viewer.'], function () {
@ -76,7 +76,7 @@
Route::post('auth/{hashid}', [ViewSharedStudyController::class, 'auth'])->name('auth'); Route::post('auth/{hashid}', [ViewSharedStudyController::class, 'auth'])->name('auth');
}); });
Route::view('/ck', 'ck'); Route::view('ck', 'ck');
Route::group(['prefix' => 'social', 'as' => 'social.'], function () { Route::group(['prefix' => 'social', 'as' => 'social.'], function () {
Route::get('{driver}', [SocialLoginController::class, 'redirect'])->name('login'); Route::get('{driver}', [SocialLoginController::class, 'redirect'])->name('login');