refactorings
This commit is contained in:
parent
832948984a
commit
3fa3654938
@ -5,7 +5,8 @@
|
||||
enum StudyLevelStatus: int
|
||||
{
|
||||
case Pending = 0;
|
||||
case StudyArrived = 1 << 1;
|
||||
case StudyLocked = 1 << 2;
|
||||
case StudyUnlocked = 1 << 3;
|
||||
case Unassigned = 10;
|
||||
case Assigned = 20;
|
||||
case ReadInProgress = 30;
|
||||
case ReadCompleted = 40;
|
||||
}
|
||||
|
@ -2,19 +2,18 @@
|
||||
|
||||
namespace App\Http\Controllers\Staff;
|
||||
|
||||
use App\Domain\ACL\Permission;
|
||||
use App\Domain\Report\ReportStatus;
|
||||
use App\Http\Controllers\HashidControllerBase;
|
||||
use App\Http\Requests\StoreReportRequest;
|
||||
use App\Models\Study;
|
||||
use App\Models\StudyReport;
|
||||
use App\Services\AuditTrail\Activity;
|
||||
use App\Services\Report\ReportManager;
|
||||
|
||||
class ReportController extends HashidControllerBase
|
||||
{
|
||||
public function popup()
|
||||
{
|
||||
abort_unless(me()->may([Permission::ReportEdit, Permission::ReportDictate, Permission::ReportApprove, Permission::ReportDownload]), 403);
|
||||
ReportManager::ensureDownloadAccess();
|
||||
$this->decodeKeys();
|
||||
$study = Study::with(['reports.radiologist', 'reports.study', 'assignedPhysicians'])->findOrFail($this->key);
|
||||
if (me()->isRadiologist()) {
|
||||
@ -28,104 +27,60 @@ public function popup()
|
||||
|
||||
public function save(StoreReportRequest $request)
|
||||
{
|
||||
abort_unless(me()->may([Permission::ReportEdit, Permission::ReportDictate, Permission::ReportApprove]), 403);
|
||||
ReportManager::ensureEditAccess();
|
||||
$this->decodeKeys();
|
||||
$study = Study::findOrFail($this->key);
|
||||
$manager = ReportManager::make($this->key);
|
||||
$reportStatus = ReportStatus::from($request->integer('report_status'));
|
||||
|
||||
$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();
|
||||
$report = $manager->createReport(request('content'), $reportStatus);
|
||||
|
||||
if ($reportStatus->value === ReportStatus::Finalized->value) {
|
||||
$report->setStatus($reportStatus);
|
||||
|
||||
$study->setReportStatus($reportStatus);
|
||||
audit()
|
||||
->on($study)
|
||||
->did(Activity::Report_Finalize)
|
||||
->log();
|
||||
|
||||
$study->unlockStudy();
|
||||
audit()
|
||||
->on($study)
|
||||
->did(Activity::Study_Unlock)
|
||||
->log();
|
||||
$manager->finalizeReport($report);
|
||||
}
|
||||
|
||||
return view('staff.reports.close-window');
|
||||
return view('staff.content.pages.close-window');
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
abort_unless(me()->may([Permission::ReportEdit, Permission::ReportDictate, Permission::ReportApprove]), 403);
|
||||
ReportManager::ensureEditAccess();
|
||||
$this->decodeKeys();
|
||||
$study = Study::findOrFail($this->key);
|
||||
if (! $study->canEditReport()) {
|
||||
return view('content.pages.notice', [
|
||||
'color' => 'warning',
|
||||
'title' => 'Read Completed',
|
||||
'heading' => 'Read Completed',
|
||||
'message' => 'Reading has been completed already.',
|
||||
]);
|
||||
$manager = ReportManager::make($this->key);
|
||||
|
||||
$view = $manager->check();
|
||||
if ($view) {
|
||||
return $view;
|
||||
}
|
||||
|
||||
if (! $study->canObtainLock()) {
|
||||
return view('content.pages.notice', [
|
||||
'color' => 'danger',
|
||||
'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();
|
||||
}
|
||||
$manager->lockStudyIfRequired();
|
||||
$study = $manager->getStudy();
|
||||
$report = $manager->latestReport();
|
||||
|
||||
// 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'));
|
||||
}
|
||||
|
||||
public function edit(string $uuid)
|
||||
{
|
||||
abort_unless(me()->may([Permission::ReportEdit, Permission::ReportDictate, Permission::ReportApprove]), 403);
|
||||
$report = StudyReport::with(['study', 'radiologist'])->where('accession_number', $uuid)->firstOrFail();
|
||||
$study = $report->study;
|
||||
$title = 'View Report';
|
||||
$close = false;
|
||||
ReportManager::ensureEditAccess();
|
||||
$report = StudyReport::with(['study_id', 'id'])->accession($uuid)->firstOrFail();
|
||||
$manager = ReportManager::make($report->study_id);
|
||||
|
||||
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)
|
||||
{
|
||||
abort_unless(me()->may([Permission::ReportEdit, Permission::ReportDictate, Permission::ReportApprove, Permission::ReportDownload]), 403);
|
||||
$report = StudyReport::with(['study', 'radiologist'])->where('accession_number', $uuid)->firstOrFail();
|
||||
ReportManager::ensureDownloadAccess();
|
||||
$report = StudyReport::with(['study_id', 'id'])->accession($uuid)->firstOrFail();
|
||||
$title = 'View Report';
|
||||
|
||||
return view('staff.reports.viewer.html-report', compact('report', 'title'));
|
||||
|
@ -338,14 +338,17 @@ public function isUnlocked(): bool
|
||||
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,
|
||||
'locked_at' => now(),
|
||||
]
|
||||
);
|
||||
];
|
||||
if ($status) {
|
||||
$params['study_status'] = $status->value;
|
||||
}
|
||||
|
||||
$this->update($params);
|
||||
}
|
||||
|
||||
public function unlockStudy(): void
|
||||
|
@ -29,6 +29,13 @@ public function approver(): BelongsTo
|
||||
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
|
||||
{
|
||||
$query->where('study_id', $study instanceof Study ? $study->id : $study);
|
||||
|
@ -41,7 +41,7 @@ private function checkUpdate(string $orthanc_uuid, StudiesSync $sync, Collection
|
||||
return;
|
||||
}
|
||||
|
||||
if ($study_status < StudyLevelStatus::StudyArrived->value) {
|
||||
if ($study_status < StudyLevelStatus::Unassigned->value) {
|
||||
$sync->getUpdateQueue()->add($orthanc_uuid);
|
||||
}
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ public function transformData(mixed $orthanc_src): array
|
||||
];
|
||||
|
||||
if ((bool) data_get($orthanc_src, 'IsStable', false)) {
|
||||
$study['study_status'] = StudyLevelStatus::StudyArrived->value;
|
||||
$study['study_status'] = StudyLevelStatus::Unassigned->value;
|
||||
} else {
|
||||
$study['study_status'] = StudyLevelStatus::Pending->value;
|
||||
}
|
||||
|
@ -2,4 +2,128 @@
|
||||
|
||||
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>
|
||||
<meta charset="UTF-8">
|
||||
<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"
|
||||
crossorigin="anonymous">
|
||||
<title>{{ $title }}</title>
|
||||
<style>
|
||||
body {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 50px;
|
||||
margin-top: 60px;
|
||||
font-family: "Rubik", serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.alert-coupon {
|
||||
@ -20,8 +20,9 @@
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-size: 20px;
|
||||
line-height: 1.5;
|
||||
line-height: 1.6;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
@ -34,7 +35,7 @@
|
||||
<div class="container">
|
||||
|
||||
<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">
|
||||
<h4>{{ $heading }}</h4>
|
||||
<p>{{ $message }}</p>
|
||||
|
Loading…
Reference in New Issue
Block a user