Skip to content

Commit

Permalink
Merge pull request #702 from Pixilib/GaelO2-dev
Browse files Browse the repository at this point in the history
Gael o2 dev
  • Loading branch information
salimkanoun committed Nov 30, 2023
2 parents 2f744e4 + 90a16dc commit 1172a2f
Show file tree
Hide file tree
Showing 26 changed files with 173 additions and 159 deletions.
7 changes: 6 additions & 1 deletion GaelO2/app/GaelO/Adapters/FrameworkAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\GaelO\Adapters;

use App\GaelO\Exceptions\GaelOException;
use App\GaelO\Interfaces\Adapters\FrameworkInterface;
use App\Models\User;
use Illuminate\Auth\Events\Registered;
Expand Down Expand Up @@ -47,7 +48,11 @@ public static function deleteFile(string $path): void

public static function getFile(string $path): string
{
return Storage::get($path);
$file = Storage::get($path);
if($file === null){
throw new GaelOException("File not found in storage");
}
return $file;
}

public static function sendRegisteredEventForEmailVerification(int $userId): void
Expand Down
4 changes: 2 additions & 2 deletions GaelO2/app/GaelO/Adapters/JobAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ public function sendQcReportJob(int $visitId): void
JobQcReport::dispatch($visitId);
}

public function sendRadiomicsReport(int $visitId, int $behalfUserId): void
public function sendRadiomicsReport(int $visitId, ?int $behalfUserId, ?array $destinatorEmails): void
{
JobRadiomicsReport::dispatch($visitId, $behalfUserId);
JobRadiomicsReport::dispatch($visitId, $behalfUserId, $destinatorEmails);
}
}
2 changes: 1 addition & 1 deletion GaelO2/app/GaelO/Interfaces/Adapters/JobInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
interface JobInterface
{
public function sendQcReportJob(int $visitId) : void;
public function sendRadiomicsReport(int $visitId, int $behalfUserId) :void;
public function sendRadiomicsReport(int $visitId, ?int $behalfUserId, ?array $destinatorEmails) :void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public function createSeriesFromOrthanc(string $orthancSeriesId, bool $pet = fal
return $request->getBody();
}

public function createDicom(string $filename){
public function createDicom(string $filename)
{
$request = $this->httpClientInterface->uploadFile('POST', "/dicoms", $filename);
return $request->getBody();
}
Expand Down Expand Up @@ -154,6 +155,12 @@ public function getStatsMask(string $maskId): array
return $request->getJsonBody();
}

public function getStatsMaskSeries(string $maskId, string $seriesId): array
{
$request = $this->httpClientInterface->requestJson('POST', "/tools/stats-mask-image", ["seriesId" => $seriesId, "maskId" => $maskId]);
return $request->getJsonBody();
}

public function deleteRessource(string $type, string $id): void
{
$request = $this->httpClientInterface->requestJson('DELETE', "/" . $type . "/" . $id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ class ExpectedPatient
{
public string $code;
public int $centerCode;
public string $inclusionStatus;
public ?string $inclusionStatus;

public function __construct(string $code, int $centerCode, string $inclusionStatus)
public function __construct(string $code, int $centerCode = null, ?string $inclusionStatus = null)
{
$this->code = $code;
$this->centerCode = $centerCode;
Expand Down
8 changes: 2 additions & 6 deletions GaelO2/app/GaelO/Services/MailServices.php
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,7 @@ public function sendQcReport(string $studyName, string $visitType, string $patie
$this->mailInterface->send();
}

public function sendRadiomicsReport(string $studyName, string $patientCode, string $visitType, string $visitDate, string $imagePath, array $stats, int $uploaderId)
public function sendRadiomicsReport(string $studyName, string $patientCode, string $visitType, string $visitDate, string $imagePath, array $stats, array $emailList)
{
$parameters = [
'patientCode' => $patientCode,
Expand All @@ -606,11 +606,7 @@ public function sendRadiomicsReport(string $studyName, string $patientCode, stri
'stats' => $stats
];

$mailListBuilder = new MailListBuilder($this->userRepositoryInterface, $this->studyRepositoryInterface);
$mailListBuilder->withUserEmail($uploaderId)
->withUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR);

$this->mailInterface->setTo($mailListBuilder->get());
$this->mailInterface->setTo($emailList);
$this->mailInterface->setReplyTo($this->getStudyContactEmail($studyName));
$this->mailInterface->setParameters($parameters);
$this->mailInterface->setBody(MailConstants::EMAIL_RADIOMICS_REPORT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class CreateFileToVisitRequest
public int $visitId;
public string $studyName;
public string $key;
public string $contentType;
public ?string $contentType = null;
public string $content;
public ?string $extension = null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,17 @@ private function checkAuthorization(int $userId, int $visitId, string $role, str
{
$qcStatus = $visitContext['state_quality_control'];

//If QC is done, can't reactivate series
if (in_array($qcStatus, [QualityControlStateEnum::ACCEPTED->value, QualityControlStateEnum::REFUSED->value, QualityControlStateEnum::NOT_NEEDED->value])) {
throw new GaelOForbiddenException();
//If QC is performed, can't reactivate series
if (in_array($qcStatus, [QualityControlStateEnum::ACCEPTED->value, QualityControlStateEnum::REFUSED->value])) {
throw new GaelOForbiddenException("Can't reactivate series on QC performed visit");
}

//If QC is not needed we allow supervisor to reactivate series (reactivation after a dicom study delete)
if($qcStatus === QualityControlStateEnum::NOT_NEEDED->value && $role !== Constants::ROLE_SUPERVISOR){
throw new GaelOForbiddenException("In case of not needed QC only supervisor can reactivate series");
}

//We deny access to all other of than investigator, controller or supervisor
if (!in_array($role, [Constants::ROLE_INVESTIGATOR, Constants::ROLE_CONTROLLER, Constants::ROLE_SUPERVISOR])) {
throw new GaelOForbiddenException();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ public function execute(ValidateDicomUploadRequest $validateDicomUploadRequest,
{

try {
//Run as a background task even if the user leave the website
ignore_user_abort(true);

//Initialize unzipPath to avoid null pointer in catch
$unzipedPath = null;
//Retrieve Visit Context
Expand Down
9 changes: 4 additions & 5 deletions GaelO2/app/GaelO/views/mails/mail_conclusion.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

@section('content')
Review of the following visit is concluded <br>
Study : {{$study}}<br>
Patient Code : {{$patientCode}}
Visit : {{$visitType}}<br>
Conclusion Value : {{$conclusionValue}}
Study : {{ $study }}<br>
Patient Code : {{ $patientCode }}
Visit : {{ $visitType }}<br>
<b>Conclusion Value : {{ $conclusionValue }}</b>
@endsection

24 changes: 24 additions & 0 deletions GaelO2/app/GaelO/views/mails/mail_radiomics_report.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,30 @@
</table>
</div>
<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
<div style="margin:0px auto;max-width:600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
<tbody>
<tr>
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
<tbody>
<tr>
<td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:13px;line-height:16px;text-align:center;color:#000000;">For Investigational use only<br> These automatic calculations shall be validated by a trained physician<br></div>
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
<div style="margin:0px auto;max-width:600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
<tbody>
Expand Down
8 changes: 8 additions & 0 deletions GaelO2/app/GaelO/views/mails/mjml/radiomics_report.mjml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@
</mj-table>
</mj-column>
</mj-section>
<mj-section>
<mj-column>
<mj-text align="center" line-height="16px">
For Investigational use only<br>
These automatic calculations shall be validated by a trained physician<br>
</mj-text>
</mj-column>
</mj-section>
<mj-section>
<mj-column>
<mj-divider border-width="1px" border-color="#616161"></mj-divider>
Expand Down
9 changes: 7 additions & 2 deletions GaelO2/app/Http/Controllers/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@ class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;

public function getJsonResponse($body, int $statusCode, string $statusText)
public function getJsonResponse($body, int $statusCode, string $statusText, bool $emptyArrayAsObject = false)
{
if ($body === null) return response()->noContent()->setStatusCode($statusCode, $statusText);
else return response()->json($body)->setStatusCode($statusCode, $statusText);
else {
if (is_array($body) && sizeof($body) === 0 && $emptyArrayAsObject) {
$body = (object) [];
}
return response()->json($body)->setStatusCode($statusCode, $statusText);
};
}
}
4 changes: 2 additions & 2 deletions GaelO2/app/Http/Controllers/ReviewController.php
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ public function getAssociatedDataOfVisitForReviewer(GetAssociatedDataForReview $

$getAssociatedDataForReview->execute($getAssociatedDataForReviewRequest, $getAssociatedDataForReviewResponse);

return $this->getJsonResponse($getAssociatedDataForReviewResponse->body, $getAssociatedDataForReviewResponse->status, $getAssociatedDataForReviewResponse->statusText);
return $this->getJsonResponse($getAssociatedDataForReviewResponse->body, $getAssociatedDataForReviewResponse->status, $getAssociatedDataForReviewResponse->statusText, true);
}

public function getAssociatedDataOfVisitForInvestigator(Request $request, GetAssociatedDataForInvestigator $getAssociatedDataForInvestigator, GetAssociatedDataForInvestigatorRequest $getAssociatedDataForInvestigatorRequest, GetAssociatedDataForInvestigatorResponse $getAssociatedDataForInvestigatorResponse, int $visitId)
Expand All @@ -314,6 +314,6 @@ public function getAssociatedDataOfVisitForInvestigator(Request $request, GetAss

$getAssociatedDataForInvestigator->execute($getAssociatedDataForInvestigatorRequest, $getAssociatedDataForInvestigatorResponse);

return $this->getJsonResponse($getAssociatedDataForInvestigatorResponse->body, $getAssociatedDataForInvestigatorResponse->status, $getAssociatedDataForInvestigatorResponse->statusText);
return $this->getJsonResponse($getAssociatedDataForInvestigatorResponse->body, $getAssociatedDataForInvestigatorResponse->status, $getAssociatedDataForInvestigatorResponse->statusText, true);
}
}
3 changes: 3 additions & 0 deletions GaelO2/app/Jobs/JobQcReport.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,5 +163,8 @@ public function failed(Throwable $exception)
{
$mailServices = App::make(MailServices::class);
$mailServices->sendJobFailure('QcReport', ['visitId' => $this->visitId], $exception->getMessage());
if (app()->bound('sentry')) {
app('sentry')->captureException($exception);
}
}
}
61 changes: 43 additions & 18 deletions GaelO2/app/Jobs/JobRadiomicsReport.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use App\GaelO\Exceptions\GaelOException;
use App\GaelO\Interfaces\Repositories\DicomStudyRepositoryInterface;
use App\GaelO\Interfaces\Repositories\VisitRepositoryInterface;
use App\GaelO\Repositories\StudyRepository;
use App\GaelO\Services\GaelOClientService;
use App\GaelO\Services\GaelOProcessingService\GaelOProcessingService;
use App\GaelO\Services\MailServices;
Expand All @@ -19,7 +20,6 @@
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Log;
use DateTime;
use Exception;
use Throwable;
Expand All @@ -32,20 +32,23 @@ class JobRadiomicsReport implements ShouldQueue, ShouldBeUnique
public $timeout = 1200;
public $tries = 1;
private int $visitId;
private int $behalfUserId;
private ?int $behalfUserId;
private ?array $destinatorEmails;
private array $createdFiles = [];
private GaelOProcessingService $gaelOProcessingService;
private OrthancService $orthancService;

public function __construct(int $visitId, int $behalfUserId)
public function __construct(int $visitId, ?int $behalfUserId, ?array $destinatorEmails)
{
$this->onQueue('processing');
$this->visitId = $visitId;
$this->behalfUserId = $behalfUserId;
$this->destinatorEmails = $destinatorEmails;
}

public function handle(
VisitRepositoryInterface $visitRepositoryInterface,
StudyRepository $studyRepository,
DicomStudyRepositoryInterface $dicomStudyRepositoryInterface,
OrthancService $orthancService,
GaelOProcessingService $gaelOProcessingService,
Expand Down Expand Up @@ -112,18 +115,28 @@ public function handle(
$maskdicom = $this->gaelOProcessingService->getMaskDicomOrientation($fragmentedMaskId, 'LPI', true);

#get Stats
$stats = $this->gaelOProcessingService->getStatsMask($threshold41MaskId);
$statValue = ['tmtv 41%' => $stats['volume'], 'DmaxVox' => $stats['dMax']];
$stats = $this->gaelOProcessingService->getStatsMaskSeries($threshold41MaskId, $idPT);
$statValue = [
'TMTV 41%' => $stats['volume'],
'Dmax (voxel)' => $stats['dmax'],
'SUVmax' => $stats['suvmax'],
'SUVmean'=> $stats['suvmean'],
'SUVpeak' => $stats['suvpeak'],
'TLG' => $stats['tlg'],
'Dmax Bulk' => $stats['dmaxbulk'],
];

$mailServices->sendRadiomicsReport(
$studyName,
$patientCode,
$visitType,
$formattedVisitDate,
$mipMask,
$statValue,
$creatorUserId
);
if($this->destinatorEmails){
$mailServices->sendRadiomicsReport(
$studyName,
$patientCode,
$visitType,
$formattedVisitDate,
$mipMask,
$statValue,
$this->destinatorEmails
);
}

$pdfReport = $pdfServices->saveRadiomicsPdf(
$studyName,
Expand All @@ -134,7 +147,13 @@ public function handle(
);

//Send file to store using API as job worker may not access to the storage backend
$user = User::find($this->behalfUserId);
if ($this->behalfUserId) {
$user = User::find($this->behalfUserId);
} else {
$studyEntity = $studyRepository->find($studyName);
$user = User::where('email', $studyEntity->contactEmail)->sole();
}

$tokenResult = $user->createToken('GaelO')->plainTextToken;
$gaeloClientService->loadUrl();
$gaeloClientService->setAuthorizationToken($tokenResult);
Expand All @@ -159,9 +178,7 @@ public function handle(
private function sendDicomToProcessing(string $orthancSeriesIdPt)
{
$temporaryZipDicom = tempnam(ini_get('upload_tmp_dir'), 'TMP_Inference_');
$temporaryZipDicomHandle = fopen($temporaryZipDicom, 'r+');

$this->orthancService->getZipStreamToFile([$orthancSeriesIdPt], $temporaryZipDicomHandle);
$this->orthancService->getZipStreamToFile([$orthancSeriesIdPt], $temporaryZipDicom);
$this->gaelOProcessingService->createDicom($temporaryZipDicom);
$this->addCreatedRessource('dicoms', $orthancSeriesIdPt);

Expand All @@ -183,6 +200,11 @@ private function getSeriesOrthancIds(array $dicomStudyEntity)
}
}

if (!$idPT || !$idCT) {
//Can happen in case of a study reactivation, at reactivation series are softdeleted so we won't run the inference
throw new GaelOException("Didn't found CT and PT Series to run the inference");
}

return [
'orthancSeriesIdPt' => $idPT,
'orthancSeriesIdCt' => $idCT
Expand All @@ -208,5 +230,8 @@ public function failed(Throwable $exception)
{
$mailServices = App::make(MailServices::class);
$mailServices->sendJobFailure('RadiomicsReport', ['visitId' => $this->visitId, 'behalfUserId' => $this->behalfUserId], $exception->getMessage());
if (app()->bound('sentry')) {
app('sentry')->captureException($exception);
}
}
}
Loading

0 comments on commit 1172a2f

Please sign in to comment.