refactorings
This commit is contained in:
parent
832948984a
commit
3fa3654938
@ -5,7 +5,8 @@
|
|||||||
enum StudyLevelStatus: int
|
enum StudyLevelStatus: int
|
||||||
{
|
{
|
||||||
case Pending = 0;
|
case Pending = 0;
|
||||||
case StudyArrived = 1 << 1;
|
case Unassigned = 10;
|
||||||
case StudyLocked = 1 << 2;
|
case Assigned = 20;
|
||||||
case StudyUnlocked = 1 << 3;
|
case ReadInProgress = 30;
|
||||||
|
case ReadCompleted = 40;
|
||||||
}
|
}
|
||||||
|
@ -2,19 +2,18 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Staff;
|
namespace App\Http\Controllers\Staff;
|
||||||
|
|
||||||
use App\Domain\ACL\Permission;
|
|
||||||
use App\Domain\Report\ReportStatus;
|
use App\Domain\Report\ReportStatus;
|
||||||
use App\Http\Controllers\HashidControllerBase;
|
use App\Http\Controllers\HashidControllerBase;
|
||||||
use App\Http\Requests\StoreReportRequest;
|
use App\Http\Requests\StoreReportRequest;
|
||||||
use App\Models\Study;
|
use App\Models\Study;
|
||||||
use App\Models\StudyReport;
|
use App\Models\StudyReport;
|
||||||
use App\Services\AuditTrail\Activity;
|
use App\Services\Report\ReportManager;
|
||||||
|
|
||||||
class ReportController extends HashidControllerBase
|
class ReportController extends HashidControllerBase
|
||||||
{
|
{
|
||||||
public function popup()
|
public function popup()
|
||||||
{
|
{
|
||||||
abort_unless(me()->may([Permission::ReportEdit, Permission::ReportDictate, Permission::ReportApprove, Permission::ReportDownload]), 403);
|
ReportManager::ensureDownloadAccess();
|
||||||
$this->decodeKeys();
|
$this->decodeKeys();
|
||||||
$study = Study::with(['reports.radiologist', 'reports.study', 'assignedPhysicians'])->findOrFail($this->key);
|
$study = Study::with(['reports.radiologist', 'reports.study', 'assignedPhysicians'])->findOrFail($this->key);
|
||||||
if (me()->isRadiologist()) {
|
if (me()->isRadiologist()) {
|
||||||
@ -28,104 +27,60 @@ public function popup()
|
|||||||
|
|
||||||
public function save(StoreReportRequest $request)
|
public function save(StoreReportRequest $request)
|
||||||
{
|
{
|
||||||
abort_unless(me()->may([Permission::ReportEdit, Permission::ReportDictate, Permission::ReportApprove]), 403);
|
ReportManager::ensureEditAccess();
|
||||||
$this->decodeKeys();
|
$this->decodeKeys();
|
||||||
$study = Study::findOrFail($this->key);
|
$manager = ReportManager::make($this->key);
|
||||||
$reportStatus = ReportStatus::from($request->integer('report_status'));
|
$reportStatus = ReportStatus::from($request->integer('report_status'));
|
||||||
|
$report = $manager->createReport(request('content'), $reportStatus);
|
||||||
$report = StudyReport::make([
|
|
||||||
'study_id' => $study->id,
|
|
||||||
'institute_id' => $study->institute_id,
|
|
||||||
'facility_id' => $study->facility_id,
|
|
||||||
'report_status' => $reportStatus->value,
|
|
||||||
'read_by_id' => me()->id,
|
|
||||||
]);
|
|
||||||
$report->saveContent(request('content'));
|
|
||||||
$report->save();
|
|
||||||
$report->refresh();
|
|
||||||
|
|
||||||
audit()
|
|
||||||
->on($study)
|
|
||||||
->did($reportStatus->value >= ReportStatus::Finalized->value ? Activity::Report_Finalize : Activity::Report_Save)
|
|
||||||
->notes($report->accession_number)
|
|
||||||
->log();
|
|
||||||
|
|
||||||
if ($reportStatus->value === ReportStatus::Finalized->value) {
|
if ($reportStatus->value === ReportStatus::Finalized->value) {
|
||||||
$report->setStatus($reportStatus);
|
$manager->finalizeReport($report);
|
||||||
|
|
||||||
$study->setReportStatus($reportStatus);
|
|
||||||
audit()
|
|
||||||
->on($study)
|
|
||||||
->did(Activity::Report_Finalize)
|
|
||||||
->log();
|
|
||||||
|
|
||||||
$study->unlockStudy();
|
|
||||||
audit()
|
|
||||||
->on($study)
|
|
||||||
->did(Activity::Study_Unlock)
|
|
||||||
->log();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('staff.reports.close-window');
|
return view('staff.content.pages.close-window');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create()
|
public function create()
|
||||||
{
|
{
|
||||||
abort_unless(me()->may([Permission::ReportEdit, Permission::ReportDictate, Permission::ReportApprove]), 403);
|
ReportManager::ensureEditAccess();
|
||||||
$this->decodeKeys();
|
$this->decodeKeys();
|
||||||
$study = Study::findOrFail($this->key);
|
$manager = ReportManager::make($this->key);
|
||||||
if (! $study->canEditReport()) {
|
|
||||||
return view('content.pages.notice', [
|
$view = $manager->check();
|
||||||
'color' => 'warning',
|
if ($view) {
|
||||||
'title' => 'Read Completed',
|
return $view;
|
||||||
'heading' => 'Read Completed',
|
|
||||||
'message' => 'Reading has been completed already.',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! $study->canObtainLock()) {
|
$manager->lockStudyIfRequired();
|
||||||
return view('content.pages.notice', [
|
$study = $manager->getStudy();
|
||||||
'color' => 'danger',
|
$report = $manager->latestReport();
|
||||||
'title' => 'Study Locked',
|
|
||||||
'heading' => 'Study Locked',
|
|
||||||
'message' => 'Study is locked by another user.',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($study->isUnlocked()) {
|
|
||||||
$study->lockStudy();
|
|
||||||
audit()
|
|
||||||
->on($study)
|
|
||||||
->did(Activity::Study_Lock)
|
|
||||||
->log();
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: study_status: Read in Progress
|
// todo: study_status: Read in Progress
|
||||||
|
|
||||||
$report = StudyReport::forStudy($study)
|
|
||||||
->where('report_status', ReportStatus::Preliminary->value)
|
|
||||||
->select(['id', 'accession_number', 'file_path'])
|
|
||||||
->latest()
|
|
||||||
->first();
|
|
||||||
|
|
||||||
return view('staff.reports.create', compact('study', 'report'));
|
return view('staff.reports.create', compact('study', 'report'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function edit(string $uuid)
|
public function edit(string $uuid)
|
||||||
{
|
{
|
||||||
abort_unless(me()->may([Permission::ReportEdit, Permission::ReportDictate, Permission::ReportApprove]), 403);
|
ReportManager::ensureEditAccess();
|
||||||
$report = StudyReport::with(['study', 'radiologist'])->where('accession_number', $uuid)->firstOrFail();
|
$report = StudyReport::with(['study_id', 'id'])->accession($uuid)->firstOrFail();
|
||||||
$study = $report->study;
|
$manager = ReportManager::make($report->study_id);
|
||||||
$title = 'View Report';
|
|
||||||
$close = false;
|
|
||||||
|
|
||||||
return view('staff.reports.create', compact('study', 'report', 'close'));
|
$view = $manager->check();
|
||||||
|
if ($view) {
|
||||||
|
return $view;
|
||||||
|
}
|
||||||
|
|
||||||
|
$manager->lockStudyIfRequired();
|
||||||
|
$study = $manager->getStudy();
|
||||||
|
|
||||||
|
return view('staff.reports.create', compact('study', 'report'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function view(string $uuid)
|
public function view(string $uuid)
|
||||||
{
|
{
|
||||||
abort_unless(me()->may([Permission::ReportEdit, Permission::ReportDictate, Permission::ReportApprove, Permission::ReportDownload]), 403);
|
ReportManager::ensureDownloadAccess();
|
||||||
$report = StudyReport::with(['study', 'radiologist'])->where('accession_number', $uuid)->firstOrFail();
|
$report = StudyReport::with(['study_id', 'id'])->accession($uuid)->firstOrFail();
|
||||||
$title = 'View Report';
|
$title = 'View Report';
|
||||||
|
|
||||||
return view('staff.reports.viewer.html-report', compact('report', 'title'));
|
return view('staff.reports.viewer.html-report', compact('report', 'title'));
|
||||||
|
@ -338,14 +338,17 @@ public function isUnlocked(): bool
|
|||||||
return $this->locked_at === null;
|
return $this->locked_at === null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function lockStudy(User|int|null $user = null): void
|
public function lockStudy(User|int|null $user = null, ?StudyLevelStatus $status = null): void
|
||||||
{
|
{
|
||||||
$this->update(
|
$params = [
|
||||||
[
|
|
||||||
'locking_physician_id' => me($user)->id,
|
'locking_physician_id' => me($user)->id,
|
||||||
'locked_at' => now(),
|
'locked_at' => now(),
|
||||||
]
|
];
|
||||||
);
|
if ($status) {
|
||||||
|
$params['study_status'] = $status->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->update($params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function unlockStudy(): void
|
public function unlockStudy(): void
|
||||||
|
@ -29,6 +29,13 @@ public function approver(): BelongsTo
|
|||||||
return $this->belongsTo(User::class, 'approved_by_id');
|
return $this->belongsTo(User::class, 'approved_by_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function scopeAccession(Builder $query, string $uuid): Builder
|
||||||
|
{
|
||||||
|
$query->wherewhere('accession_number', $uuid);
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
public function scopeForStudy(Builder $query, Study|int $study): Builder
|
public function scopeForStudy(Builder $query, Study|int $study): Builder
|
||||||
{
|
{
|
||||||
$query->where('study_id', $study instanceof Study ? $study->id : $study);
|
$query->where('study_id', $study instanceof Study ? $study->id : $study);
|
||||||
|
@ -41,7 +41,7 @@ private function checkUpdate(string $orthanc_uuid, StudiesSync $sync, Collection
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($study_status < StudyLevelStatus::StudyArrived->value) {
|
if ($study_status < StudyLevelStatus::Unassigned->value) {
|
||||||
$sync->getUpdateQueue()->add($orthanc_uuid);
|
$sync->getUpdateQueue()->add($orthanc_uuid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@ public function transformData(mixed $orthanc_src): array
|
|||||||
];
|
];
|
||||||
|
|
||||||
if ((bool) data_get($orthanc_src, 'IsStable', false)) {
|
if ((bool) data_get($orthanc_src, 'IsStable', false)) {
|
||||||
$study['study_status'] = StudyLevelStatus::StudyArrived->value;
|
$study['study_status'] = StudyLevelStatus::Unassigned->value;
|
||||||
} else {
|
} else {
|
||||||
$study['study_status'] = StudyLevelStatus::Pending->value;
|
$study['study_status'] = StudyLevelStatus::Pending->value;
|
||||||
}
|
}
|
||||||
|
@ -2,4 +2,128 @@
|
|||||||
|
|
||||||
namespace App\Services\Report;
|
namespace App\Services\Report;
|
||||||
|
|
||||||
final class ReportManager {}
|
use App\Domain\ACL\Permission;
|
||||||
|
use App\Domain\Report\ReportStatus;
|
||||||
|
use App\Models\Study;
|
||||||
|
use App\Models\StudyReport;
|
||||||
|
use App\Services\AuditTrail\Activity;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
final class ReportManager
|
||||||
|
{
|
||||||
|
public function __construct(private readonly Study $study) {}
|
||||||
|
|
||||||
|
public static function make(int $study_id): static
|
||||||
|
{
|
||||||
|
return new self(Study::findOrFail($study_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReports(): Collection
|
||||||
|
{
|
||||||
|
return $this->study->reports->sortByDesc('created_at');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createReport(string $content, ReportStatus $status): StudyReport
|
||||||
|
{
|
||||||
|
$report = StudyReport::make([
|
||||||
|
'study_id' => $this->study->id,
|
||||||
|
'institute_id' => $this->study->institute_id,
|
||||||
|
'facility_id' => $this->study->facility_id,
|
||||||
|
'report_status' => $status->value,
|
||||||
|
'read_by_id' => me()->id,
|
||||||
|
]);
|
||||||
|
$report->saveContent($content);
|
||||||
|
$report->save();
|
||||||
|
$report->refresh();
|
||||||
|
|
||||||
|
audit()
|
||||||
|
->on($this->study)
|
||||||
|
->did(Activity::Report_Save)
|
||||||
|
->notes($report->accession_number)
|
||||||
|
->log();
|
||||||
|
|
||||||
|
return $report;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function finalizeReport(StudyReport $report): void
|
||||||
|
{
|
||||||
|
$report->setStatus(ReportStatus::Finalized);
|
||||||
|
audit()
|
||||||
|
->on($this->study)
|
||||||
|
->did(Activity::Report_Finalize)
|
||||||
|
->notes($report->accession_number)
|
||||||
|
->log();
|
||||||
|
|
||||||
|
$this->study->setReportStatus(ReportStatus::Finalized);
|
||||||
|
|
||||||
|
audit()
|
||||||
|
->on($this->study)
|
||||||
|
->did(Activity::Report_Finalize)
|
||||||
|
->log();
|
||||||
|
|
||||||
|
$this->study->unlockStudy();
|
||||||
|
audit()
|
||||||
|
->on($this->study)
|
||||||
|
->did(Activity::Study_Unlock)
|
||||||
|
->log();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function check(): ?View
|
||||||
|
{
|
||||||
|
if (! $this->study->canEditReport()) {
|
||||||
|
return view('content.pages.notice', [
|
||||||
|
'color' => 'warning',
|
||||||
|
'title' => 'Read Completed',
|
||||||
|
'heading' => 'Read Completed',
|
||||||
|
'message' => 'The report has been finalized, and the study is now closed for any further modifications.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $this->study->canObtainLock()) {
|
||||||
|
return view('content.pages.notice', [
|
||||||
|
'color' => 'danger',
|
||||||
|
'title' => 'Study Locked',
|
||||||
|
'heading' => 'Study Locked',
|
||||||
|
'message' => 'Study is locked by another user.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function lockStudyIfRequired(): void
|
||||||
|
{
|
||||||
|
if ($this->study->isUnlocked()) {
|
||||||
|
$this->study->lockStudy();
|
||||||
|
audit()
|
||||||
|
->on($this->study)
|
||||||
|
->did(Activity::Study_Lock)
|
||||||
|
->log();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function latestReport(): ?StudyReport
|
||||||
|
{
|
||||||
|
return StudyReport::forStudy($this->study)
|
||||||
|
->where('report_status', ReportStatus::Preliminary->value)
|
||||||
|
->select(['id', 'accession_number', 'file_path'])
|
||||||
|
->latest()
|
||||||
|
->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStudy(): Study
|
||||||
|
{
|
||||||
|
return $this->study;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function ensureDownloadAccess(): void
|
||||||
|
{
|
||||||
|
abort_unless(me()->may([Permission::ReportEdit, Permission::ReportDictate, Permission::ReportApprove, Permission::ReportDownload]), 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function ensureEditAccess(): void
|
||||||
|
{
|
||||||
|
abort_unless(me()->may([Permission::ReportEdit, Permission::ReportDictate, Permission::ReportApprove]), 403);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,16 +3,16 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Rubik:wght@400..600&display=swap" rel="stylesheet">
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||||
crossorigin="anonymous">
|
crossorigin="anonymous">
|
||||||
<title>{{ $title }}</title>
|
<title>{{ $title }}</title>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
margin-top: 20px;
|
margin-top: 60px;
|
||||||
}
|
font-family: "Rubik", serif;
|
||||||
|
font-optical-sizing: auto;
|
||||||
body {
|
font-weight: 400;
|
||||||
padding: 50px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert-coupon {
|
.alert-coupon {
|
||||||
@ -20,8 +20,9 @@
|
|||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1, h2, h3, h4, h5, h6 {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
line-height: 1.5;
|
line-height: 1.6;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
p:last-child {
|
p:last-child {
|
||||||
@ -34,7 +35,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-7 col-md-7 d-flex justify-content-center">
|
||||||
<div class="alert alert-{{ $color }} alert-coupon">
|
<div class="alert alert-{{ $color }} alert-coupon">
|
||||||
<h4>{{ $heading }}</h4>
|
<h4>{{ $heading }}</h4>
|
||||||
<p>{{ $message }}</p>
|
<p>{{ $message }}</p>
|
||||||
|
Loading…
Reference in New Issue
Block a user