diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e4e68ebdf..0dbcc9727 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -5,6 +5,7 @@ on: branches: - GaelO2 - GaelO2-dev + - tmtv-inference tags: - '*' @@ -22,6 +23,7 @@ jobs: with: images: ghcr.io/pixilib/gaelo tags: | + type=ref,event=branch type=ref,event=tag v2-latest diff --git a/GaelO2/.env.example b/GaelO2/.env.example index 5fff70efc..a9bfe77ca 100644 --- a/GaelO2/.env.example +++ b/GaelO2/.env.example @@ -15,22 +15,22 @@ DB_DATABASE=laravel DB_USERNAME=root DB_PASSWORD= -TUS_URL = 'http://localhost:1080' +TUS_URL='http://localhost:1080' -ORTHANC_TEMPORARY_URL = 'http://localhost:8042' -ORTHANC_TEMPORARY_LOGIN = 'login' -ORTHANC_TEMPORARY_PASSWORD = 'password' +ORTHANC_TEMPORARY_URL='http://localhost:8042' +ORTHANC_TEMPORARY_LOGIN='login' +ORTHANC_TEMPORARY_PASSWORD='password' -ORTHANC_STORAGE_URL = 'http://localhost:8043' -ORTHANC_STORAGE_LOGIN = 'login' -ORTHANC_STORAGE_PASSWORD = 'password' +ORTHANC_STORAGE_URL='http://localhost:8043' +ORTHANC_STORAGE_LOGIN='login' +ORTHANC_STORAGE_PASSWORD='password' -GAELO_PROCESSING_PROTOCOL='http://' -GAELO_PROCESSING_HOST = 'gaeloprocessing' -GAELO_PROCESSING_PORT= '8000' +GAELO_PROCESSING_URL='http://gaeloprocessing:8000' +GAELO_PROCESSING_LOGIN='login' +GAELO_PROCESSING_PASSWORD='password' -AZURE_CLIENT_ID ='' -AZURE_DIRECTORY_ID ='' +AZURE_CLIENT_ID='' +AZURE_DIRECTORY_ID='' AZURE_CLIENT_SECRET='' AZURE_SUBSCRIPTION_ID='' AZURE_CONTAINER_GROUP='' diff --git a/GaelO2/app/GaelO/Adapters/FrameworkAdapter.php b/GaelO2/app/GaelO/Adapters/FrameworkAdapter.php index 671923614..65c235000 100644 --- a/GaelO2/app/GaelO/Adapters/FrameworkAdapter.php +++ b/GaelO2/app/GaelO/Adapters/FrameworkAdapter.php @@ -59,7 +59,7 @@ public static function sendRegisteredEventForEmailVerification(int $userId): voi public static function sendResetPasswordLink(string $email): bool { $status = Password::sendResetLink( - ['email' => $email] + ['email' => strtolower($email)] ); return $status === Password::RESET_LINK_SENT; diff --git a/GaelO2/app/GaelO/Adapters/HttpClientAdapter.php b/GaelO2/app/GaelO/Adapters/HttpClientAdapter.php index f3a3913b3..cd35648ec 100644 --- a/GaelO2/app/GaelO/Adapters/HttpClientAdapter.php +++ b/GaelO2/app/GaelO/Adapters/HttpClientAdapter.php @@ -46,6 +46,19 @@ public function setBasicAuthentication(string $login, string $password): void $this->password = $password; } + public function uploadFile(string $method, string $uri, string $filename): Psr7ResponseInterface + { + $fileHandler = fopen($filename, 'rb'); + $headers = [ + 'auth' => [$this->login, $this->password], + 'content-type' => 'application/zip', + 'body' => $fileHandler + ]; + + $response = $this->client->request($method, $this->address . $uri, $headers); + return new Psr7ResponseAdapter($response); + } + public function requestUploadArrayDicom(string $method, string $uri, array $files): array { @@ -71,7 +84,7 @@ public function requestUploadArrayDicom(string $method, string $uri, array $file }, 'rejected' => function (RequestException $exception, $index) { $reason = "Error sending dicom to orthanc"; - + if ($exception->hasResponse()) { $reason = $exception->getResponse()->getStatusCode(); Log::error($exception->getResponse()->getBody()->getContents()); @@ -79,7 +92,7 @@ public function requestUploadArrayDicom(string $method, string $uri, array $file $reason = $exception->getMessage(); } // this is delivered each failed request - Log::error('DICOM Import Failed in Orthanc Temporary: '.$reason. ' index: '.$index); + Log::error('DICOM Import Failed in Orthanc Temporary: ' . $reason . ' index: ' . $index); }, ]); @@ -95,7 +108,14 @@ public function requestUploadArrayDicom(string $method, string $uri, array $file public function requestJson(string $method, string $uri, array $body = []): Psr7ResponseInterface { - $authenticationOption = ['auth' => [$this->login, $this->password]]; + if ($this->login !== '' && $this->password !== '') { + $authenticationOption['auth'] = [$this->login, $this->password]; + } + + if ($this->authorizationToken != null) { + $authenticationOption['headers']['Authorization'] = 'Bearer ' . $this->authorizationToken; + } + $bodyOption = ['json' => $body]; $options = array_merge($authenticationOption, $bodyOption); $response = $this->client->request($method, $this->address . $uri, $options); @@ -135,7 +155,7 @@ public function requestStreamResponseToFile(string $method, string $uri, $ressou return new Psr7ResponseAdapter($response); } - public function rowRequest(string $method, string $uri, $body, ?array $headers, $ressourceDestination = null, $httpErrors = true): Psr7ResponseInterface + public function rawRequest(string $method, string $uri, $body, ?array $headers, $ressourceDestination = null, $httpErrors = true): Psr7ResponseInterface { $options = []; diff --git a/GaelO2/app/GaelO/Adapters/JobAdapter.php b/GaelO2/app/GaelO/Adapters/JobAdapter.php index cc57b101a..7cc69ed77 100644 --- a/GaelO2/app/GaelO/Adapters/JobAdapter.php +++ b/GaelO2/app/GaelO/Adapters/JobAdapter.php @@ -4,6 +4,7 @@ use App\GaelO\Interfaces\Adapters\JobInterface; use App\Jobs\JobQcReport; +use App\Jobs\JobRadiomicsReport; class JobAdapter implements JobInterface { @@ -11,4 +12,9 @@ public function sendQcReportJob(int $visitId): void { JobQcReport::dispatch($visitId); } + + public function sendRadiomicsReport(int $visitId, int $behalfUserId): void + { + JobRadiomicsReport::dispatch($visitId, $behalfUserId); + } } diff --git a/GaelO2/app/GaelO/Adapters/MailerAdapter.php b/GaelO2/app/GaelO/Adapters/MailerAdapter.php index 12e6c4d8f..35662096c 100644 --- a/GaelO2/app/GaelO/Adapters/MailerAdapter.php +++ b/GaelO2/app/GaelO/Adapters/MailerAdapter.php @@ -29,7 +29,9 @@ use App\GaelO\Interfaces\Adapters\MailerInterface; use App\Mail\QcReport; use App\Mail\ImportPatient; +use App\Mail\JobFailure; use App\Mail\MagicLink; +use App\Mail\RadiomicsReport; use App\Mail\RequestPatientCreation; use App\Mail\UserCreated; use Illuminate\Mail\Mailable; @@ -158,6 +160,12 @@ private function getModel(int $model): Mailable case MailConstants::EMAIL_REQUEST_PATIENT_CREATION: $model = new RequestPatientCreation($this->parameters); break; + case MailConstants::EMAIL_RADIOMICS_REPORT: + $model = new RadiomicsReport($this->parameters); + break; + case MailConstants::EMAIL_JOB_FAILURE: + $model = new JobFailure($this->parameters); + break; default: throw new GaelOException("Unkown Mail Type"); break; diff --git a/GaelO2/app/GaelO/Adapters/MimeAdapter.php b/GaelO2/app/GaelO/Adapters/MimeAdapter.php index 4fe0196d1..8e30d5883 100644 --- a/GaelO2/app/GaelO/Adapters/MimeAdapter.php +++ b/GaelO2/app/GaelO/Adapters/MimeAdapter.php @@ -3,20 +3,22 @@ namespace App\GaelO\Adapters; use App\GaelO\Interfaces\Adapters\MimeInterface; -use Mimey\MimeTypes; +use Illuminate\Support\Facades\Log; +use League\MimeTypeDetection\ExtensionMimeTypeDetector; +use League\MimeTypeDetection\GeneratedExtensionToMimeTypeMap; class MimeAdapter implements MimeInterface { public static function getExtensionsFromMime(string $mime): array { - $mimes = new MimeTypes(); - return $mimes->getAllExtensions($mime); + $mimes = new ExtensionMimeTypeDetector(); + return $mimes->lookupAllExtensions($mime); } - public static function getMimesFromExtension(string $extension): array + public static function getMimeFromExtension(string $extension): string { - $mimes = new MimeTypes(); - return $mimes->getAllMimeTypes($extension); + $mimes = new GeneratedExtensionToMimeTypeMap(); + return $mimes->lookupMimeType($extension); } } diff --git a/GaelO2/app/GaelO/Adapters/PdfAdapter.php b/GaelO2/app/GaelO/Adapters/PdfAdapter.php new file mode 100644 index 000000000..fa5db9726 --- /dev/null +++ b/GaelO2/app/GaelO/Adapters/PdfAdapter.php @@ -0,0 +1,16 @@ +setOption('defaultFont', 'sans-serif'); + $pdf->save($filename); + } +} diff --git a/GaelO2/app/GaelO/Adapters/Psr7ResponseAdapter.php b/GaelO2/app/GaelO/Adapters/Psr7ResponseAdapter.php index 095043d02..253767eef 100644 --- a/GaelO2/app/GaelO/Adapters/Psr7ResponseAdapter.php +++ b/GaelO2/app/GaelO/Adapters/Psr7ResponseAdapter.php @@ -4,6 +4,7 @@ use App\GaelO\Interfaces\Adapters\Psr7ResponseInterface; use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\StreamInterface; class Psr7ResponseAdapter implements Psr7ResponseInterface { @@ -20,6 +21,11 @@ public function getStatusCode(): int return $this->response->getStatusCode(); } + public function getStream(): StreamInterface + { + return $this->response->getBody(); + } + public function getBody(): string { return $this->response->getBody()->getContents(); diff --git a/GaelO2/app/GaelO/Constants/Constants.php b/GaelO2/app/GaelO/Constants/Constants.php index d974735f4..0616da62c 100644 --- a/GaelO2/app/GaelO/Constants/Constants.php +++ b/GaelO2/app/GaelO/Constants/Constants.php @@ -23,6 +23,7 @@ class Constants const TRACKER_CREATE_VISIT = "Create Visit"; const TRACKER_UPLOAD_SERIES = "Upload Series"; const TRACKER_UPDATE_VISIT_DATE = "Update Visit Date"; + const TRACKER_UPDATE_VISIT_FILE = "Update Visit File"; const TRACKER_UPLOAD_VALIDATION_FAILED = "Upload Failed"; const TRACKER_ASK_UNLOCK = "Ask Unlock Form"; const TRACKER_UNLOCK_INVESTIGATOR_FORM = "Unlock Investigator Form"; diff --git a/GaelO2/app/GaelO/Constants/MailConstants.php b/GaelO2/app/GaelO/Constants/MailConstants.php index c6cbf333a..c23ac65a8 100644 --- a/GaelO2/app/GaelO/Constants/MailConstants.php +++ b/GaelO2/app/GaelO/Constants/MailConstants.php @@ -27,4 +27,6 @@ class MailConstants { const EMAIL_UNLOCK_QC_REQUEST=22; const EMAIL_QC_REPORT = 23; const EMAIL_REQUEST_PATIENT_CREATION = 24; + const EMAIL_RADIOMICS_REPORT = 25; + const EMAIL_JOB_FAILURE = 26; } diff --git a/GaelO2/app/GaelO/Constants/SettingsConstants.php b/GaelO2/app/GaelO/Constants/SettingsConstants.php index 79d8f2ac7..32a42bddb 100644 --- a/GaelO2/app/GaelO/Constants/SettingsConstants.php +++ b/GaelO2/app/GaelO/Constants/SettingsConstants.php @@ -20,9 +20,9 @@ class SettingsConstants { const ORTHANC_STORAGE_LOGIN = 'orthanc_storage_login'; const ORTHANC_STORAGE_PASSWORD = 'orthanc_storage_password'; - const GAELO_PROCESSING_PROTOCOL='gaelo_processing_protocol'; - const GAELO_PROCESSING_HOST = 'gaelo_processing_host'; - const GAELO_PROCESSING_PORT= 'gaelo_processing_port'; + const GAELO_PROCESSING_URL='gaelo_processing_url'; + const GAELO_PROCESSING_LOGIN='gaelo_processing_login'; + const GAELO_PROCESSING_PASSWORD='gaelo_processing_password'; const AZURE_CLIENT_ID ='azure_client_id'; const AZURE_DIRECTORY_ID ='azure_directory_id'; diff --git a/GaelO2/app/GaelO/Entities/ReviewEntity.php b/GaelO2/app/GaelO/Entities/ReviewEntity.php index 86ec4ef12..9c4fa053c 100644 --- a/GaelO2/app/GaelO/Entities/ReviewEntity.php +++ b/GaelO2/app/GaelO/Entities/ReviewEntity.php @@ -21,13 +21,13 @@ public static function fillFromDBReponseArray(array $array): ReviewEntity $reviewEntity = new ReviewEntity(); $reviewEntity->local = $array['local']; $reviewEntity->id = $array['id']; - $reviewEntity->study_name = $array['study_name']; + $reviewEntity->studyName = $array['study_name']; $reviewEntity->userId = $array['user_id']; $reviewEntity->date = $array['review_date']; $reviewEntity->visitId = $array['visit_id']; $reviewEntity->validated = $array['validated']; $reviewEntity->data = $array['review_data']; - $reviewEntity->files = $array['sent_files']; + $reviewEntity->files = $array['sent_files'] ?? []; $reviewEntity->adjudication = $array['adjudication']; return $reviewEntity; } diff --git a/GaelO2/app/GaelO/Entities/StudyEntity.php b/GaelO2/app/GaelO/Entities/StudyEntity.php index bbb68c2b8..bfa3eb20d 100644 --- a/GaelO2/app/GaelO/Entities/StudyEntity.php +++ b/GaelO2/app/GaelO/Entities/StudyEntity.php @@ -13,6 +13,7 @@ class StudyEntity public bool $monitorShowAll; public bool $documentationMandatory; public bool $deleted; + public bool $creatablePatientsInvestigator; public ?string $ancillaryOf; //Array of VisitGroupEntities @@ -29,6 +30,7 @@ public static function fillFromDBReponseArray(array $array) : StudyEntity $studyEntity->monitorShowAll = $array['monitor_show_all']; $studyEntity->ancillaryOf = $array['ancillary_of']; $studyEntity->documentationMandatory = $array['documentation_mandatory']; + $studyEntity->creatablePatientsInvestigator = $array['creatable_patients_investigator']; $studyEntity->deleted = $array['deleted_at'] !== null; return $studyEntity; diff --git a/GaelO2/app/GaelO/Entities/VisitEntity.php b/GaelO2/app/GaelO/Entities/VisitEntity.php index 28b6b0f65..68f1c477f 100644 --- a/GaelO2/app/GaelO/Entities/VisitEntity.php +++ b/GaelO2/app/GaelO/Entities/VisitEntity.php @@ -30,6 +30,7 @@ class VisitEntity public ?bool $correctiveActionInvestigatorForm; public ?string $correctiveActionOther; public ?bool $correctiveActionDecision; + public array $files; public ?string $deletedAt; public VisitGroupEntity $visitGroup; @@ -70,6 +71,7 @@ public static function fillFromDBReponseArray(array $array): VisitEntity $visitEntity->correctiveActionInvestigatorForm = $array['corrective_action_investigator_form']; $visitEntity->correctiveActionOther = $array['corrective_action_comment']; $visitEntity->correctiveActionDecision = $array['corrective_action_applied']; + $visitEntity->files = $array['sent_files'] ?? []; $visitEntity->deletedAt = $array['deleted_at']; return $visitEntity; } diff --git a/GaelO2/app/GaelO/Interfaces/Adapters/HttpClientInterface.php b/GaelO2/app/GaelO/Interfaces/Adapters/HttpClientInterface.php index 7db7ad477..0a6394e9a 100644 --- a/GaelO2/app/GaelO/Interfaces/Adapters/HttpClientInterface.php +++ b/GaelO2/app/GaelO/Interfaces/Adapters/HttpClientInterface.php @@ -13,8 +13,9 @@ public function setAuthorizationToken(string $authorizationToken): void; public function setBasicAuthentication(string $login, string $password): void; - public function rowRequest(string $method, string $uri, $body, ?array $headers, $ressourceDestination = null, $httpErrors = true): Psr7ResponseInterface; + public function rawRequest(string $method, string $uri, $body, ?array $headers, $ressourceDestination = null, $httpErrors = true): Psr7ResponseInterface; + public function uploadFile(string $method, string $uri, string $filename) : Psr7ResponseInterface; /** * Return array of PSR7 response adapter of multiple request, used to sent multiple files to an endpoint */ diff --git a/GaelO2/app/GaelO/Interfaces/Adapters/JobInterface.php b/GaelO2/app/GaelO/Interfaces/Adapters/JobInterface.php index 5e24d8861..c064361df 100644 --- a/GaelO2/app/GaelO/Interfaces/Adapters/JobInterface.php +++ b/GaelO2/app/GaelO/Interfaces/Adapters/JobInterface.php @@ -5,4 +5,5 @@ interface JobInterface { public function sendQcReportJob(int $visitId) : void; + public function sendRadiomicsReport(int $visitId, int $behalfUserId) :void; } \ No newline at end of file diff --git a/GaelO2/app/GaelO/Interfaces/Adapters/MimeInterface.php b/GaelO2/app/GaelO/Interfaces/Adapters/MimeInterface.php index 43c09af6f..7fcc343bc 100644 --- a/GaelO2/app/GaelO/Interfaces/Adapters/MimeInterface.php +++ b/GaelO2/app/GaelO/Interfaces/Adapters/MimeInterface.php @@ -4,5 +4,5 @@ Interface MimeInterface{ public static function getExtensionsFromMime(string $mime) : array; - public static function getMimesFromExtension(string $extension) : array; + public static function getMimeFromExtension(string $extension) : string; } diff --git a/GaelO2/app/GaelO/Interfaces/Adapters/PdfInterface.php b/GaelO2/app/GaelO/Interfaces/Adapters/PdfInterface.php new file mode 100644 index 000000000..a88af820b --- /dev/null +++ b/GaelO2/app/GaelO/Interfaces/Adapters/PdfInterface.php @@ -0,0 +1,8 @@ +studyModel->findOrFail($name)->delete(); } - public function addStudy(String $name, string $code, int $patientCodeLength, string $contactEmail, bool $controllerShowAll, bool $monitorShowAll, bool $documentationMandatory, ?string $ancillaryOf): void + public function addStudy(String $name, string $code, int $patientCodeLength, string $contactEmail, bool $controllerShowAll, bool $monitorShowAll, bool $documentationMandatory, ?string $ancillaryOf, bool $creatablePatientsInvestigator): void { $study = new Study(); $study->name = $name; @@ -38,6 +38,7 @@ public function addStudy(String $name, string $code, int $patientCodeLength, str $study->monitor_show_all = $monitorShowAll; $study->documentation_mandatory = $documentationMandatory; $study->ancillary_of = $ancillaryOf; + $study->creatable_patients_investigator = $creatablePatientsInvestigator; $study->save(); } diff --git a/GaelO2/app/GaelO/Repositories/UserRepository.php b/GaelO2/app/GaelO/Repositories/UserRepository.php index 251f7f146..dc5dfd496 100644 --- a/GaelO2/app/GaelO/Repositories/UserRepository.php +++ b/GaelO2/app/GaelO/Repositories/UserRepository.php @@ -65,7 +65,7 @@ public function createUser( $user = new User(); $user->lastname = $lastname; $user->firstname = $firstname; - $user->email = $email; + $user->email = strtolower($email); $user->phone = $phone; $user->administrator = $administrator; $user->center_code = $centerCode; @@ -98,7 +98,7 @@ public function updateUser( $user = $this->userModel->findOrFail($id); $user->lastname = $lastname; $user->firstname = $firstname; - $user->email = $email; + $user->email = strtolower($email); $user->phone = $phone; $user->administrator = $administrator; $user->center_code = $centerCode; @@ -136,9 +136,9 @@ public function resetAttemptsAndUpdateLastConnexion(int $userId): void public function getUserByEmail(String $email, bool $withTrashed = false): array { if ($withTrashed) { - $user = $this->userModel->withTrashed()->where('email', $email)->sole(); + $user = $this->userModel->withTrashed()->where('email', strtolower($email))->sole(); } else { - $user = $this->userModel->where('email', $email)->sole(); + $user = $this->userModel->where('email', strtolower($email))->sole(); } return $user->toArray(); @@ -146,7 +146,7 @@ public function getUserByEmail(String $email, bool $withTrashed = false): array public function isExistingEmail(String $email): bool { - $user = $this->userModel->withTrashed()->where('email', $email); + $user = $this->userModel->withTrashed()->where('email', strtolower($email)); return $user->count() > 0 ? true : false; } @@ -157,8 +157,8 @@ public function reactivateUser(int $id): void public function getAdministrators(): array { - $emails = $this->userModel->where('administrator', true)->get(); - return empty($emails) ? [] : $emails->toArray(); + $administrators = $this->userModel->where('administrator', true)->get(); + return empty($administrators) ? [] : $administrators->toArray(); } /** @@ -168,7 +168,7 @@ public function getAdministrators(): array public function getInvestigatorsOfStudyFromCenter(string $study, int $centerCode, ?string $job): array { - $emails = $this->userModel + $investigators = $this->userModel ->with('affiliatedCenters') ->whereHas('roles', function ($query) use ($study, $job) { if ($job !== null) { @@ -188,7 +188,7 @@ public function getInvestigatorsOfStudyFromCenter(string $study, int $centerCode }) ->get(); - return empty($emails) ? [] : $emails->toArray(); + return empty($investigators) ? [] : $investigators->toArray(); } public function getUsersByRolesInStudy(string $study, string $role): array diff --git a/GaelO2/app/GaelO/Repositories/VisitRepository.php b/GaelO2/app/GaelO/Repositories/VisitRepository.php index c797bca0b..b69b02e85 100644 --- a/GaelO2/app/GaelO/Repositories/VisitRepository.php +++ b/GaelO2/app/GaelO/Repositories/VisitRepository.php @@ -14,7 +14,6 @@ use App\GaelO\Util; use App\Models\ReviewStatus; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Log; class VisitRepository implements VisitRepositoryInterface { @@ -489,7 +488,10 @@ public function resetQc(int $visitId): void $visitEntity = $this->visitModel->findOrFail($visitId); - $visitEntity['state_quality_control'] = QualityControlStateEnum::NOT_DONE->value; + //If status Qc Not needed keep the not needed status + if ($visitEntity['state_quality_control'] !== QualityControlStateEnum::NOT_NEEDED) { + $visitEntity['state_quality_control'] = QualityControlStateEnum::NOT_DONE; + } $visitEntity['controller_user_id'] = null; $visitEntity['control_date'] = null; $visitEntity['image_quality_control'] = null; @@ -558,4 +560,12 @@ public function reactivateVisit(int $visitId): void { $this->visitModel->withTrashed()->findOrFail($visitId)->restore(); } + + public function updateVisitFile(int $visitId, array $associatedFile): void + { + + $review = $this->visitModel->findOrFail($visitId); + $review->sent_files = $associatedFile; + $review->save(); + } } diff --git a/GaelO2/app/GaelO/Services/DicomService.php b/GaelO2/app/GaelO/Services/DicomService.php index 096e2dd3d..8d0775048 100644 --- a/GaelO2/app/GaelO/Services/DicomService.php +++ b/GaelO2/app/GaelO/Services/DicomService.php @@ -14,6 +14,7 @@ class DicomService private DicomSeriesRepositoryInterface $dicomSeriesRepositoryInterface; private DicomStudyRepositoryInterface $dicomStudyRepositoryInterface; private VisitService $visitService; + private int $currentUserId; public function __construct( DicomSeriesRepositoryInterface $dicomSeriesRepositoryInterface, @@ -26,6 +27,11 @@ public function __construct( $this->visitService = $visitService; } + public function setCurrentUserId(int $userId) + { + $this->currentUserId = $userId; + } + public function deleteSeries(string $seriesInstanceUID, string $role) { @@ -40,6 +46,7 @@ public function deleteSeries(string $seriesInstanceUID, string $role) if (sizeof($remainingSeries) === 0) { $this->dicomStudyRepositoryInterface->delete($seriesData['dicom_study']['study_uid']); $this->visitService->setVisitId($visitId); + $this->visitService->setCurrentUserId($this->currentUserId); $this->visitService->updateUploadStatus(UploadStatusEnum::NOT_DONE->value); //Reset QC only if suppervisor, we don't change QC status for investigator and controller (as it still ongoing) if ($role === Constants::ROLE_SUPERVISOR) { @@ -67,6 +74,7 @@ public function reactivateDicomStudy(string $studyInstanceUID): void //Update upload status to Done $this->visitService->setVisitId($visitId); + $this->visitService->setCurrentUserId($this->currentUserId); $this->visitService->updateUploadStatus(UploadStatusEnum::DONE->value); } } diff --git a/GaelO2/app/GaelO/Services/ExportStudyService.php b/GaelO2/app/GaelO/Services/ExportStudyService.php index fc2b1fca1..313dbf417 100644 --- a/GaelO2/app/GaelO/Services/ExportStudyService.php +++ b/GaelO2/app/GaelO/Services/ExportStudyService.php @@ -146,7 +146,7 @@ public function exportVisitTable(): void unset($visit['patient']); //transform target_lesions as json string $visit['review_status']['target_lesions'] = json_encode($visit['review_status']['target_lesions']); - + $visit['sent_files'] = json_encode($visit['sent_files']); $resultsData[$sheetName][] = array_merge(['visit_group' => $visitGroupName, 'visit_type' => $visitTypeName], $visit, $visit['review_status']); } diff --git a/GaelO2/app/GaelO/Services/FormService/FormService.php b/GaelO2/app/GaelO/Services/FormService/FormService.php index 26364751d..e7c428d97 100644 --- a/GaelO2/app/GaelO/Services/FormService/FormService.php +++ b/GaelO2/app/GaelO/Services/FormService/FormService.php @@ -5,6 +5,7 @@ use App\GaelO\Entities\StudyEntity; use App\GaelO\Exceptions\GaelOBadRequestException; use App\GaelO\Exceptions\GaelOException; +use App\GaelO\Exceptions\GaelOForbiddenException; use App\GaelO\Interfaces\Adapters\FrameworkInterface; use App\GaelO\Interfaces\Repositories\ReviewRepositoryInterface; use App\GaelO\Interfaces\Repositories\StudyRepositoryInterface; @@ -53,6 +54,7 @@ public function __construct( public function setCurrentUserId(int $currentUserId): void { $this->currentUserId = $currentUserId; + $this->visitService->setCurrentUserId($currentUserId); } public function setVisitContextAndStudy(array $visitContext, string $studyName): void @@ -79,13 +81,13 @@ public abstract function updateForm(int $reviewId, array $uploadedFileKeys, arra public abstract function unlockForm(int $reviewId); public abstract function deleteForm(int $reviewId); - public function attachFile(array $reviewEntity, string $key, string $filename, string $mimeType, $binaryData): void + public function attachFile(array $reviewEntity, string $key, string $mimeType, string $extension, $binaryData): string { $associatedFiles = []; //Safty check - if($reviewEntity['local']!== $this->local) throw new GaelOException("Form Service Unconsitancy"); + if ($reviewEntity['local'] !== $this->local) throw new GaelOException("Form Service Unconsitancy"); if ($reviewEntity['local']) { $associatedFiles = $this->abstractVisitRules->getAssociatedFilesInvestigator(); @@ -95,24 +97,32 @@ public function attachFile(array $reviewEntity, string $key, string $filename, s else $associatedFiles = $this->abstractVisitRules->getAssociatedFilesReview(); } + if (!array_key_exists($key, $associatedFiles)) { + throw new GaelOForbiddenException("Unexpected file key"); + } + $associatiedFile = $associatedFiles[$key]; if (!empty($reviewEntity['sent_files'][$key])) { throw new GaelOBadRequestException("Already Existing File for this review"); } - if ( !in_array($mimeType, $associatiedFile->mimes) ) { + if (!in_array($mimeType, $associatiedFile->mimes)) { throw new GaelOBadRequestException("File Key or Mime Not Allowed"); } $destinationPath = $this->studyName . '/' . 'attached_review_file'; + $filename = 'review_' . $reviewEntity['id'] . '_' . $key . '.' . $extension; $destinationFileName = $destinationPath . '/' . $filename; + $this->frameworkInterface->storeFile($destinationFileName, $binaryData); $reviewEntity['sent_files'][$key] = $destinationFileName; $this->reviewRepositoryInterface->updateReviewFile($reviewEntity['id'], $reviewEntity['sent_files']); + + return $filename; } public function removeFile(array $reviewEntity, string $key): void @@ -133,7 +143,7 @@ public function getVisitRules(): AbstractVisitRules return $this->abstractVisitRules; } - public function getVisitDecisionObject() : AbstractVisitDecisions + public function getVisitDecisionObject(): AbstractVisitDecisions { return $this->abstractVisitRules->getVisitDecisionObject(); } diff --git a/GaelO2/app/GaelO/Services/FormService/ReviewFormService.php b/GaelO2/app/GaelO/Services/FormService/ReviewFormService.php index d56855d2e..a9eb42f09 100644 --- a/GaelO2/app/GaelO/Services/FormService/ReviewFormService.php +++ b/GaelO2/app/GaelO/Services/FormService/ReviewFormService.php @@ -11,6 +11,9 @@ use App\GaelO\Interfaces\Repositories\ReviewStatusRepositoryInterface; use App\GaelO\Interfaces\Repositories\StudyRepositoryInterface; use App\GaelO\Services\FormService\FormService; +use App\GaelO\Services\GaelOStudiesService\AbstractGaelOStudy; +use App\GaelO\Services\GaelOStudiesService\Events\AwaitingAdjudicationEvent; +use App\GaelO\Services\GaelOStudiesService\Events\VisitConcludedEvent; use App\GaelO\Services\MailServices; use App\GaelO\Services\VisitService; @@ -103,18 +106,16 @@ private function doSpecificReviewDecisions() //Send Notification emails if ($reviewStatus === ReviewStatusEnum::WAIT_ADJUDICATION->value) { - $this->mailServices->sendAwaitingAdjudicationMessage($this->studyName, $this->patientId, $this->patientCode, $this->visitType, $this->visitId); - } else if ($reviewStatus === ReviewStatusEnum::DONE->value ) { + $awaitingAdjudicationEvent = new AwaitingAdjudicationEvent($this->visitContext); + $studyObject = AbstractGaelOStudy::getSpecificStudyObject($this->studyName); + $studyObject->onEventStudy($awaitingAdjudicationEvent); + } else if ($reviewStatus === ReviewStatusEnum::DONE->value) { //In case of conclusion reached send conclusion (but not to uploader if ancillary study) - $this->mailServices->sendVisitConcludedMessage( - $this->visitId, - $this->studyEntity->isAncillaryStudy() ? null : $this->uploaderId, - $this->studyName, - $this->patientId, - $this->patientCode, - $this->visitType, - $conclusion - ); + $visitConcludedEvent = new VisitConcludedEvent($this->visitContext); + $visitConcludedEvent->setConclusion($conclusion); + $visitConcludedEvent->setUploaderUserId($this->studyEntity->isAncillaryStudy() ? null : $this->uploaderId); + $studyObject = AbstractGaelOStudy::getSpecificStudyObject($this->studyName); + $studyObject->onEventStudy($visitConcludedEvent); } } } diff --git a/GaelO2/app/GaelO/Services/GaelOClientService.php b/GaelO2/app/GaelO/Services/GaelOClientService.php new file mode 100644 index 000000000..6f679c8d6 --- /dev/null +++ b/GaelO2/app/GaelO/Services/GaelOClientService.php @@ -0,0 +1,55 @@ +httpClientInterface = $httpClientInterface; + $this->frameworkInterface = $frameworkInterface; + } + + public function loadUrl() + { + //Set address of Orthanc server + $url = $this->frameworkInterface::getConfig(SettingsConstants::APP_URL); + if ($url) $this->httpClientInterface->setUrl($url); + } + + public function setAuthorizationToken(string $token): void + { + $this->httpClientInterface->setAuthorizationToken($token); + } + + public function login(string $email, $password): void + { + $payload = [ + 'email' => $email, + 'password' => $password + ]; + $answer = $this->httpClientInterface->requestJson("POST", '/api/login', $payload); + $this->httpClientInterface->setAuthorizationToken($answer['access_token']); + } + + public function createFileToVisit(string $studyName, int $visitId, string $key, ?string $extension, string $filePath) + { + $payload = [ + 'extension' => $extension, + 'content' => base64_encode(file_get_contents($filePath)) + ]; + $this->httpClientInterface->requestJson("POST", '/api/visits/' . $visitId . '/files/' . $key . '?studyName=' . $studyName . '&role=Supervisor', $payload); + } + + public function deleteFileToVisit(string $studyName, int $visitId, string $key) + { + $this->httpClientInterface->rawRequest('DELETE', '/api/visits/' . $visitId . '/files/' . $key . '?studyName=' . $studyName . '&role=Supervisor', null, null); + } +} diff --git a/GaelO2/app/GaelO/Services/GaelOProcessingService/AzureService.php b/GaelO2/app/GaelO/Services/GaelOProcessingService/AzureService.php index 44ad6d3b8..11e6f6361 100644 --- a/GaelO2/app/GaelO/Services/GaelOProcessingService/AzureService.php +++ b/GaelO2/app/GaelO/Services/GaelOProcessingService/AzureService.php @@ -48,7 +48,7 @@ public function startAci(): bool { $this->setAccessToken(); $uri = "/start?api-version=2021-09-01"; - $response = $this->httpClientInterface->rowRequest('POST', $uri, null, ['Accept' => 'application/json'])->getStatusCode(); + $response = $this->httpClientInterface->rawRequest('POST', $uri, null, ['Accept' => 'application/json'])->getStatusCode(); return $response === 202; } @@ -57,7 +57,7 @@ public function stopACI(): bool { $this->setAccessToken(); $uri = "/stop?api-version=2021-09-01"; - $response = $this->httpClientInterface->rowRequest('POST', $uri, null, ['Accept' => 'application/json'])->getStatusCode(); + $response = $this->httpClientInterface->rawRequest('POST', $uri, null, ['Accept' => 'application/json'])->getStatusCode(); return $response === 204; } @@ -66,7 +66,7 @@ public function getStatusAci(): array { $this->setAccessToken(); $uri = "?api-version=2021-09-01"; - $response = $this->httpClientInterface->rowRequest('GET', $uri, null, ['Accept' => 'application/json'])->getJsonBody(); + $response = $this->httpClientInterface->rawRequest('GET', $uri, null, ['Accept' => 'application/json'])->getJsonBody(); /*3 states disponible * Pending -> Creation en cours diff --git a/GaelO2/app/GaelO/Services/GaelOProcessingService/GaelOProcessingService.php b/GaelO2/app/GaelO/Services/GaelOProcessingService/GaelOProcessingService.php index 30738ac7f..29bc99d21 100644 --- a/GaelO2/app/GaelO/Services/GaelOProcessingService/GaelOProcessingService.php +++ b/GaelO2/app/GaelO/Services/GaelOProcessingService/GaelOProcessingService.php @@ -5,61 +5,157 @@ use App\GaelO\Constants\SettingsConstants; use App\GaelO\Interfaces\Adapters\FrameworkInterface; use App\GaelO\Interfaces\Adapters\HttpClientInterface; -use App\GaelO\Interfaces\Adapters\Psr7ResponseInterface; -use App\GaelO\Services\OrthancService; +use Illuminate\Support\Facades\Log; class GaelOProcessingService { private HttpClientInterface $httpClientInterface; - private OrthancService $orthancService; private FrameworkInterface $frameworkInterface; - private string $host; - private int $port; - private string $protocol; - - /** - * GaelO Processing Interaction - */ - public function __construct(OrthancService $orthancService, FrameworkInterface $frameworkInterface, HttpClientInterface $httpClientInterface) + public function __construct(HttpClientInterface $httpClientInterface, FrameworkInterface $frameworkInterface) { - $this->httpClientInterface = $httpClientInterface; - $this->orthancService = $orthancService; $this->frameworkInterface = $frameworkInterface; - //Set GAELO Processing URL Passed in Env variable (default address) - $this->port = $this->frameworkInterface::getConfig(SettingsConstants::GAELO_PROCESSING_PORT); - $this->protocol = $this->frameworkInterface::getConfig(SettingsConstants::GAELO_PROCESSING_PROTOCOL); - $this->host = $this->frameworkInterface::getConfig(SettingsConstants::GAELO_PROCESSING_HOST); - $this->setServerAdress(); - //Need to access to Orthanc storage - $this->orthancService->setOrthancServer(true); + $this->setParams(); } - public function setHost(string $host) + public function setParams(): void { - $this->host = $host; + //Set Time Limit at 1H as operation could be long + set_time_limit(3600); + //Set address of Processing Server + $url = $this->frameworkInterface->getConfig(SettingsConstants::GAELO_PROCESSING_URL); + $login = $this->frameworkInterface->getConfig(SettingsConstants::GAELO_PROCESSING_LOGIN); + $password = $this->frameworkInterface->getConfig(SettingsConstants::GAELO_PROCESSING_PASSWORD); + $this->httpClientInterface->setUrl($url); + $this->httpClientInterface->setBasicAuthentication($login, $password); } - /** - * Setter for dynamic IP of gaelo processing - */ - public function setServerAdress() + + public function createSeriesFromOrthanc(string $orthancSeriesId, bool $pet = false, bool $convertToSuv = false) { - $url = $this->protocol . $this->host . ':' . $this->port; - $this->httpClientInterface->setUrl($url); + $request = $this->httpClientInterface->requestJson('POST', "/tools/create-series-from-orthanc", ['seriesId' => $orthancSeriesId, 'PET' => $pet, 'convertToSuv' => $convertToSuv]); + return $request->getBody(); + } + + public function createDicom(string $filename){ + $request = $this->httpClientInterface->uploadFile('POST', "/dicoms", $filename); + return $request->getBody(); + } + + public function executeInference(string $modelName, array $payload) + { + $request = $this->httpClientInterface->requestJson('POST', "/models/" . $modelName . "/inference", $payload); + return $request->getJsonBody(); + } + + public function createMIPForSeries(string $seriesId, array $payload = []): string + { + $downloadedFilePath = tempnam(ini_get('upload_tmp_dir'), 'TMP_Inference_'); + + $this->httpClientInterface->requestStreamResponseToFile('POST', "/series/" . $seriesId . "/mip", $downloadedFilePath, ['content-Type' => 'application/json'], $payload); + return $downloadedFilePath; + } + + public function getNiftiMask(string $maskId): string + { + $downloadedFilePath = tempnam(ini_get('upload_tmp_dir'), 'TMP_Inference_'); + + $this->httpClientInterface->requestStreamResponseToFile('GET', "/masks/" . $maskId . "/file", $downloadedFilePath, ['content-Type' => 'application/json'], []); + return $downloadedFilePath; + } + + public function getNiftiSeries(string $imageId): string + { + $downloadedFilePath = tempnam(ini_get('upload_tmp_dir'), 'TMP_Inference_'); + + $this->httpClientInterface->requestStreamResponseToFile('GET', "/series/" . $imageId . "/file", $downloadedFilePath, ['content-Type' => 'application/json'], []); + return $downloadedFilePath; + } + + public function createRtssFromMask(string $orthancSeriesId, string $maskId): string + { + $payload = [ + 'maskId' => $maskId, + 'orthancSeriesId' => $orthancSeriesId + ]; + + $request = $this->httpClientInterface->requestJson('POST', "/tools/mask-to-rtss", $payload); + return $request->getBody(); } - /** - * Fetch zipped dicom and transmit it to GaelO Processing - */ - public function sendDicom(array $orthancID, ?string $transferSyntaxUID): Psr7ResponseInterface + public function getRtss(string $rtssId): string { - // Fetch dicom from Orthanc - $psr7Response = $this->orthancService->getOrthancZipStreamAsString($orthancID, $transferSyntaxUID); - // Send Dicom to GaelO Processing - $response = $this->httpClientInterface->rowRequest('POST', "/app/dicom", $psr7Response->getBody(), ['content-type' => 'application/zip', 'Accept' => 'application/json']); + $downloadedFilePath = tempnam(ini_get('upload_tmp_dir'), 'TMP_Inference_'); - return $response; + $this->httpClientInterface->requestStreamResponseToFile('GET', "/rtss/" . $rtssId . "/file", $downloadedFilePath, []); + return $downloadedFilePath; + } + + public function createSegFromMask(string $orthancSeriesId, string $maskId): string + { + $payload = [ + 'maskId' => $maskId, + 'orthancSeriesId' => $orthancSeriesId + ]; + + $request = $this->httpClientInterface->requestJson('POST', "/tools/mask-to-seg", $payload); + return $request->getBody(); + } + + public function getSeg(string $segId): string + { + $downloadedFilePath = tempnam(ini_get('upload_tmp_dir'), 'TMP_Inference_'); + + $this->httpClientInterface->requestStreamResponseToFile('GET', "/seg/" . $segId . "/file", $downloadedFilePath, []); + return $downloadedFilePath; + } + + public function thresholdMask(string $maskId, string $seriesId, string|float $threshold) + { + $payload = [ + 'maskId' => $maskId, + 'seriesId' => $seriesId, + 'threshold' => $threshold + ]; + $request = $this->httpClientInterface->requestJson('POST', "/tools/threshold-mask", $payload); + return $request->getBody(); + } + + public function fragmentMask(string $seriesId, string $maskId, bool $output3D): string + { + $payload = [ + 'maskId' => $maskId, + 'seriesId' => $seriesId, + 'ouput3D' => $output3D + ]; + + $request = $this->httpClientInterface->requestJson('POST', "/tools/mask-fragmentation", $payload); + return $request->getBody(); + } + + public function getMaskDicomOrientation(string $maskId, string $orientation, bool $compress): string + { + $downloadedFilePath = tempnam(ini_get('upload_tmp_dir'), 'TMP_Inference_'); + + $payload = [ + 'maskId' => $maskId, + 'orientation' => $orientation, + 'compress' => $compress + ]; + + $this->httpClientInterface->requestStreamResponseToFile('POST', "/tools/mask-dicom", $downloadedFilePath, [], $payload); + return $downloadedFilePath; + } + + public function getStatsMask(string $maskId): array + { + $request = $this->httpClientInterface->requestJson('GET', "/masks/" . $maskId . "/stats"); + return $request->getJsonBody(); + } + + public function deleteRessource(string $type, string $id): void + { + $request = $this->httpClientInterface->requestJson('DELETE', "/" . $type . "/" . $id); } } diff --git a/GaelO2/app/GaelO/Services/GaelOStudiesService/AbstractGaelOStudy.php b/GaelO2/app/GaelO/Services/GaelOStudiesService/AbstractGaelOStudy.php index 3037eee68..f901d00c1 100644 --- a/GaelO2/app/GaelO/Services/GaelOStudiesService/AbstractGaelOStudy.php +++ b/GaelO2/app/GaelO/Services/GaelOStudiesService/AbstractGaelOStudy.php @@ -3,11 +3,35 @@ namespace App\GaelO\Services\GaelOStudiesService; use App\GaelO\Adapters\FrameworkAdapter; +use App\GaelO\Constants\Constants; +use App\GaelO\Interfaces\Adapters\JobInterface; +use App\GaelO\Interfaces\Repositories\ReviewRepositoryInterface; +use App\GaelO\Interfaces\Repositories\UserRepositoryInterface; +use App\GaelO\Services\GaelOStudiesService\Events\AwaitingAdjudicationEvent; +use App\GaelO\Services\GaelOStudiesService\Events\BaseStudyEvent; +use App\GaelO\Services\GaelOStudiesService\Events\CorrectiveActionEvent; +use App\GaelO\Services\GaelOStudiesService\Events\QCModifiedEvent; +use App\GaelO\Services\GaelOStudiesService\Events\VisitConcludedEvent; +use App\GaelO\Services\GaelOStudiesService\Events\VisitUploadedEvent; +use App\GaelO\Services\MailService\MailListBuilder; +use App\GaelO\Services\MailServices; abstract class AbstractGaelOStudy { + protected MailServices $mailServices; + protected UserRepositoryInterface $userRepositoryInterface; + protected ReviewRepositoryInterface $reviewRepositoryInterface; + protected JobInterface $jobInterface; protected string $studyName; + public function __construct(MailServices $mailServices, UserRepositoryInterface $userRepositoryInterface, ReviewRepositoryInterface $reviewRepositoryInterface, JobInterface $jobInterface) + { + $this->mailServices = $mailServices; + $this->userRepositoryInterface = $userRepositoryInterface; + $this->reviewRepositoryInterface = $reviewRepositoryInterface; + $this->jobInterface = $jobInterface; + } + public abstract function getVisitRulesClass(string $visitGroupName, string $visitTypeName): String; public function getSpecificVisitRules(string $visitGroup, string $visitName): AbstractVisitRules @@ -32,11 +56,208 @@ public function getReviewableVisitTypeIds(): null|array /** * For ancillaries studies, shall return the patients metadata tags for which a review is expected */ - public function getReviewablePatientsTags() : null|array + public function getReviewablePatientsTags(): null|array { return null; } + /** + * If needed to predifine expected patients + * return + */ + public function getExpectedPatients(): array + { + return []; + } + + /** + * To make specific study action on study event, can be overriden to avoid some automatic mails + */ + public function onEventStudy(BaseStudyEvent $studyEvent): void + { + if ($studyEvent instanceof VisitUploadedEvent) { + $this->onVisitUploaded($studyEvent); + } else if ($studyEvent instanceof QCModifiedEvent) { + $this->onQcModified($studyEvent); + } else if ($studyEvent instanceof CorrectiveActionEvent) { + $this->onCorrectiveAction($studyEvent); + } else if ($studyEvent instanceof AwaitingAdjudicationEvent) { + $this->onAwaitingAdjudication($studyEvent); + } else if ($studyEvent instanceof VisitConcludedEvent) { + $this->onVisitConcluded($studyEvent); + } + } + + protected function onVisitUploaded(VisitUploadedEvent $visitUploadedEvent): void + { + $studyName = $visitUploadedEvent->getStudyName(); + $patientId = $visitUploadedEvent->getPatientId(); + $patientCode = $visitUploadedEvent->getPatientCode(); + $visitId = $visitUploadedEvent->getVisitId(); + $qcNeeded = $visitUploadedEvent->isQcNeeded(); + $visitType = $visitUploadedEvent->getVisitTypeName(); + $creatorUserId = $visitUploadedEvent->getCreatorUserId(); + $uploaderUserId = $visitUploadedEvent->getUploaderUserId(); + $reviewNeeded = $visitUploadedEvent->isReviewNeeded(); + + //Send to supervisors and monitors of the study + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); + $mailListBuilder->withUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR) + ->withUsersEmailsByRolesInStudy($studyName, Constants::ROLE_MONITOR) + ->withUserEmail($creatorUserId) + ->withUserEmail($uploaderUserId); + + //If QC is awaiting add controllers + if ($qcNeeded) { + $mailListBuilder->withUsersEmailsByRolesInStudy($studyName, Constants::ROLE_CONTROLLER); + } + + $this->mailServices->sendUploadedVisitMessage($mailListBuilder->get(), $visitId, $studyName, $patientId, $patientCode, $visitType); + + if ($qcNeeded) { + $this->jobInterface->sendQcReportJob($visitId); + } + + if (!$qcNeeded && $reviewNeeded) { + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); + $mailListBuilder->withUsersEmailsByRolesInStudy($studyName, Constants::ROLE_REVIEWER); + $this->mailServices->sendReviewReadyMessage($mailListBuilder->get(), $visitId, $studyName, $patientId, $patientCode, $visitType); + } + } + + protected function onQcModified(QCModifiedEvent $qcModifiedEvent) + { + $studyName = $qcModifiedEvent->getStudyName(); + $creatorId = $qcModifiedEvent->getCreatorUserId(); + $patientId = $qcModifiedEvent->getPatientId(); + $patientCode = $qcModifiedEvent->getPatientCode(); + $patientCenterCode = $qcModifiedEvent->getPatientCenterCode(); + $visitId = $qcModifiedEvent->getVisitId(); + $visitType = $qcModifiedEvent->getVisitTypeName(); + $currentUserId = $qcModifiedEvent->getCurrentUserId(); + $visitModality = $qcModifiedEvent->getVisitModality(); + $qcStatus = $qcModifiedEvent->getQcStatus(); + + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); + $mailListBuilder->withUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR) + ->withUsersEmailsByRolesInStudy($studyName, Constants::ROLE_MONITOR) + ->withUserEmail($creatorId) + ->withUserEmail($currentUserId) + ->withInvestigatorOfCenterInStudy($studyName, $patientCenterCode); + + $this->mailServices->sendQcDecisionMessage( + $mailListBuilder->get(), + $visitId, + $studyName, + $qcStatus, + $patientId, + $patientCode, + $visitModality, + $visitType, + $qcModifiedEvent->getFormQcStatus(), + $qcModifiedEvent->getImageQcStatus(), + $qcModifiedEvent->getFormQcComment(), + $qcModifiedEvent->getImageQcComment() + ); + } + + protected function onCorrectiveAction(CorrectiveActionEvent $correctiveActionEvent) + { + $studyName = $correctiveActionEvent->getStudyName(); + $patientId = $correctiveActionEvent->getPatientId(); + $patientCode = $correctiveActionEvent->getPatientCode(); + $visitId = $correctiveActionEvent->getVisitId(); + $visitType = $correctiveActionEvent->getVisitTypeName(); + $currentUserId = $correctiveActionEvent->getCurrentUserId(); + $visitModality = $correctiveActionEvent->getVisitModality(); + $correctiveActionDone = $correctiveActionEvent->getCorrectiveActionDone(); + + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); + $mailListBuilder->withUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR) + ->withUsersEmailsByRolesInStudy($studyName, Constants::ROLE_CONTROLLER) + ->withUserEmail($currentUserId); + + $this->mailServices->sendCorrectiveActionMessage( + $mailListBuilder->get(), + $visitId, + $studyName, + $correctiveActionDone, + $patientId, + $patientCode, + $visitModality, + $visitType + ); + } + + protected function onAwaitingAdjudication(AwaitingAdjudicationEvent $event) + { + $studyName = $event->getStudyName(); + $patientId = $event->getPatientId(); + $patientCode = $event->getPatientCode(); + $visitId = $event->getVisitId(); + $visitType = $event->getVisitTypeName(); + + //Get All Users with Reviwers in this study + $reviewersUsers = $this->userRepositoryInterface->getUsersByRolesInStudy($studyName, Constants::ROLE_REVIEWER); + + //Get All Reviews of this visit + $reviews = $this->reviewRepositoryInterface->getReviewsForStudyVisit($studyName, $visitId, true); + $reviewerDoneUserIdArray = array_map(function ($user) { + return $user['user_id']; + }, $reviews); + + //Select users who didn't validate review form of this visit + $availableReviewers = array_filter($reviewersUsers, function ($user) use ($reviewerDoneUserIdArray) { + return !in_array($user['id'], $reviewerDoneUserIdArray); + }); + + //Build email list + $availableReviewersEmails = array_map(function ($user) { + return $user['email']; + }, $availableReviewers); + + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); + $mailListBuilder->withUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR); + $supervisorEmails = $mailListBuilder->get(); + + $emails = [ + ...$availableReviewersEmails, + ...$supervisorEmails + ]; + + $this->mailServices->sendAwaitingAdjudicationMessage($emails, $studyName, $patientId, $patientCode, $visitType, $visitId); + } + + protected function onVisitConcluded(VisitConcludedEvent $event) + { + $studyName = $event->getStudyName(); + $patientId = $event->getPatientId(); + $patientCode = $event->getPatientCode(); + $visitId = $event->getVisitId(); + $visitType = $event->getVisitTypeName(); + $conclusion = $event->getConclusion(); + $uploaderUserId = $event->getUploaderUserId(); + + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); + $mailListBuilder->withUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR) + ->withUsersEmailsByRolesInStudy($studyName, Constants::ROLE_MONITOR); + + //If uplaoder need to be included + if ($uploaderUserId) { + $mailListBuilder->withUserEmail($uploaderUserId); + } + + $this->mailServices->sendVisitConcludedMessage( + $mailListBuilder->get(), + $visitId, + $studyName, + $patientId, + $patientCode, + $visitType, + $conclusion, + ); + } + /** * Facade to instanciate specific study object, if does not exist, return the DefaultGaelOStudy Object */ @@ -46,5 +267,4 @@ public static function getSpecificStudyObject(string $studyName): AbstractGaelOS if (class_exists($class)) return FrameworkAdapter::make($class); else return FrameworkAdapter::make(DefaultGaelOStudy::class); } - } diff --git a/GaelO2/app/GaelO/Services/GaelOStudiesService/AbstractVisitRules.php b/GaelO2/app/GaelO/Services/GaelOStudiesService/AbstractVisitRules.php index 320114ea4..592d45e25 100644 --- a/GaelO2/app/GaelO/Services/GaelOStudiesService/AbstractVisitRules.php +++ b/GaelO2/app/GaelO/Services/GaelOStudiesService/AbstractVisitRules.php @@ -30,6 +30,11 @@ abstract public static function getReviewerValidationRules(): array; abstract public static function getReviewerAdjudicationValidationRules(): array; + /** + * @return AssociatedFile[] + */ + abstract public static function getAssociatedFilesVisit() : array; + /** * @return AssociatedFile[] */ diff --git a/GaelO2/app/GaelO/Services/GaelOStudiesService/AssociatedFiles/AssociatedFile.php b/GaelO2/app/GaelO/Services/GaelOStudiesService/AssociatedFiles/AssociatedFile.php index e4bda6bf4..433c10376 100644 --- a/GaelO2/app/GaelO/Services/GaelOStudiesService/AssociatedFiles/AssociatedFile.php +++ b/GaelO2/app/GaelO/Services/GaelOStudiesService/AssociatedFiles/AssociatedFile.php @@ -4,7 +4,7 @@ class AssociatedFile { - public String $key; + public string $key; public array $mimes; public bool $mandatory; diff --git a/GaelO2/app/GaelO/Services/GaelOStudiesService/DefaultVisitRules.php b/GaelO2/app/GaelO/Services/GaelOStudiesService/DefaultVisitRules.php index 04468de79..7c3e49ba9 100644 --- a/GaelO2/app/GaelO/Services/GaelOStudiesService/DefaultVisitRules.php +++ b/GaelO2/app/GaelO/Services/GaelOStudiesService/DefaultVisitRules.php @@ -10,7 +10,7 @@ public static function getInvestigatorValidationRules(): array return []; } - public static function getReviewerValidationRules(): array + public static function getReviewerValidationRules(): array { return []; } @@ -20,6 +20,11 @@ public static function getReviewerAdjudicationValidationRules(): array return []; } + public static function getAssociatedFilesVisit(): array + { + return []; + } + public static function getAssociatedFilesInvestigator(): array { return []; @@ -39,6 +44,4 @@ public static function getVisitDecisionClass(): string { return DefaultVisitDecisions::class; } - - } diff --git a/GaelO2/app/GaelO/Services/GaelOStudiesService/Events/AwaitingAdjudicationEvent.php b/GaelO2/app/GaelO/Services/GaelOStudiesService/Events/AwaitingAdjudicationEvent.php new file mode 100644 index 000000000..d7177d26d --- /dev/null +++ b/GaelO2/app/GaelO/Services/GaelOStudiesService/Events/AwaitingAdjudicationEvent.php @@ -0,0 +1,12 @@ +type = $type; + } +} diff --git a/GaelO2/app/GaelO/Services/GaelOStudiesService/Events/BaseVisitEvent.php b/GaelO2/app/GaelO/Services/GaelOStudiesService/Events/BaseVisitEvent.php new file mode 100644 index 000000000..25f06e8d9 --- /dev/null +++ b/GaelO2/app/GaelO/Services/GaelOStudiesService/Events/BaseVisitEvent.php @@ -0,0 +1,61 @@ +visitEntity = $visitEntity; + } + + public function getStudyName(): string + { + return $this->visitEntity['patient']['study_name']; + } + + public function getVisitId(): int + { + return $this->visitEntity['id']; + } + + public function getPatientId(): string + { + return $this->visitEntity['patient_id']; + } + + public function getPatientCode(): string + { + return $this->visitEntity['patient']['code']; + } + + public function getPatientCenterCode(): string + { + return $this->visitEntity['patient']['center_code']; + } + + public function getVisitTypeName(): string + { + return $this->visitEntity['visit_type']['name']; + } + + public function isQcNeeded(): bool + { + return $this->visitEntity['state_quality_control'] !== QualityControlStateEnum::NOT_NEEDED->value; + } + + public function getCreatorUserId(): int + { + return $this->visitEntity['creator_user_id']; + } + + public function getVisitModality(): string + { + return $this->visitEntity['visit_type']['visit_group']['modality']; + } +} diff --git a/GaelO2/app/GaelO/Services/GaelOStudiesService/Events/CorrectiveActionEvent.php b/GaelO2/app/GaelO/Services/GaelOStudiesService/Events/CorrectiveActionEvent.php new file mode 100644 index 000000000..93bb2b29c --- /dev/null +++ b/GaelO2/app/GaelO/Services/GaelOStudiesService/Events/CorrectiveActionEvent.php @@ -0,0 +1,34 @@ +userId = $userId; + } + + public function getCurrentUserId(): int + { + return $this->userId; + } + + public function setCorrrectiveActionDone(bool $done): void + { + $this->correctiveActionDone = $done; + } + + public function getCorrectiveActionDone(): bool + { + return $this->correctiveActionDone; + } +} diff --git a/GaelO2/app/GaelO/Services/GaelOStudiesService/Events/GaelOStudyEventEnum.php b/GaelO2/app/GaelO/Services/GaelOStudiesService/Events/GaelOStudyEventEnum.php new file mode 100644 index 000000000..8546cc46d --- /dev/null +++ b/GaelO2/app/GaelO/Services/GaelOStudiesService/Events/GaelOStudyEventEnum.php @@ -0,0 +1,12 @@ +userId = $userId; + } + + public function getCurrentUserId(): int + { + return $this->userId; + } + + public function setQcStatus(string $qcStatus) :void + { + $this->qcStatus = $qcStatus; + } + + public function getQcStatus() :string + { + return $this->qcStatus; + } + + public function setFormQcStatus(string $status): void + { + $this->formQcStatus = $status; + } + + public function getFormQcStatus(): string + { + return $this->formQcStatus; + } + + public function setImageQcStatus(string $status): void + { + $this->imageQcStatus = $status; + } + + public function getImageQcStatus(): string + { + return $this->imageQcStatus; + } + + public function setFormQcComment(string $formQcComment): void + { + $this->formQcComment = $formQcComment; + } + + public function getFormQcComment(): string + { + return $this->formQcComment; + } + + public function setImageQcComment(string $imageQcComment): void + { + $this->imageQcComment = $imageQcComment; + } + + public function getImageQcComment(): string + { + return $this->imageQcComment; + } +} diff --git a/GaelO2/app/GaelO/Services/GaelOStudiesService/Events/VisitConcludedEvent.php b/GaelO2/app/GaelO/Services/GaelOStudiesService/Events/VisitConcludedEvent.php new file mode 100644 index 000000000..55cc72130 --- /dev/null +++ b/GaelO2/app/GaelO/Services/GaelOStudiesService/Events/VisitConcludedEvent.php @@ -0,0 +1,34 @@ +uploaderUserId = $userId; + } + + public function getUploaderUserId(): ?int + { + return $this->uploaderUserId; + } + + public function setConclusion(?string $conclusion): void + { + $this->conclusion = $conclusion; + } + + public function getConclusion(): ?string + { + return $this->conclusion; + } +} diff --git a/GaelO2/app/GaelO/Services/GaelOStudiesService/Events/VisitUploadedEvent.php b/GaelO2/app/GaelO/Services/GaelOStudiesService/Events/VisitUploadedEvent.php new file mode 100644 index 000000000..f2b4fd9d2 --- /dev/null +++ b/GaelO2/app/GaelO/Services/GaelOStudiesService/Events/VisitUploadedEvent.php @@ -0,0 +1,35 @@ +uploaderUserId = $userId; + } + + public function setReviewNeeded(bool $reviewNeeded): void + { + $this->reviewNeeded = $reviewNeeded; + } + + public function isReviewNeeded(): bool + { + return $this->reviewNeeded; + } + + public function getUploaderUserId() :int + { + return $this->uploaderUserId; + } + +} diff --git a/GaelO2/app/GaelO/Services/GaelOStudiesService/ExpectedPatient/ExpectedPatient.php b/GaelO2/app/GaelO/Services/GaelOStudiesService/ExpectedPatient/ExpectedPatient.php new file mode 100644 index 000000000..1f0f5a9c1 --- /dev/null +++ b/GaelO2/app/GaelO/Services/GaelOStudiesService/ExpectedPatient/ExpectedPatient.php @@ -0,0 +1,17 @@ +code = $code; + $this->centerCode = $centerCode; + $this->inclusionStatus = $inclusionStatus; + } +} diff --git a/GaelO2/app/GaelO/Services/MailService/MailListBuilder.php b/GaelO2/app/GaelO/Services/MailService/MailListBuilder.php new file mode 100644 index 000000000..d5886e558 --- /dev/null +++ b/GaelO2/app/GaelO/Services/MailService/MailListBuilder.php @@ -0,0 +1,66 @@ +userRepositoryInterface = $userRepositoryInterface; + } + + public function withUserEmail(int $userId): self + { + $email = $this->userRepositoryInterface->find($userId)['email']; + $this->emails[] = $email; + return $this; + } + + public function withAdminsEmails(): self + { + $admins = $this->userRepositoryInterface->getAdministrators(); + $adminsEmails = array_map(function ($user) { + return $user['email']; + }, $admins); + $this->emails = [...$adminsEmails, ...$this->emails]; + return $this; + } + + private function filterNonVerifiedEmailsUsers(array $users) + { + $emails = []; + foreach ($users as $user) { + if ($user['email_verified_at'] != null) $emails[] = $user['email']; + } + return $emails; + } + + public function withUsersEmailsByRolesInStudy(string $studyName, string $role): self + { + $users = $this->userRepositoryInterface->getUsersByRolesInStudy($studyName, $role); + //Filter user with a verified email (password have been set) + $emails = $this->filterNonVerifiedEmailsUsers($users); + $this->emails = [...$emails, ...$this->emails]; + return $this; + } + + public function withInvestigatorOfCenterInStudy(String $studyName, String $center, ?String $job = null): self + { + $users = $this->userRepositoryInterface->getInvestigatorsOfStudyFromCenter($studyName, $center, $job); + $emails = $this->filterNonVerifiedEmailsUsers($users); + $this->emails = [...$emails, ...$this->emails]; + return $this; + } + + public function get(): array + { + return $this->emails; + } +} diff --git a/GaelO2/app/GaelO/Services/MailServices.php b/GaelO2/app/GaelO/Services/MailServices.php index 21e124e2f..d97a476ce 100644 --- a/GaelO2/app/GaelO/Services/MailServices.php +++ b/GaelO2/app/GaelO/Services/MailServices.php @@ -5,77 +5,43 @@ use App\GaelO\Constants\Constants; use App\GaelO\Constants\MailConstants; use App\GaelO\Interfaces\Adapters\MailerInterface; -use App\GaelO\Interfaces\Repositories\ReviewRepositoryInterface; use App\GaelO\Interfaces\Repositories\StudyRepositoryInterface; use App\GaelO\Interfaces\Repositories\UserRepositoryInterface; -use App\GaelO\Repositories\ReviewRepository; +use App\GaelO\Services\MailService\MailListBuilder; class MailServices { private MailerInterface $mailInterface; private UserRepositoryInterface $userRepositoryInterface; - private ReviewRepository $reviewRepositoryInterface; private StudyRepositoryInterface $studyRepositoryInterface; public function __construct( MailerInterface $mailInterface, UserRepositoryInterface $userRepositoryInterface, - ReviewRepositoryInterface $reviewRepositoryInterface, - StudyRepositoryInterface $studyRepositoryInterface, + StudyRepositoryInterface $studyRepositoryInterface ) { $this->mailInterface = $mailInterface; $this->userRepositoryInterface = $userRepositoryInterface; - $this->reviewRepositoryInterface = $reviewRepositoryInterface; $this->studyRepositoryInterface = $studyRepositoryInterface; } - private function getUserEmail(int $userId): string + public function getStudyContactEmail(string $studyName): string { - return $this->userRepositoryInterface->find($userId)['email']; + $studyEntity = $this->studyRepositoryInterface->find($studyName); + return $studyEntity->contactEmail; } - private function getUserName(int $userId) + + public function getUserName(int $userId) { $userEntity = $this->userRepositoryInterface->find($userId); return $userEntity['firstname'] . ' ' . $userEntity['lastname']; } - private function getAdminsEmails(): array - { - $admins = $this->userRepositoryInterface->getAdministrators(); - $adminsEmails = array_map(function ($user) { - return $user['email']; - }, $admins); - return $adminsEmails; - } - - private function filterNonVerifiedEmailsUsers(array $users) - { - $emails = []; - foreach ($users as $user) { - if ($user['email_verified_at'] != null) $emails[] = $user['email']; - } - return $emails; - } - - private function getUsersEmailsByRolesInStudy(string $studyName, string $role) - { - $users = $this->userRepositoryInterface->getUsersByRolesInStudy($studyName, $role); - //Filter user with a verified email (password have been set) - return $this->filterNonVerifiedEmailsUsers($users); - } - - private function getInvestigatorOfCenterInStudy(String $studyName, String $center, ?String $job = null): array - { - $users = $this->userRepositoryInterface->getInvestigatorsOfStudyFromCenter($studyName, $center, $job); - return $this->filterNonVerifiedEmailsUsers($users); - } - - private function getStudyContactEmail(string $studyName): string + public function getUserEmail(int $userId) { - $studyEntity = $this->studyRepositoryInterface->find($studyName); - return $studyEntity->contactEmail; + return $this->userRepositoryInterface->find($userId)['email']; } public function sendRequestMessage(string $name, string $email, string $center, string $request): void @@ -87,7 +53,10 @@ public function sendRequestMessage(string $name, string $email, string $center, 'request' => $request ]; - $destinators = [...$this->getAdminsEmails(), $email]; + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); + $mailListBuilder->withAdminsEmails(); + $destinators = [...$mailListBuilder->get(), $email]; + $this->mailInterface->setTo($destinators); $this->mailInterface->setReplyTo(); $this->mailInterface->setParameters($parameters); @@ -110,7 +79,11 @@ public function sendAccountBlockedMessage(String $email, int $userId): void 'studies' => $studies ]; //Send to user and administrators - $this->mailInterface->setTo([$email, ...$this->getAdminsEmails()]); + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); + $mailListBuilder->withAdminsEmails(); + $destinators = [...$mailListBuilder->get(), $email]; + + $this->mailInterface->setTo($destinators); $this->mailInterface->setReplyTo(); $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_BLOCKED_ACCOUNT); @@ -125,7 +98,10 @@ public function sendAdminConnectedMessage(String $email, String $remoteAddress): 'remoteAddress' => $remoteAddress ]; //Send to administrators - $this->mailInterface->setTo($this->getAdminsEmails()); + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); + $mailListBuilder->withAdminsEmails(); + + $this->mailInterface->setTo($mailListBuilder->get()); $this->mailInterface->setReplyTo(); $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_ADMIN_LOGGED); @@ -143,7 +119,10 @@ public function sendForbiddenResetPasswordDueToDeactivatedAccount(String $userEm ]; //Send to administrators - $this->mailInterface->setTo([$userEmail, ...$this->getAdminsEmails()]); + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); + $mailListBuilder->withAdminsEmails(); + + $this->mailInterface->setTo([$userEmail, ...$mailListBuilder->get()]); $this->mailInterface->setReplyTo(); $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_CHANGE_PASSWORD_DEACTIVATED); @@ -161,14 +140,17 @@ public function sendImportPatientMessage(String $studyName, string $contactEmail ]; //Send to supervisors of the study - $this->mailInterface->setTo($this->getUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR)); + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); + $mailListBuilder->withUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR); + + $this->mailInterface->setTo($mailListBuilder->get()); $this->mailInterface->setReplyTo($contactEmail); $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_IMPORT_PATIENT); $this->mailInterface->send(); } - public function sendUploadedVisitMessage(int $visitId, int $uploadUserId, string $studyName, string $patientId, string $patientCode, string $visitType, bool $qcNeeded) + public function sendUploadedVisitMessage(array $emails, int $visitId, string $studyName, string $patientId, string $patientCode, string $visitType) { $parameters = [ @@ -180,28 +162,14 @@ public function sendUploadedVisitMessage(int $visitId, int $uploadUserId, string 'visitId' => $visitId ]; - //Send to supervisors and monitors of the study - $destinators = [ - $this->getUserEmail($uploadUserId), - ...$this->getUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR), - ...$this->getUsersEmailsByRolesInStudy($studyName, Constants::ROLE_MONITOR) - ]; - //If QC is awaiting add controllers - if ($qcNeeded) { - $destinators = [ - ...$destinators, - ...$this->getUsersEmailsByRolesInStudy($studyName, Constants::ROLE_CONTROLLER) - ]; - } - - $this->mailInterface->setTo($destinators); + $this->mailInterface->setTo($emails); $this->mailInterface->setReplyTo($this->getStudyContactEmail($studyName)); $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_UPLOADED_VISIT); $this->mailInterface->send(); } - public function sendReviewReadyMessage(int $visitId, string $studyName, string $patientId, string $patientCode, string $visitType) + public function sendReviewReadyMessage(array $emails, int $visitId, string $studyName, string $patientId, string $patientCode, string $visitType) { $parameters = [ @@ -212,7 +180,7 @@ public function sendReviewReadyMessage(int $visitId, string $studyName, string $ 'visitId' => $visitId ]; - $this->mailInterface->setTo($this->getUsersEmailsByRolesInStudy($studyName, Constants::ROLE_REVIEWER)); + $this->mailInterface->setTo($emails); $this->mailInterface->setReplyTo($this->getStudyContactEmail($studyName)); $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_REVIEW_READY); @@ -229,6 +197,8 @@ public function sendValidationFailMessage( string $errorMessage ) { + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); + $parameters = [ 'name' => 'User', 'idVisit' => $visitId, @@ -241,7 +211,7 @@ public function sendValidationFailMessage( 'errorMessage' => $errorMessage ]; - $this->mailInterface->setTo($this->getAdminsEmails()); + $this->mailInterface->setTo($mailListBuilder->withAdminsEmails()->get()); $this->mailInterface->setReplyTo(); $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_UPLOAD_FAILURE); @@ -249,11 +219,9 @@ public function sendValidationFailMessage( } public function sendQcDecisionMessage( + array $emails, int $visitId, - int $uploaderId, - int $controllerId, string $studyName, - int $centerCode, string $qcDecision, string $patientId, string $patientCode, @@ -280,27 +248,15 @@ public function sendQcDecisionMessage( 'imageComment' => $imageComment ]; - - $this->mailInterface->setTo( - [ - ...$this->getUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR), - ...$this->getUsersEmailsByRolesInStudy($studyName, Constants::ROLE_MONITOR), - $this->getUserEmail($uploaderId), - $this->getUserEmail($controllerId), - ...$this->getInvestigatorOfCenterInStudy($studyName, $centerCode) - ] - ); - + $this->mailInterface->setTo($emails); $this->mailInterface->setReplyTo($this->getStudyContactEmail($studyName)); $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_QC_DECISION); $this->mailInterface->send(); } - public function sendCorrectiveActionMessage(int $visitId, int $currentUserId, string $studyName, bool $correctionApplied, string $patientId, string $patientCode, string $visitModality, string $visitType) + public function sendCorrectiveActionMessage(array $emails, int $visitId, string $studyName, bool $correctionApplied, string $patientId, string $patientCode, string $visitModality, string $visitType) { - - $parameters = [ 'name' => 'User', 'correctionApplied' => $correctionApplied, @@ -312,14 +268,7 @@ public function sendCorrectiveActionMessage(int $visitId, int $currentUserId, st 'visitId' => $visitId ]; - $this->mailInterface->setTo( - [ - ...$this->getUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR), - ...$this->getUsersEmailsByRolesInStudy($studyName, Constants::ROLE_CONTROLLER), - $this->getUserEmail($currentUserId), - ] - ); - + $this->mailInterface->setTo($emails); $this->mailInterface->setReplyTo($this->getStudyContactEmail($studyName)); $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_CORRECTIVE_ACTION); @@ -340,11 +289,11 @@ public function sendUnlockMessage(int $visitId, int $currentUserId, string $role 'visitId' => $visitId ]; - $this->mailInterface->setTo([ - $this->getUserEmail($currentUserId), - ...$this->getUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR) - ]); + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); + $mailListBuilder->withUserEmail($currentUserId) + ->withUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR); + $this->mailInterface->setTo($mailListBuilder->get()); $this->mailInterface->setReplyTo($this->getStudyContactEmail($studyName)); $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_UNLOCK_REQUEST); @@ -364,17 +313,18 @@ public function sendUnlockQCMessage(int $visitId, int $currentUserId, string $st 'visitId' => $visitId ]; - $this->mailInterface->setTo([ - ...$this->getUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR) - ]); + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); + $mailListBuilder->withUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR) + ->withUserEmail($currentUserId); + $this->mailInterface->setTo($mailListBuilder->get()); $this->mailInterface->setReplyTo($this->getStudyContactEmail($studyName)); $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_UNLOCK_QC_REQUEST); $this->mailInterface->send(); } - public function sendAwaitingAdjudicationMessage(string $studyName, string $patientId, string $patientCode, string $visitType, int $visitId) + public function sendAwaitingAdjudicationMessage(array $emails, string $studyName, string $patientId, string $patientCode, string $visitType, int $visitId) { $parameters = [ @@ -386,39 +336,14 @@ public function sendAwaitingAdjudicationMessage(string $studyName, string $patie 'visitId' => $visitId ]; - //Get All Users with Reviwers in this study - $reviewersUsers = $this->userRepositoryInterface->getUsersByRolesInStudy($studyName, Constants::ROLE_REVIEWER); - - //Get All Reviews of this visit - $reviews = $this->reviewRepositoryInterface->getReviewsForStudyVisit($studyName, $visitId, true); - $reviewerDoneUserIdArray = array_map(function ($user) { - return $user['user_id']; - }, $reviews); - - //Select users who didn't validate review form of this visit - $availableReviewers = array_filter($reviewersUsers, function ($user) use ($reviewerDoneUserIdArray) { - return !in_array($user['id'], $reviewerDoneUserIdArray); - }); - - //Build email list - $availableReviewersEmails = array_map(function ($user) { - return $user['email']; - }, $availableReviewers); - - $this->mailInterface->setTo( - [ - ...$this->getUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR), - ...$availableReviewersEmails, - ] - ); - + $this->mailInterface->setTo($emails); $this->mailInterface->setReplyTo($this->getStudyContactEmail($studyName)); $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_ADJUDICATION); $this->mailInterface->send(); } - public function sendVisitConcludedMessage(int $visitId, ?int $uploaderId, string $studyName, string $patientId, string $patientCode, string $visitType, string $conclusionValue) + public function sendVisitConcludedMessage(array $emails, int $visitId, string $studyName, string $patientId, string $patientCode, string $visitType, string $conclusionValue) { $parameters = [ @@ -431,18 +356,7 @@ public function sendVisitConcludedMessage(int $visitId, ?int $uploaderId, string 'conclusionValue' => $conclusionValue ]; - $destinators = [ - ...$this->getUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR), - ...$this->getUsersEmailsByRolesInStudy($studyName, Constants::ROLE_MONITOR), - - ]; - //If uplaoder need to be included - if ($uploaderId) { - $destinators[] = $this->getUserEmail($uploaderId); - } - - $this->mailInterface->setTo($destinators); - + $this->mailInterface->setTo($emails); $this->mailInterface->setReplyTo($this->getStudyContactEmail($studyName)); $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_CONCLUSION); @@ -452,6 +366,8 @@ public function sendVisitConcludedMessage(int $visitId, ?int $uploaderId, string public function sendDeleteFormMessage(int $visitId, bool $investigatorForm, int $formOwnerId, string $studyName, string $patientId, string $patientCode, string $visitType) { + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); + $parameters = [ 'name' => $this->getUserName($formOwnerId), 'study' => $studyName, @@ -462,12 +378,7 @@ public function sendDeleteFormMessage(int $visitId, bool $investigatorForm, int 'formType' => $investigatorForm ? 'Investigator' : 'Review' ]; - $this->mailInterface->setTo( - [ - $this->getUserEmail($formOwnerId) - ] - ); - + $this->mailInterface->setTo($mailListBuilder->withUserEmail($formOwnerId)->get()); $this->mailInterface->setReplyTo($this->getStudyContactEmail($studyName)); $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_DELETED_FORM); @@ -476,6 +387,7 @@ public function sendDeleteFormMessage(int $visitId, bool $investigatorForm, int public function sendUnlockedFormMessage(int $visitId, bool $investigatorForm, int $requestingUserId, string $studyName, string $patientId, string $patientCode, string $visitType) { + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); $parameters = [ 'name' => $this->getUserName($requestingUserId), @@ -487,8 +399,9 @@ public function sendUnlockedFormMessage(int $visitId, bool $investigatorForm, in 'formType' => $investigatorForm ? 'Investigator' : 'Review' ]; - $this->mailInterface->setTo($this->getUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR)); + $mailListBuilder->withUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR); + $this->mailInterface->setTo($mailListBuilder->get()); $this->mailInterface->setReplyTo($this->getStudyContactEmail($studyName)); $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_UNLOCK_FORM); @@ -498,6 +411,7 @@ public function sendUnlockedFormMessage(int $visitId, bool $investigatorForm, in public function sendVisitNotDoneMessage(int $visitId, string $studyName, string $patientId, string $patientCode, string $visitType, string $reasonNotDone, int $userId) { + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); $parameters = [ 'patientId' => $patientId, @@ -510,9 +424,8 @@ public function sendVisitNotDoneMessage(int $visitId, string $studyName, string ]; $this->mailInterface->setTo( - $this->getUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR) + $mailListBuilder->withUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR)->get() ); - $this->mailInterface->setReplyTo($this->getStudyContactEmail($studyName)); $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_VISIT_NOT_DONE); @@ -528,13 +441,14 @@ public function sendReminder(string $senderId, array $userIds, string $studyName 'content' => $content ]; - $destinatorsEmails = array_map(function ($userId) { - return $this->getUserEmail($userId); - }, $userIds); - $senderEmail = $this->getUserEmail($senderId); + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); + foreach ($userIds as $userId) { + return $mailListBuilder->withUserEmail($userId); + }; + $mailListBuilder->withUserEmail($senderId); - $this->mailInterface->setTo([...$destinatorsEmails, $senderEmail]); - $this->mailInterface->setReplyTo($senderEmail); + $this->mailInterface->setTo($mailListBuilder->get()); + $this->mailInterface->setReplyTo($this->getUserEmail($senderId)); $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_REMINDER); $this->mailInterface->send(); @@ -548,9 +462,10 @@ public function sendPatientCreationRequest(int $senderId, string $studyName, str 'patients' => $patients ]; - $this->mailInterface->setTo( - $this->getUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR) - ); + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); + $mailListBuilder->withUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR); + + $this->mailInterface->setTo($mailListBuilder->get()); $this->mailInterface->setReplyTo($this->getUserEmail($senderId)); $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_REQUEST_PATIENT_CREATION); @@ -571,9 +486,10 @@ public function sendMailToSupervisors(int $senderId, string $studyName, string $ 'canReply' => true ]; - $this->mailInterface->setTo( - $this->getUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR) - ); + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); + $mailListBuilder->withUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR); + + $this->mailInterface->setTo($mailListBuilder->get()); $this->mailInterface->setReplyTo($this->getUserEmail($senderId)); $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_USER); @@ -589,14 +505,12 @@ public function sendMailToUser(int $senderId, array $userIds, ?string $studyName 'canReply' => true ]; - $this->mailInterface->setTo( - array_map(function ($userId) { - return $this->getUserEmail($userId); - }, $userIds) - ); - + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); + foreach ($userIds as $userId) { + $mailListBuilder->withUserEmail($userId); + } + $this->mailInterface->setTo($mailListBuilder->get()); $this->mailInterface->setReplyTo($this->getUserEmail($senderId)); - $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_USER); $this->mailInterface->send(); @@ -612,9 +526,9 @@ public function sendMailToAdministrators(int $senderId, string $studyName, strin 'canReply' => true ]; - $this->mailInterface->setTo( - $this->getAdminsEmails() - ); + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface); + $mailListBuilder->withAdminsEmails(); + $this->mailInterface->setTo($mailListBuilder->get()); $this->mailInterface->setReplyTo($this->getUserEmail($senderId)); $this->mailInterface->setParameters($parameters); $this->mailInterface->setBody(MailConstants::EMAIL_USER); @@ -680,4 +594,43 @@ public function sendQcReport(string $studyName, string $visitType, string $patie $this->mailInterface->setBody(MailConstants::EMAIL_QC_REPORT); $this->mailInterface->send(); } + + public function sendRadiomicsReport(string $studyName, string $patientCode, string $visitType, string $visitDate, string $imagePath, array $stats, int $uploaderId) + { + $parameters = [ + 'patientCode' => $patientCode, + 'visitType' => $visitType, + 'studyName' => $studyName, + 'visitDate' => $visitDate, + 'image_path' => [$imagePath], + 'stats' => $stats + ]; + + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface, $this->studyRepositoryInterface); + $mailListBuilder->withUserEmail($uploaderId) + ->withUsersEmailsByRolesInStudy($studyName, Constants::ROLE_SUPERVISOR); + + $this->mailInterface->setTo($mailListBuilder->get()); + $this->mailInterface->setReplyTo($this->getStudyContactEmail($studyName)); + $this->mailInterface->setParameters($parameters); + $this->mailInterface->setBody(MailConstants::EMAIL_RADIOMICS_REPORT); + $this->mailInterface->send(); + } + + + public function sendJobFailure(string $jobType, array $details, string $errorMessage) + { + $parameters = [ + 'jobType' => $jobType, + 'details' => $details, + 'errorMessage' => $errorMessage + ]; + + $mailListBuilder = new MailListBuilder($this->userRepositoryInterface, $this->studyRepositoryInterface); + $mailListBuilder->withAdminsEmails(); + $this->mailInterface->setTo($mailListBuilder->get()); + $this->mailInterface->setParameters($parameters); + $this->mailInterface->setBody(MailConstants::EMAIL_JOB_FAILURE); + $this->mailInterface->send(); + } } diff --git a/GaelO2/app/GaelO/Services/OrthancService.php b/GaelO2/app/GaelO/Services/OrthancService.php index 7ae785608..904b4ebaa 100644 --- a/GaelO2/app/GaelO/Services/OrthancService.php +++ b/GaelO2/app/GaelO/Services/OrthancService.php @@ -86,7 +86,7 @@ public function addPeer(string $name, string $url, string $username, string $pas */ public function deletePeer(string $name) { - $this->httpClientInterface->rowRequest('DELETE', '/peers/' . $name, null, null); + $this->httpClientInterface->rawRequest('DELETE', '/peers/' . $name, null, null); } /** @@ -132,13 +132,13 @@ public function searchInOrthanc( public function deleteFromOrthanc(string $level, string $uid) { - $this->httpClientInterface->rowRequest('DELETE', '/' . $level . '/' . $uid, null, null); + $this->httpClientInterface->rawRequest('DELETE', '/' . $level . '/' . $uid, null, null); } public function isPeerAccelerated(string $peer): bool { - $peers = $this->httpClientInterface->rowRequest('GET', '/transfers/peers', null, null)->getJsonBody(); + $peers = $this->httpClientInterface->rawRequest('GET', '/transfers/peers', null, null)->getJsonBody(); if ($peers[$peer] == "installed") { return true; @@ -376,7 +376,7 @@ public function getOrthancZipStream(array $seriesOrthancIDs, ?string $transfertS $this->httpClientInterface->streamResponse('POST', '/tools/create-archive', $payload); } - public function getOrthancZipStreamAsString(array $seriesOrthancIDs, ?string $transfertSyntaxUID = null): Psr7ResponseAdapter + public function getOrthancZipAsStream(array $seriesOrthancIDs, ?string $transfertSyntaxUID = null): Psr7ResponseAdapter { $payload = [ 'Resources' => $seriesOrthancIDs diff --git a/GaelO2/app/GaelO/Services/PdfServices.php b/GaelO2/app/GaelO/Services/PdfServices.php new file mode 100644 index 000000000..4b0384fa6 --- /dev/null +++ b/GaelO2/app/GaelO/Services/PdfServices.php @@ -0,0 +1,32 @@ +pdfInterface = $pdfInterface; + } + + public function saveRadiomicsPdf(string $studyName, string $patientCode, string $visitType, string $visitDate, array $stats): string + { + $parameters = [ + 'patientCode' => $patientCode, + 'visitType' => $visitType, + 'studyName' => $studyName, + 'visitDate' => $visitDate, + 'stats' => $stats + ]; + + $pdfReportTempFile = tempnam(ini_get('upload_tmp_dir'), 'TMP_pdf_'); + $this->pdfInterface->saveViewToPdf('mails.mail_radiomics_report', $parameters, $pdfReportTempFile); + + return $pdfReportTempFile; + } +} diff --git a/GaelO2/app/GaelO/Services/SpecificStudiesRules/TEST/TEST.php b/GaelO2/app/GaelO/Services/SpecificStudiesRules/TEST/TEST.php index a8b419573..aa7453f3f 100644 --- a/GaelO2/app/GaelO/Services/SpecificStudiesRules/TEST/TEST.php +++ b/GaelO2/app/GaelO/Services/SpecificStudiesRules/TEST/TEST.php @@ -4,6 +4,8 @@ use App\GaelO\Services\GaelOStudiesService\AbstractGaelOStudy; use App\GaelO\Services\GaelOStudiesService\DefaultVisitRules; +use App\GaelO\Services\GaelOStudiesService\Events\BaseStudyEvent; +use App\GaelO\Services\GaelOStudiesService\Events\VisitUploadedEvent; class TEST extends AbstractGaelOStudy { diff --git a/GaelO2/app/GaelO/Services/SpecificStudiesRules/TEST/TEST_FDG_PET0.php b/GaelO2/app/GaelO/Services/SpecificStudiesRules/TEST/TEST_FDG_PET0.php index 366fe8f13..dd27f0395 100644 --- a/GaelO2/app/GaelO/Services/SpecificStudiesRules/TEST/TEST_FDG_PET0.php +++ b/GaelO2/app/GaelO/Services/SpecificStudiesRules/TEST/TEST_FDG_PET0.php @@ -80,11 +80,19 @@ public static function getReviewerAdjudicationValidationRules(): array return []; } + public static function getAssociatedFilesVisit(): array + { + return [ + 'prediction' => new AssociatedFile('prediction', [MimeAdapter::getMimeFromExtension('csv')], false), + 'tmtv41' => new AssociatedFile('tmtv41', [MimeAdapter::getMimeFromExtension('gz')], false) + ]; + } + public static function getAssociatedFilesInvestigator(): array { return [ - '41' => new AssociatedFile('41', MimeAdapter::getMimesFromExtension('csv'), true), - '25' => new AssociatedFile('25', MimeAdapter::getMimesFromExtension('zip'), false) + '41' => new AssociatedFile('41', [MimeAdapter::getMimeFromExtension('csv')], true), + '25' => new AssociatedFile('25', [MimeAdapter::getMimeFromExtension('zip')], false) ]; } diff --git a/GaelO2/app/GaelO/Services/SpecificStudiesRules/TEST/TEST_WB_CT0.php b/GaelO2/app/GaelO/Services/SpecificStudiesRules/TEST/TEST_WB_CT0.php index 5cb3260ea..f1a7feb79 100644 --- a/GaelO2/app/GaelO/Services/SpecificStudiesRules/TEST/TEST_WB_CT0.php +++ b/GaelO2/app/GaelO/Services/SpecificStudiesRules/TEST/TEST_WB_CT0.php @@ -29,6 +29,11 @@ public static function getReviewerValidationRules(): array ]; } + public static function getAssociatedFilesVisit(): array + { + return []; + } + public static function getAssociatedFilesInvestigator(): array { return []; diff --git a/GaelO2/app/GaelO/Services/TusService.php b/GaelO2/app/GaelO/Services/TusService.php index c0d76571d..674521251 100644 --- a/GaelO2/app/GaelO/Services/TusService.php +++ b/GaelO2/app/GaelO/Services/TusService.php @@ -32,7 +32,7 @@ public function getFile(string $tusFileId) : string { } public function deleteFile(string $tusFileId) : void { - $this->httpClientInterface->rowRequest('DELETE', '/api/tus/'.$tusFileId, null, ['Tus-Resumable' => '1.0.0'] ); + $this->httpClientInterface->rawRequest('DELETE', '/api/tus/'.$tusFileId, null, ['Tus-Resumable' => '1.0.0'] ); } } diff --git a/GaelO2/app/GaelO/Services/VisitService.php b/GaelO2/app/GaelO/Services/VisitService.php index 9bea498db..adca68ec8 100644 --- a/GaelO2/app/GaelO/Services/VisitService.php +++ b/GaelO2/app/GaelO/Services/VisitService.php @@ -2,38 +2,41 @@ namespace App\GaelO\Services; -use App\GaelO\Constants\Constants; use App\GaelO\Constants\Enums\InvestigatorFormStateEnum; use App\GaelO\Constants\Enums\QualityControlStateEnum; use App\GaelO\Constants\Enums\ReviewStatusEnum; use App\GaelO\Constants\Enums\UploadStatusEnum; -use App\GaelO\Interfaces\Adapters\JobInterface; +use App\GaelO\Exceptions\GaelOBadRequestException; +use App\GaelO\Exceptions\GaelOForbiddenException; +use App\GaelO\Exceptions\GaelONotFoundException; +use App\GaelO\Interfaces\Adapters\FrameworkInterface; use App\GaelO\Repositories\ReviewRepository; use App\GaelO\Repositories\ReviewStatusRepository; use App\GaelO\Repositories\VisitRepository; +use App\GaelO\Services\GaelOStudiesService\AbstractGaelOStudy; +use App\GaelO\Services\GaelOStudiesService\Events\QCModifiedEvent; +use App\GaelO\Services\GaelOStudiesService\Events\VisitUploadedEvent; class VisitService { private VisitRepository $visitRepository; private ReviewRepository $reviewRepository; - private MailServices $mailServices; private ReviewStatusRepository $reviewStatusRepository; - private JobInterface $jobInterface; + private FrameworkInterface $frameworkInterface; private int $visitId; + private int $currentUserId; public function __construct( + FrameworkInterface $frameworkInterface, VisitRepository $visitRepository, ReviewRepository $reviewRepository, - ReviewStatusRepository $reviewStatusRepository, - MailServices $mailServices, - JobInterface $jobInterface + ReviewStatusRepository $reviewStatusRepository ) { + $this->frameworkInterface = $frameworkInterface; $this->visitRepository = $visitRepository; - $this->mailServices = $mailServices; $this->reviewStatusRepository = $reviewStatusRepository; $this->reviewRepository = $reviewRepository; - $this->jobInterface = $jobInterface; } public function setVisitId(int $visitId) @@ -41,6 +44,11 @@ public function setVisitId(int $visitId) $this->visitId = $visitId; } + public function setCurrentUserId(int $currentUserId) + { + $this->currentUserId = $currentUserId; + } + public function getVisitContext(): array { return $this->visitRepository->getVisitContext($this->visitId); @@ -49,13 +57,12 @@ public function getVisitContext(): array public function updateUploadStatus(string $uploadStatus) { - if($uploadStatus === UploadStatusEnum::NOT_DONE->value){ - $visitContext = $this->visitRepository->getVisitContext($this->visitId); - if($visitContext['state_investigator_form'] === InvestigatorFormStateEnum::DONE->value) { + if ($uploadStatus === UploadStatusEnum::NOT_DONE->value) { + $visitContext = $this->getVisitContext($this->visitId); + if ($visitContext['state_investigator_form'] === InvestigatorFormStateEnum::DONE->value) { $this->reviewRepository->unlockInvestigatorForm($this->visitId); $this->updateInvestigatorFormStatus(InvestigatorFormStateEnum::DRAFT->value); } - } $updatedEntity = $this->visitRepository->updateUploadStatus($this->visitId, $uploadStatus); @@ -64,7 +71,7 @@ public function updateUploadStatus(string $uploadStatus) $updatedEntity['upload_status'] === UploadStatusEnum::DONE->value && ($updatedEntity['state_investigator_form'] === InvestigatorFormStateEnum::NOT_NEEDED->value || $updatedEntity['state_investigator_form'] === InvestigatorFormStateEnum::DONE->value) ) { - $this->sendUploadEmailAndSkipQcIfNeeded($this->visitId); + $this->visitUploaded($this->visitId); } } @@ -75,41 +82,39 @@ public function updateInvestigatorFormStatus(string $stateInvestigatorForm) $updatedEntity['upload_status'] === UploadStatusEnum::DONE->value && ($updatedEntity['state_investigator_form'] === InvestigatorFormStateEnum::DONE->value) ) { - $this->sendUploadEmailAndSkipQcIfNeeded($this->visitId); + $this->visitUploaded($this->visitId); } } - private function sendUploadEmailAndSkipQcIfNeeded() + /** + * Skip QC if needed and send email notification of uploaded visit + */ + private function visitUploaded() { - //If uploaded done and investigator done (Done or Not Needed) send notification message - $visitEntity = $this->visitRepository->getVisitContext($this->visitId); - - $patientId = $visitEntity['patient_id']; - $visitType = $visitEntity['visit_type']['name']; + $visitEntity = $this->getVisitContext($this->visitId); $studyName = $visitEntity['patient']['study_name']; - $patientCode = $visitEntity['patient']['code']; - $reviewStatus = $this->getReviewStatus($studyName); $qcNeeded = $visitEntity['state_quality_control'] !== QualityControlStateEnum::NOT_NEEDED->value; $reviewNeeded = $reviewStatus['review_status'] !== ReviewStatusEnum::NOT_NEEDED->value; - $this->mailServices->sendUploadedVisitMessage($this->visitId, $visitEntity['creator_user_id'], $studyName, $patientId, $patientCode, $visitType, $qcNeeded); - // Send auto qc job - if($qcNeeded){ - $this->jobInterface->sendQcReportJob($this->visitId); - } //If Qc NotNeeded mark visit as available for review - if (!$qcNeeded && $reviewNeeded ) { + if (!$qcNeeded && $reviewNeeded) { $this->reviewStatusRepository->updateReviewAvailability($this->visitId, $studyName, true); - $this->mailServices->sendReviewReadyMessage($this->visitId, $studyName, $patientId, $patientCode, $visitType); } + + //Notify of the upload done + $visitUploadedEvent = new VisitUploadedEvent($visitEntity); + $visitUploadedEvent->setReviewNeeded($reviewNeeded); + $visitUploadedEvent->setUploaderUserId($this->currentUserId); + $studyObject = AbstractGaelOStudy::getSpecificStudyObject($studyName); + $studyObject->onEventStudy($visitUploadedEvent); } public function editQc(string $stateQc, int $controllerId, ?bool $imageQc, ?bool $formQc, ?string $imageQcComment, ?string $formQcComment) { - $visitEntity = $this->visitRepository->getVisitContext($this->visitId); + $visitEntity = $this->getVisitContext($this->visitId); $studyName = $visitEntity['patient']['study_name']; $reviewStatus = $this->getReviewStatus($studyName); @@ -127,16 +132,27 @@ public function editQc(string $stateQc, int $controllerId, ?bool $imageQc, ?bool if ($stateQc === QualityControlStateEnum::ACCEPTED->value && $reviewNeeded) { //Invalidate invistagator form and set it status as draft in the visit - $this->reviewStatusRepository->updateReviewAvailability($this->visitId, $studyName , true); + $this->reviewStatusRepository->updateReviewAvailability($this->visitId, $studyName, true); } + + $qcModifiedEvent = new QCModifiedEvent($visitEntity); + $qcModifiedEvent->setCurrentUserId($controllerId); + $qcModifiedEvent->setQcStatus($stateQc); + $qcModifiedEvent->setFormQcStatus($formQc ? 'Accepted ' : 'Refused'); + $qcModifiedEvent->setImageQcStatus($imageQc ? 'Accepted ' : 'Refused'); + $qcModifiedEvent->setFormQcComment($formQcComment ?? 'None'); + $qcModifiedEvent->setImageQcComment($imageQcComment ?? 'None'); + + $studyObject = AbstractGaelOStudy::getSpecificStudyObject($studyName); + $studyObject->onEventStudy($qcModifiedEvent); } public function resetQc(): void { - $visitEntity = $this->visitRepository->getVisitContext($this->visitId); + $visitEntity = $this->getVisitContext($this->visitId); $studyName = $visitEntity['patient']['study_name']; $this->visitRepository->resetQc($this->visitId); - $this->reviewStatusRepository->updateReviewAvailability($this->visitId, $studyName , false); + $this->reviewStatusRepository->updateReviewAvailability($this->visitId, $studyName, false); } public function getReviewStatus(string $studyName) @@ -144,4 +160,55 @@ public function getReviewStatus(string $studyName) return $this->reviewStatusRepository->getReviewStatus($this->visitId, $studyName); } + public function attachFile(string $key, string $mimeType, string $extension, $binaryData): string + { + $visitEntity = $this->getVisitContext($this->visitId); + $studyName = $visitEntity['patient']['study_name']; + $visitGroupName = $visitEntity['visit_type']['visit_group']['name']; + $visitTypeName = $visitEntity['visit_type']['name']; + + $studyRule = AbstractGaelOStudy::getSpecificStudyObject($studyName); + $abstractVisitRules = $studyRule->getSpecificVisitRules($visitGroupName, $visitTypeName); + $associatedFilesVisit = $abstractVisitRules->getAssociatedFilesVisit(); + + if (!empty($visitEntity['sent_files'][$key])) { + throw new GaelOBadRequestException("Already Existing File for this visit"); + } + + if (!array_key_exists($key, $associatedFilesVisit)) { + throw new GaelOForbiddenException("Unexpected file key"); + } + + $associatiedFile = $associatedFilesVisit[$key]; + + if (!in_array($mimeType, $associatiedFile->mimes)) { + throw new GaelOBadRequestException("Mime Not Allowed"); + } + + $destinationPath = $studyName . '/' . 'attached_visit_file'; + + $filename = 'visit_' . $this->visitId . '_' . $key . '.' . $extension; + $destinationFileName = $destinationPath . '/' . $filename; + + $this->frameworkInterface->storeFile($destinationFileName, $binaryData); + + $visitEntity['sent_files'][$key] = $destinationFileName; + $this->visitRepository->updateVisitFile($visitEntity['id'], $visitEntity['sent_files']); + return $filename; + } + + public function removeFile(string $key): void + { + $visitEntity = $this->getVisitContext($this->visitId); + + if (empty($visitEntity['sent_files'][$key])) { + throw new GaelONotFoundException('Non exisiting key file in review'); + } + + $targetedFile = $visitEntity['sent_files'][$key]; + $this->frameworkInterface->deleteFile($targetedFile); + + unset($visitEntity['sent_files'][$key]); + $this->visitRepository->updateVisitFile($visitEntity['id'], $visitEntity['sent_files']); + } } diff --git a/GaelO2/app/GaelO/UseCases/CreateFileToForm/CreateFileToForm.php b/GaelO2/app/GaelO/UseCases/CreateFileToForm/CreateFileToForm.php index cc282a43a..fd7849c93 100644 --- a/GaelO2/app/GaelO/UseCases/CreateFileToForm/CreateFileToForm.php +++ b/GaelO2/app/GaelO/UseCases/CreateFileToForm/CreateFileToForm.php @@ -70,8 +70,6 @@ public function execute(CreateFileToFormRequest $createFileToReviewRequest, Crea $extension = $this->mimeInterface::getExtensionsFromMime($createFileToReviewRequest->contentType)[0]; - $fileName = 'review_' . $reviewId . '_' . $key . '.' . $extension; - $visitContext = $this->visitRepositoryInterface->getVisitWithContextAndReviewStatus($visitId, $studyName); $formService = null; @@ -83,11 +81,12 @@ public function execute(CreateFileToFormRequest $createFileToReviewRequest, Crea } $formService->setVisitContextAndStudy($visitContext, $studyName); - $formService->attachFile($reviewEntity, $key, $fileName, $createFileToReviewRequest->contentType, base64_decode($binaryData)); + + $filename = $formService->attachFile($reviewEntity, $key, $createFileToReviewRequest->contentType, $extension, base64_decode($binaryData)); $actionDetails = [ 'uploaded_file' => $key, - 'filename' => $fileName, + 'filename' => $filename, 'review_id' => $reviewId ]; diff --git a/GaelO2/app/GaelO/UseCases/CreateFileToFormFromTus/CreateFileToFormFromTus.php b/GaelO2/app/GaelO/UseCases/CreateFileToFormFromTus/CreateFileToFormFromTus.php index cee1f0b28..19dca615f 100644 --- a/GaelO2/app/GaelO/UseCases/CreateFileToFormFromTus/CreateFileToFormFromTus.php +++ b/GaelO2/app/GaelO/UseCases/CreateFileToFormFromTus/CreateFileToFormFromTus.php @@ -14,6 +14,7 @@ use App\GaelO\Interfaces\Repositories\VisitRepositoryInterface; use App\GaelO\Services\AuthorizationService\AuthorizationReviewService; use App\GaelO\Services\AuthorizationService\AuthorizationVisitService; +use App\GaelO\Services\FormService\FormService; use App\GaelO\Services\FormService\InvestigatorFormService; use App\GaelO\Services\FormService\ReviewFormService; use App\GaelO\Services\OrthancService; @@ -141,8 +142,6 @@ public function execute(CreateFileToFormFromTusRequest $createFileToFormFromTusR $extension = $this->mimeInterface::getExtensionsFromMime($mime)[0]; } - $fileName = 'review_' . $reviewId . '_' . $key . '.' . $extension; - $visitContext = $this->visitRepositoryInterface->getVisitWithContextAndReviewStatus($visitId, $studyName); $formService = null; @@ -154,12 +153,13 @@ public function execute(CreateFileToFormFromTusRequest $createFileToFormFromTusR } $formService->setVisitContextAndStudy($visitContext, $studyName); - $formService->attachFile($reviewEntity, $key, $fileName, $mime, fopen($file, 'r')); + $filename = $formService->attachFile($reviewEntity, $key, $mime, $extension, fopen($file, 'r')); + //Remove temporary file unlink($file); $actionDetails = [ 'uploaded_file' => $key, - 'filename' => $fileName, + 'filename' => $filename, 'review_id' => $reviewId ]; diff --git a/GaelO2/app/GaelO/UseCases/CreateFileToVisit/CreateFileToVisit.php b/GaelO2/app/GaelO/UseCases/CreateFileToVisit/CreateFileToVisit.php new file mode 100644 index 000000000..def410fb8 --- /dev/null +++ b/GaelO2/app/GaelO/UseCases/CreateFileToVisit/CreateFileToVisit.php @@ -0,0 +1,111 @@ +authorizationVisitService = $authorizationVisitService; + $this->visitService = $visitService; + $this->trackerRepositoryInterface = $trackerRepositoryInterface; + $this->mimeInterface = $mimeInterface; + } + + public function execute(CreateFileToVisitRequest $createFileToVisitRequest, CreateFileToVisitResponse $createFileToVisitResponse) + { + + try { + + $visitId = $createFileToVisitRequest->visitId; + $key = $createFileToVisitRequest->key; + $content = $createFileToVisitRequest->content; + $currentUserId = $createFileToVisitRequest->currentUserId; + $contentType = $createFileToVisitRequest->contentType; + + $this->visitService->setVisitId($visitId); + $this->visitService->setCurrentUserId($currentUserId); + $visitContext = $this->visitService->getVisitContext(); + $studyName = $visitContext['patient']['study_name']; + + if ($createFileToVisitRequest->studyName !== $studyName) { + throw new GaelOForbiddenException('Should be called from original study'); + } + + if (!Util::isBase64Encoded($content)) { + throw new GaelOBadRequestException("Payload should be base64 encoded"); + } + + $this->checkAuthorization($visitId, $currentUserId, $studyName); + + if ($createFileToVisitRequest->extension == null) { + $extension = $this->mimeInterface::getExtensionsFromMime($createFileToVisitRequest->contentType)[0]; + } else { + $extension = $createFileToVisitRequest->extension; + if(strchr($extension, '.')) { + $finalExtension = strrchr($extension, '.'); + //remove first dot + $finalExtension = substr($finalExtension, 1); + $contentType = $this->mimeInterface::getMimeFromExtension($finalExtension); + }else{ + $contentType = $this->mimeInterface::getMimeFromExtension($extension); + } + + } + + $filename = $this->visitService->attachFile($key, $contentType, $extension, base64_decode($content)); + + $actionDetails = [ + 'uploaded_file' => $key, + 'filename' => $filename + ]; + + $this->trackerRepositoryInterface->writeAction( + $currentUserId, + Constants::ROLE_SUPERVISOR, + $studyName, + $visitId, + Constants::TRACKER_UPDATE_VISIT_FILE, + $actionDetails + ); + + $createFileToVisitResponse->status = 201; + $createFileToVisitResponse->statusText = 'Created'; + } catch (AbstractGaelOException $e) { + $createFileToVisitResponse->body = $e->getErrorBody(); + $createFileToVisitResponse->status = $e->statusCode; + $createFileToVisitResponse->statusText = $e->statusText; + } catch (Exception $e) { + throw $e; + } + } + + private function checkAuthorization(int $visitId, int $currentUserId, string $studyName): void + { + $this->authorizationVisitService->setVisitId($visitId); + $this->authorizationVisitService->setUserId($currentUserId); + $this->authorizationVisitService->setStudyName($studyName); + if (!$this->authorizationVisitService->isVisitAllowed(Constants::ROLE_SUPERVISOR)) throw new GaelOForbiddenException(); + } +} diff --git a/GaelO2/app/GaelO/UseCases/CreateFileToVisit/CreateFileToVisitRequest.php b/GaelO2/app/GaelO/UseCases/CreateFileToVisit/CreateFileToVisitRequest.php new file mode 100644 index 000000000..825a3d3cd --- /dev/null +++ b/GaelO2/app/GaelO/UseCases/CreateFileToVisit/CreateFileToVisitRequest.php @@ -0,0 +1,14 @@ +documentationMandatory; $contactEmail = $createStudyRequest->contactEmail; $ancillaryOf = $createStudyRequest->ancillaryOf; + $creatablePatientsInvestigator = $createStudyRequest->creatablePatientsInvestigator; if (preg_match('/[^A-Z0-9]/', $studyName)) { throw new GaelOBadRequestException('Only uppercase alphanumerical name allowed, no space or special characters'); @@ -70,7 +71,11 @@ public function execute(CreateStudyRequest $createStudyRequest, CreateStudyRespo throw new GaelOBadRequestException('Missing Monitor Show All'); } - $this->studyRepositoryInterface->addStudy($studyName, $studyCode, $patientCodeLength, $contactEmail, $controllerShowAll, $monitorShowAll, $documentationMandatory, $ancillaryOf); + if (!isset($creatablePatientsInvestigator)) { + throw new GaelOBadRequestException('Missing Creatable Patient Investigator'); + } + + $this->studyRepositoryInterface->addStudy($studyName, $studyCode, $patientCodeLength, $contactEmail, $controllerShowAll, $monitorShowAll, $documentationMandatory, $ancillaryOf, $creatablePatientsInvestigator); $currentUserId = $createStudyRequest->currentUserId; $actionDetails = [ diff --git a/GaelO2/app/GaelO/UseCases/CreateStudy/CreateStudyRequest.php b/GaelO2/app/GaelO/UseCases/CreateStudy/CreateStudyRequest.php index f536dc7d0..d4b55c670 100644 --- a/GaelO2/app/GaelO/UseCases/CreateStudy/CreateStudyRequest.php +++ b/GaelO2/app/GaelO/UseCases/CreateStudy/CreateStudyRequest.php @@ -13,4 +13,5 @@ class CreateStudyRequest public bool $controllerShowAll; public bool $documentationMandatory; public ?string $ancillaryOf = null; + public bool $creatablePatientsInvestigator; } diff --git a/GaelO2/app/GaelO/UseCases/DeleteFileOfVisit/DeleteFileOfVisit.php b/GaelO2/app/GaelO/UseCases/DeleteFileOfVisit/DeleteFileOfVisit.php new file mode 100644 index 000000000..0582c978f --- /dev/null +++ b/GaelO2/app/GaelO/UseCases/DeleteFileOfVisit/DeleteFileOfVisit.php @@ -0,0 +1,89 @@ +authorizationVisitService = $authorizationVisitService; + $this->trackerRepositoryInterface = $trackerRepositoryInterface; + $this->visitService = $visitService; + } + + public function execute(DeleteFileOfVisitRequest $deleteFileOfVisitRequest, DeleteFileOfVisitResponse $deleteFileOfVisitResponse) + { + try { + + $studyName = $deleteFileOfVisitRequest->studyName; + $visitId = $deleteFileOfVisitRequest->visitId; + $role = $deleteFileOfVisitRequest->role; + $currentUserId = $deleteFileOfVisitRequest->currentUserId; + $fileKey = $deleteFileOfVisitRequest->key; + + if ($role !== Constants::ROLE_SUPERVISOR) { + throw new GaelOForbiddenException("Supervisor role only"); + } + + $this->visitService->setVisitId($visitId); + $this->visitService->setCurrentUserId($currentUserId); + $visitContext = $this->visitService->getVisitContext(); + + if ($deleteFileOfVisitRequest->studyName !== $visitContext['patient']['study_name']) { + throw new GaelOForbiddenException('Should be called from original study'); + } + + $this->checkAuthorization($visitContext, $currentUserId); + + $this->visitService->removeFile($fileKey); + + $actionDetails = [ + 'removed_file' => $fileKey + ]; + + $this->trackerRepositoryInterface->writeAction( + $currentUserId, + Constants::ROLE_SUPERVISOR, + $studyName, + $visitId, + Constants::TRACKER_UPDATE_VISIT_FILE, + $actionDetails + ); + + $deleteFileOfVisitResponse->status = 200; + $deleteFileOfVisitResponse->statusText = 'OK'; + } catch (AbstractGaelOException $e) { + $deleteFileOfVisitResponse->body = $e->getErrorBody(); + $deleteFileOfVisitResponse->status = $e->statusCode; + $deleteFileOfVisitResponse->statusText = $e->statusText; + } catch (Exception $e) { + throw $e; + } + } + + private function checkAuthorization(array $visitContext, int $currentUserId): void + { + $this->authorizationVisitService->setUserId($currentUserId); + $this->authorizationVisitService->setVisitContext($visitContext); + $this->authorizationVisitService->setStudyName($visitContext['patient']['study_name']); + if (!$this->authorizationVisitService->isVisitAllowed(Constants::ROLE_SUPERVISOR)) { + throw new GaelOForbiddenException(); + }; + } +} diff --git a/GaelO2/app/GaelO/UseCases/DeleteFileOfVisit/DeleteFileOfVisitRequest.php b/GaelO2/app/GaelO/UseCases/DeleteFileOfVisit/DeleteFileOfVisitRequest.php new file mode 100644 index 000000000..0159030db --- /dev/null +++ b/GaelO2/app/GaelO/UseCases/DeleteFileOfVisit/DeleteFileOfVisitRequest.php @@ -0,0 +1,12 @@ +checkAuthorization($currentUserId, $visitId, $role, $studyName, $visitContext); + $this->dicomService->setCurrentUserId($currentUserId); $this->dicomService->deleteSeries($seriesInstanceUID, $role); $actionDetails = [ diff --git a/GaelO2/app/GaelO/UseCases/GetCreatablePatients/GetCreatablePatients.php b/GaelO2/app/GaelO/UseCases/GetCreatablePatients/GetCreatablePatients.php new file mode 100644 index 000000000..61c801911 --- /dev/null +++ b/GaelO2/app/GaelO/UseCases/GetCreatablePatients/GetCreatablePatients.php @@ -0,0 +1,72 @@ +authorizationStudyService = $authorizationStudyService; + $this->patientRepositoryInterface = $patientRepositoryInterface; + } + + public function execute(GetCreatablePatientsRequest $getCreatablePatientsRequest, GetCreatablePatientsResponse $getCreatablePatientsResponse) + { + try { + + $studyName = $getCreatablePatientsRequest->studyName; + $role = $getCreatablePatientsRequest->role; + $currentUserId = $getCreatablePatientsRequest->currentUserId; + + $this->checkAuthorization($currentUserId, $studyName, $role); + + $studyRule = AbstractGaelOStudy::getSpecificStudyObject($studyName); + $expectedPatients = $studyRule->getExpectedPatients(); + + if (sizeof($expectedPatients) > 0) { + $createdPatients = $this->patientRepositoryInterface->getPatientsInStudy($studyName, false); + $createdPatientsCode = array_column($createdPatients, 'code'); + $creatablePatients = array_values(array_filter($expectedPatients, function (ExpectedPatient $expectedPatient) use ($createdPatientsCode) { + return !in_array($expectedPatient->code, $createdPatientsCode); + })); + $getCreatablePatientsResponse->body = $creatablePatients; + } else { + $getCreatablePatientsResponse->body = []; + } + + $getCreatablePatientsResponse->status = 200; + $getCreatablePatientsResponse->statusText = 'OK'; + } catch (AbstractGaelOException $e) { + $getCreatablePatientsResponse->body = $e->getErrorBody(); + $getCreatablePatientsResponse->status = $e->statusCode; + $getCreatablePatientsResponse->statusText = $e->statusText; + } catch (Exception $e) { + throw $e; + } + } + + private function checkAuthorization(int $userId, string $studyName, string $role) + { + if (!in_array($role, [Constants::ROLE_INVESTIGATOR, Constants::ROLE_SUPERVISOR])) { + throw new GaelOForbiddenException(); + }; + $this->authorizationStudyService->setUserId($userId); + $this->authorizationStudyService->setStudyName($studyName); + if (!$this->authorizationStudyService->isAllowedStudy($role)) { + throw new GaelOForbiddenException(); + } + } +} diff --git a/GaelO2/app/GaelO/UseCases/GetCreatablePatients/GetCreatablePatientsRequest.php b/GaelO2/app/GaelO/UseCases/GetCreatablePatients/GetCreatablePatientsRequest.php new file mode 100644 index 000000000..f67785019 --- /dev/null +++ b/GaelO2/app/GaelO/UseCases/GetCreatablePatients/GetCreatablePatientsRequest.php @@ -0,0 +1,10 @@ +authorizationVisitService = $authorizationVisitService; + $this->visitRepositoryInterface = $visitRepositoryInterface; + } + + public function execute(GetFileOfVisitRequest $getFileOfVisitRequest, GetFileOfVisitResponse $getFileOfVisitResponse) + { + + try { + $visitId = $getFileOfVisitRequest->visitId; + $fileKey = $getFileOfVisitRequest->key; + $role = $getFileOfVisitRequest->role; + $studyName = $getFileOfVisitRequest->studyName; + $currentUserId = $getFileOfVisitRequest->currentUserId; + + $visitEntity = $this->visitRepositoryInterface->getVisitContext($visitId); + + $this->checkAuthorization($visitId, $currentUserId, $role, $studyName); + if(!array_key_exists($fileKey, $visitEntity['sent_files'])){ + throw new GaelONotFoundException("File key not found"); + } + + $getFileOfVisitResponse->status = 200; + $getFileOfVisitResponse->statusText = 'OK'; + $getFileOfVisitResponse->filePath = $visitEntity['sent_files'][$fileKey]; + $getFileOfVisitResponse->filename = basename($visitEntity['sent_files'][$fileKey]); + } catch (AbstractGaelOException $e) { + $getFileOfVisitResponse->status = $e->statusCode; + $getFileOfVisitResponse->statusText = $e->statusText; + $getFileOfVisitResponse->body = $e->getErrorBody(); + } catch (Exception $e) { + throw $e; + } + } + + private function checkAuthorization(int $visitId, int $currentUserId, string $role, string $studyName): void + { + //Check if visit is allowed + $this->authorizationVisitService->setVisitId($visitId); + $this->authorizationVisitService->setUserId($currentUserId); + $this->authorizationVisitService->setStudyName($studyName); + if (!$this->authorizationVisitService->isVisitAllowed($role)) throw new GaelOForbiddenException(); + } +} diff --git a/GaelO2/app/GaelO/UseCases/GetFileOfVisit/GetFileOfVisitRequest.php b/GaelO2/app/GaelO/UseCases/GetFileOfVisit/GetFileOfVisitRequest.php new file mode 100644 index 000000000..17c890486 --- /dev/null +++ b/GaelO2/app/GaelO/UseCases/GetFileOfVisit/GetFileOfVisitRequest.php @@ -0,0 +1,12 @@ +authorizationStudyService = $authorizationStudyService; + $this->visitTypeRepository = $visitTypeRepository; + } + + public function execute(GetFilesMetadataFromVisitTypeRequest $getFilesMetadataFromVisitTypeRequest, GetFilesMetadataFromVisitTypeResponse $getFilesMetadataFromVisitTypeResponse) + { + try { + + $studyName = $getFilesMetadataFromVisitTypeRequest->studyName; + + $this->checkAuthorization($getFilesMetadataFromVisitTypeRequest->currentUserId, $studyName); + + $visitTypeEntity = $this->visitTypeRepository->find($getFilesMetadataFromVisitTypeRequest->visitTypeId, true); + $originalStudyName = $visitTypeEntity['visit_group']['study_name']; + $visitGroupEntity = $visitTypeEntity['visit_group']; + + //Check that the requested study name is an original or ancillary study of this visit type + if (!AuthorizationStudyService::isOriginalOrAncillaryStudyOf($studyName, $originalStudyName)) { + throw new GaelOForbiddenException('Forbidden acces to this Visit Type'); + } + + $visitFiles = []; + + try { + $studyRule = AbstractGaelOStudy::getSpecificStudyObject($studyName); + $visitRules = $studyRule->getSpecificVisitRules($visitGroupEntity['name'], $visitTypeEntity['name']); + $visitFiles = $visitRules->getAssociatedFilesVisit(); + } catch (GaelOException $e) {} + + $getFilesMetadataFromVisitTypeResponse->body = $visitFiles; + $getFilesMetadataFromVisitTypeResponse->status = 200; + $getFilesMetadataFromVisitTypeResponse->statusText = 'OK'; + } catch (AbstractGaelOException $e) { + $getFilesMetadataFromVisitTypeResponse->body = $e->getErrorBody(); + $getFilesMetadataFromVisitTypeResponse->status = $e->statusCode; + $getFilesMetadataFromVisitTypeResponse->statusText = $e->statusText; + } catch (Exception $e) { + throw $e; + } + } + + private function checkAuthorization(int $userId, string $studyName) + { + $this->authorizationStudyService->setUserId($userId); + $this->authorizationStudyService->setStudyName($studyName); + if (!$this->authorizationStudyService->isAllowedStudy(Constants::ROLE_SUPERVISOR)) { + throw new GaelOForbiddenException(); + } + } +} diff --git a/GaelO2/app/GaelO/UseCases/GetFilesMetadataFromVisitType/GetFilesMetadataFromVisitTypeRequest.php b/GaelO2/app/GaelO/UseCases/GetFilesMetadataFromVisitType/GetFilesMetadataFromVisitTypeRequest.php new file mode 100644 index 000000000..117ad748f --- /dev/null +++ b/GaelO2/app/GaelO/UseCases/GetFilesMetadataFromVisitType/GetFilesMetadataFromVisitTypeRequest.php @@ -0,0 +1,11 @@ +setMainCenter(CenterEntity::fillFromDBReponseArray($data['main_center'])); } $userEntity->addRoles($data['roles']); $responseArray[] = $userEntity; diff --git a/GaelO2/app/GaelO/UseCases/ImportPatients/ImportPatients.php b/GaelO2/app/GaelO/UseCases/ImportPatients/ImportPatients.php index 44aa568aa..ad19b95e7 100644 --- a/GaelO2/app/GaelO/UseCases/ImportPatients/ImportPatients.php +++ b/GaelO2/app/GaelO/UseCases/ImportPatients/ImportPatients.php @@ -44,8 +44,10 @@ public function execute(ImportPatientsRequest $importPatientsRequest, ImportPati $studyName = $importPatientsRequest->studyName; $currentUserId = $importPatientsRequest->currentUserId; $patients = $importPatientsRequest->patients; + $role = $importPatientsRequest->role; + + $this->checkAuthorization($currentUserId, $studyName, $role); - $this->checkAuthorization($currentUserId, $studyName); $arrayPatients = []; foreach ($patients as $patient) { @@ -83,14 +85,20 @@ public function execute(ImportPatientsRequest $importPatientsRequest, ImportPati } } - private function checkAuthorization(int $userId, string $studyName) + private function checkAuthorization(int $userId, string $studyName, string $role) { $this->authorizationStudyService->setUserId($userId); $this->authorizationStudyService->setStudyName($studyName); if ($this->authorizationStudyService->getStudyEntity()->isAncillaryStudy()) { throw new GaelOForbiddenException("Forbidden for ancillary studies"); } - if (!$this->authorizationStudyService->isAllowedStudy(Constants::ROLE_SUPERVISOR)) { + if (!in_array($role, [Constants::ROLE_INVESTIGATOR, Constants::ROLE_SUPERVISOR])) { + throw new GaelOForbiddenException("Role forbidden"); + } + if ($role === Constants::ROLE_INVESTIGATOR && !$this->authorizationStudyService->getStudyEntity()->creatablePatientsInvestigator) { + throw new GaelOForbiddenException("Patient creation disallowed for investigator"); + } + if (!$this->authorizationStudyService->isAllowedStudy($role)) { throw new GaelOForbiddenException(); } } diff --git a/GaelO2/app/GaelO/UseCases/ImportPatients/ImportPatientsRequest.php b/GaelO2/app/GaelO/UseCases/ImportPatients/ImportPatientsRequest.php index 97e41237e..0dbbf42b9 100644 --- a/GaelO2/app/GaelO/UseCases/ImportPatients/ImportPatientsRequest.php +++ b/GaelO2/app/GaelO/UseCases/ImportPatients/ImportPatientsRequest.php @@ -7,4 +7,5 @@ class ImportPatientsRequest public int $currentUserId; public array $patients; public string $studyName; + public string $role; } diff --git a/GaelO2/app/GaelO/UseCases/ModifyCorrectiveAction/ModifyCorrectiveAction.php b/GaelO2/app/GaelO/UseCases/ModifyCorrectiveAction/ModifyCorrectiveAction.php index 58181ca3b..4dbdcf917 100644 --- a/GaelO2/app/GaelO/UseCases/ModifyCorrectiveAction/ModifyCorrectiveAction.php +++ b/GaelO2/app/GaelO/UseCases/ModifyCorrectiveAction/ModifyCorrectiveAction.php @@ -11,6 +11,8 @@ use App\GaelO\Interfaces\Repositories\TrackerRepositoryInterface; use App\GaelO\Interfaces\Repositories\VisitRepositoryInterface; use App\GaelO\Services\AuthorizationService\AuthorizationVisitService; +use App\GaelO\Services\GaelOStudiesService\AbstractGaelOStudy; +use App\GaelO\Services\GaelOStudiesService\Events\CorrectiveActionEvent; use App\GaelO\Services\MailServices; use Exception; @@ -38,7 +40,6 @@ public function execute(ModifyCorrectiveActionRequest $modifyCorrectiveActionReq $studyName = $visitContext['patient']['study_name']; $patientId = $visitContext['patient']['id']; - $patientCode = $visitContext['patient']['code']; $visitType = $visitContext['visit_type']['name']; $visitGroupName = $visitContext['visit_type']['visit_group']['name']; $visitModality = $visitContext['visit_type']['visit_group']['modality']; @@ -92,17 +93,12 @@ public function execute(ModifyCorrectiveActionRequest $modifyCorrectiveActionReq $actionDetails ); - //Send Email - $this->mailServices->sendCorrectiveActionMessage( - $modifyCorrectiveActionRequest->visitId, - $modifyCorrectiveActionRequest->currentUserId, - $studyName, - $modifyCorrectiveActionRequest->correctiveActionDone, - $patientId, - $patientCode, - $visitModality, - $visitType - ); + $qcModifiedEvent = new CorrectiveActionEvent($visitContext); + $qcModifiedEvent->setCurrentUserId($modifyCorrectiveActionRequest->currentUserId); + $qcModifiedEvent->setCorrrectiveActionDone($modifyCorrectiveActionRequest->correctiveActionDone); + + $studyObject = AbstractGaelOStudy::getSpecificStudyObject($studyName); + $studyObject->onEventStudy($qcModifiedEvent); $modifyCorrectiveActionResponse->status = 200; $modifyCorrectiveActionResponse->statusText = 'OK'; diff --git a/GaelO2/app/GaelO/UseCases/ModifyQualityControl/ModifyQualityControl.php b/GaelO2/app/GaelO/UseCases/ModifyQualityControl/ModifyQualityControl.php index 1a837a60a..f2926c872 100644 --- a/GaelO2/app/GaelO/UseCases/ModifyQualityControl/ModifyQualityControl.php +++ b/GaelO2/app/GaelO/UseCases/ModifyQualityControl/ModifyQualityControl.php @@ -10,7 +10,6 @@ use App\GaelO\Exceptions\GaelOForbiddenException; use App\GaelO\Interfaces\Repositories\TrackerRepositoryInterface; use App\GaelO\Services\AuthorizationService\AuthorizationVisitService; -use App\GaelO\Services\MailServices; use App\GaelO\Services\VisitService; use Exception; @@ -20,14 +19,12 @@ class ModifyQualityControl private AuthorizationVisitService $authorizationVisitService; private VisitService $visitService; private TrackerRepositoryInterface $trackerRepositoryInterface; - private MailServices $mailServices; - public function __construct(AuthorizationVisitService $authorizationVisitService, VisitService $visitService, TrackerRepositoryInterface $trackerRepositoryInterface, MailServices $mailServices) + public function __construct(AuthorizationVisitService $authorizationVisitService, VisitService $visitService, TrackerRepositoryInterface $trackerRepositoryInterface) { $this->authorizationVisitService = $authorizationVisitService; $this->visitService = $visitService; $this->trackerRepositoryInterface = $trackerRepositoryInterface; - $this->mailServices = $mailServices; } public function execute(ModifyQualityControlRequest $modifyQualityControlRequest, ModifyQualityControlResponse $modifyQualityControlResponse) @@ -39,19 +36,17 @@ public function execute(ModifyQualityControlRequest $modifyQualityControlRequest $currentUserId = $modifyQualityControlRequest->currentUserId; $this->visitService->setVisitId($visitId); + $this->visitService->setCurrentUserId($currentUserId); $visitContext = $this->visitService->getVisitContext(); $studyName = $visitContext['patient']['study_name']; $patientId = $visitContext['patient']['id']; - $patientCode = $visitContext['patient']['code']; $visitType = $visitContext['visit_type']['name']; $visitGroupName = $visitContext['visit_type']['visit_group']['name']; $visitModality = $visitContext['visit_type']['visit_group']['modality']; - $centerCode = $visitContext['patient']['center_code']; - $creatorId = $visitContext['creator_user_id']; $localFormNeeded = $visitContext['state_investigator_form'] !== InvestigatorFormStateEnum::NOT_NEEDED->value; - if($modifyQualityControlRequest->studyName !== $studyName){ + if ($modifyQualityControlRequest->studyName !== $studyName) { throw new GaelOForbiddenException("Should be called from original study"); } @@ -105,23 +100,6 @@ public function execute(ModifyQualityControlRequest $modifyQualityControlRequest $actionDetails ); - $this->mailServices->sendQcDecisionMessage( - $visitId, - $creatorId, - $currentUserId, - $studyName, - $centerCode, - $modifyQualityControlRequest->stateQc, - $patientId, - $patientCode, - $visitModality, - $visitType, - $modifyQualityControlRequest->formQc ? 'Accepted ' : 'Refused', - $modifyQualityControlRequest->imageQc ? 'Accepted ' : 'Refused', - $modifyQualityControlRequest->formQcComment ?? 'None', - $modifyQualityControlRequest->imageQcComment ?? 'None' - ); - $modifyQualityControlResponse->status = 200; $modifyQualityControlResponse->statusText = 'OK'; } catch (AbstractGaelOException $e) { diff --git a/GaelO2/app/GaelO/UseCases/ModifyQualityControlReset/ModifyQualityControlReset.php b/GaelO2/app/GaelO/UseCases/ModifyQualityControlReset/ModifyQualityControlReset.php index df6397625..3ec19c604 100644 --- a/GaelO2/app/GaelO/UseCases/ModifyQualityControlReset/ModifyQualityControlReset.php +++ b/GaelO2/app/GaelO/UseCases/ModifyQualityControlReset/ModifyQualityControlReset.php @@ -36,6 +36,7 @@ public function execute(ModifyQualityControlResetRequest $modifyQualityControlRe $currentUserId = $modifyQualityControlResetRequest->currentUserId; $this->visitService->setVisitId($visitId); + $this->visitService->setCurrentUserId($currentUserId); $visitContext = $this->visitService->getVisitContext(); $studyName = $visitContext['patient']['study_name']; diff --git a/GaelO2/app/GaelO/UseCases/ReactivateDicomSeries/ReactivateDicomSeriesRequest.php b/GaelO2/app/GaelO/UseCases/ReactivateDicomSeries/ReactivateDicomSeriesRequest.php index 37cc1c66e..53fe559ca 100644 --- a/GaelO2/app/GaelO/UseCases/ReactivateDicomSeries/ReactivateDicomSeriesRequest.php +++ b/GaelO2/app/GaelO/UseCases/ReactivateDicomSeries/ReactivateDicomSeriesRequest.php @@ -8,4 +8,5 @@ class ReactivateDicomSeriesRequest public string $seriesInstanceUID; public string $reason; public string $role; + public string $studyName; } diff --git a/GaelO2/app/GaelO/UseCases/ReactivateDicomStudy/ReactivateDicomStudy.php b/GaelO2/app/GaelO/UseCases/ReactivateDicomStudy/ReactivateDicomStudy.php index 6f987440a..490b502ea 100644 --- a/GaelO2/app/GaelO/UseCases/ReactivateDicomStudy/ReactivateDicomStudy.php +++ b/GaelO2/app/GaelO/UseCases/ReactivateDicomStudy/ReactivateDicomStudy.php @@ -56,6 +56,7 @@ public function execute(ReactivateDicomStudyRequest $reactivateDicomStudyRequest $this->checkAuthorization($currentUserId, $visitId, $studyName, $visitContext); //Change dicom study Activation + $this->dicomService->setCurrentUserId($currentUserId); $this->dicomService->reactivateDicomStudy($studyData['study_uid']); //Tracker diff --git a/GaelO2/app/GaelO/UseCases/ReverseProxyDicomWeb/ReverseProxyDicomWeb.php b/GaelO2/app/GaelO/UseCases/ReverseProxyDicomWeb/ReverseProxyDicomWeb.php index bda479cdd..00cf746f9 100644 --- a/GaelO2/app/GaelO/UseCases/ReverseProxyDicomWeb/ReverseProxyDicomWeb.php +++ b/GaelO2/app/GaelO/UseCases/ReverseProxyDicomWeb/ReverseProxyDicomWeb.php @@ -60,7 +60,7 @@ public function execute(ReverseProxyDicomWebRequest $reverseProxyDicomWebRequest $headers['Forwarded'] = [$forwardedRule]; - $response = $this->httpClientInterface->rowRequest('GET', $calledUrl, null, $headers); + $response = $this->httpClientInterface->rawRequest('GET', $calledUrl, null, $headers); $responseHeaders = $response->getHeaders(); $responseHeaders['Cross-Origin-Embedder-Policy'] = ['require-corp']; diff --git a/GaelO2/app/GaelO/UseCases/ReverseProxyTus/ReverseProxyTus.php b/GaelO2/app/GaelO/UseCases/ReverseProxyTus/ReverseProxyTus.php index c54b60d95..a56db4b01 100644 --- a/GaelO2/app/GaelO/UseCases/ReverseProxyTus/ReverseProxyTus.php +++ b/GaelO2/app/GaelO/UseCases/ReverseProxyTus/ReverseProxyTus.php @@ -35,7 +35,7 @@ public function execute(ReverseProxyTusRequest $reverseProxyTusRequest, ReverseP //Make query of TUS $this->httpClientInterface->setUrl($this->frameworkInterface::getConfig(SettingsConstants::TUS_URL)); - $response = $this->httpClientInterface->rowRequest($reverseProxyTusRequest->method, $reverseProxyTusRequest->url, $reverseProxyTusRequest->body, $headers, null, false); + $response = $this->httpClientInterface->rawRequest($reverseProxyTusRequest->method, $reverseProxyTusRequest->url, $reverseProxyTusRequest->body, $headers, null, false); //Output response $reverseProxyTusResponse->status = $response->getStatusCode(); diff --git a/GaelO2/app/GaelO/UseCases/ValidateDicomUpload/ValidateDicomUpload.php b/GaelO2/app/GaelO/UseCases/ValidateDicomUpload/ValidateDicomUpload.php index ce945f88f..9cd32bccc 100644 --- a/GaelO2/app/GaelO/UseCases/ValidateDicomUpload/ValidateDicomUpload.php +++ b/GaelO2/app/GaelO/UseCases/ValidateDicomUpload/ValidateDicomUpload.php @@ -31,6 +31,7 @@ class ValidateDicomUpload private PatientRepositoryInterface $patientRepositoryInterface; private TrackerRepositoryInterface $trackerRepositoryInterface; private MailServices $mailServices; + private bool $markedProcessing = false; public function __construct( AuthorizationVisitService $authorizationService, @@ -75,15 +76,16 @@ public function execute(ValidateDicomUploadRequest $validateDicomUploadRequest, $originalOrthancId = $validateDicomUploadRequest->originalOrthancId; $this->checkAuthorization($currentUserId, $visitId, $studyName, $visitContext); - + $this->visitService->setCurrentUserId($currentUserId); + //Make Visit as being upload processing $this->visitService->updateUploadStatus(UploadStatusEnum::PROCESSING->value); + $this->markedProcessing = true; //Set Time Limit at 30min as operation could be really long set_time_limit(1800); //Create Temporary folder to work $unzipedPath = Util::getUploadTemporaryFolder(); - //Get uploaded Zips from TUS and upzip it in a temporary folder foreach ($validateDicomUploadRequest->uploadedFileTusId as $tusFileId) { $tusTempZip = $this->tusService->getFile($tusFileId); @@ -111,7 +113,7 @@ public function execute(ValidateDicomUploadRequest $validateDicomUploadRequest, if ($expectedNumberOfInstances !== $importedNumberOfInstances) { $this->orthancService->deleteFromOrthanc("studies", $importedOrthancStudyID); - throw new GaelOValidateDicomException("Imported DICOM (".$importedNumberOfInstances.") not matching announced number of Instances (".$expectedNumberOfInstances.")"); + throw new GaelOValidateDicomException("Imported DICOM (" . $importedNumberOfInstances . ") not matching announced number of Instances (" . $expectedNumberOfInstances . ")"); } //Anonymize and store new anonymized study Orthanc ID @@ -154,6 +156,7 @@ public function execute(ValidateDicomUploadRequest $validateDicomUploadRequest, //Change Visit status $this->visitService->updateUploadStatus(UploadStatusEnum::DONE->value); + $this->markedProcessing = false; //Write success in Tracker $actionDetails = [ @@ -223,7 +226,10 @@ private function checkAuthorization(int $currentUserId, int $visitId, string $st private function handleImportException(string $errorMessage, int $visitId, string $patientId, string $visitType, ?string $unzipedPath, string $studyName, int $userId) { - $this->visitService->updateUploadStatus(UploadStatusEnum::NOT_DONE->value); + //Restore upload not done status if it has been updated to processing status + if ($this->markedProcessing) { + $this->visitService->updateUploadStatus(UploadStatusEnum::NOT_DONE->value); + } $actionDetails = [ 'reason' => $errorMessage diff --git a/GaelO2/app/GaelO/Util.php b/GaelO2/app/GaelO/Util.php index e918c4eeb..f33d287ae 100644 --- a/GaelO2/app/GaelO/Util.php +++ b/GaelO2/app/GaelO/Util.php @@ -13,7 +13,7 @@ class Util { - public static function fillObject(array $dataToExtract, object $dataToFill) + public static function fillObject(array $dataToExtract, object $dataToFill) :void { //Get Expected properties awaited in DTO Request $reflect = new ReflectionClass($dataToFill); @@ -24,7 +24,6 @@ public static function fillObject(array $dataToExtract, object $dataToFill) $propertyName = $property->getName(); if (array_key_exists($propertyName, $dataToExtract)) $dataToFill->$propertyName = $dataToExtract[$propertyName]; } - return $dataToFill; } public static function endsWith(string $haystack, string $needle): bool diff --git a/GaelO2/app/GaelO/views/mails/mail_job_failure.blade.php b/GaelO2/app/GaelO/views/mails/mail_job_failure.blade.php new file mode 100644 index 000000000..2389c4577 --- /dev/null +++ b/GaelO2/app/GaelO/views/mails/mail_job_failure.blade.php @@ -0,0 +1,9 @@ +@extends('mails.mail_template') + +@section('content') + A Job Failure occured in the job {{$jobType}}
+ Error Message : {{$errorMessage}}
+ @foreach($details as $key => $value) + {{ $key }} : {{ $value }}
+ @endforeach +@endsection diff --git a/GaelO2/app/GaelO/views/mails/mail_radiomics_report.blade.php b/GaelO2/app/GaelO/views/mails/mail_radiomics_report.blade.php new file mode 100644 index 000000000..09326dcbf --- /dev/null +++ b/GaelO2/app/GaelO/views/mails/mail_radiomics_report.blade.php @@ -0,0 +1,264 @@ + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + +
+
+

Lymphoma Radiomics Report

+
+
+
+ +
+
+ +
+ + + + + + +
+ +
+ + + + + + + + + +
+
Visit {{$visitType}} of the patient {{$patientCode}} from the {{$studyName}}.
+
+
Visit Date: {{$visitDate}}
+
+
+ +
+
+ @if(!empty($message)) + +
+ + + + + + +
+ @foreach($image_path as $path) + +
+ + + + + + +
+ + + + + + +
+ tmtv preview +
+
+
+ @endforeach + +
+
+ @endif + +
+ + + + + + +
+ +
+ + + + + + +
+ @foreach($stats as $key => $value) + + + @endforeach
{{ $key }}{{ $value }}
+
+
+ +
+
+ +
+ + + + + + +
+ +
+ + + + + + +
+

+

+ +
+
+ +
+
+ +
+ + + diff --git a/GaelO2/app/GaelO/views/mails/mjml/radiomics_report.mjml b/GaelO2/app/GaelO/views/mails/mjml/radiomics_report.mjml new file mode 100644 index 000000000..f8f6a0230 --- /dev/null +++ b/GaelO2/app/GaelO/views/mails/mjml/radiomics_report.mjml @@ -0,0 +1,61 @@ + + + + + + + + + + + +

+ Lymphoma Radiomics Report +

+
+
+
+ + + Visit {{$visitType}} of the patient {{$patientCode}} from the {{$studyName}}. + + Visit Date: {{$visitDate}} + + + + + @if(!empty($message)) + + + + @foreach($image_path as $path) + + + + + + @endforeach + + + + @endif + + + + + @foreach($stats as $key => $value) + + {{ $key }} + {{ $value }} + + @endforeach + + + + + + + + +
+
\ No newline at end of file diff --git a/GaelO2/app/Http/Controllers/AskUnlockController.php b/GaelO2/app/Http/Controllers/AskUnlockController.php index 1a2dd57ee..f00068f8d 100644 --- a/GaelO2/app/Http/Controllers/AskUnlockController.php +++ b/GaelO2/app/Http/Controllers/AskUnlockController.php @@ -17,7 +17,7 @@ public function askUnlock(Request $request, RequestUnlock $requestUnlock, Reques $queryParam = $request->query(); $requestData = $request->all(); - $requestUnlockRequest = Util::fillObject($requestData, $requestUnlockRequest); + Util::fillObject($requestData, $requestUnlockRequest); $requestUnlockRequest->studyName = $queryParam['studyName']; $requestUnlockRequest->currentUserId = $currentUser['id']; diff --git a/GaelO2/app/Http/Controllers/AuthController.php b/GaelO2/app/Http/Controllers/AuthController.php index 2f4e06b85..91a771f00 100644 --- a/GaelO2/app/Http/Controllers/AuthController.php +++ b/GaelO2/app/Http/Controllers/AuthController.php @@ -23,14 +23,14 @@ public function login(Request $request, Login $login, LoginRequest $loginRequest { $requestData = $request->all(); - $loginRequest = Util::fillObject($requestData, $loginRequest); + Util::fillObject($requestData, $loginRequest); $loginRequest->ip = $request->ip(); $login->execute($loginRequest, $loginResponse); if ($loginResponse->status === 200) { - $user = User::where('email', $request->email)->sole(); + $user = User::where('email', strtolower($request->email))->sole(); $tokenResult = $user->createToken('GaelO'); return response()->json([ @@ -73,11 +73,10 @@ public function createMagicLink(Request $request, CreateMagicLink $createMagicLi $currentUser = $request->user(); $requestData = $request->all(); + Util::fillObject($requestData, $createMagicLinkRequest); $createMagicLinkRequest->targetUser = $userId; $createMagicLinkRequest->currentUserId = $currentUser['id']; - $createMagicLinkRequest = Util::fillObject($requestData, $createMagicLinkRequest); - $createMagicLink->execute($createMagicLinkRequest, $createMagicLinkResponse); return $this->getJsonResponse($createMagicLinkResponse->body, $createMagicLinkResponse->status, $createMagicLinkResponse->statusText); diff --git a/GaelO2/app/Http/Controllers/CenterController.php b/GaelO2/app/Http/Controllers/CenterController.php index 5762a5931..c74d8a984 100644 --- a/GaelO2/app/Http/Controllers/CenterController.php +++ b/GaelO2/app/Http/Controllers/CenterController.php @@ -27,10 +27,10 @@ class CenterController extends Controller public function getCenter(Request $request, GetCenterRequest $getCenterRequest, GetCenterResponse $getCenterResponse, GetCenter $getCenter, ?int $code = null) { $currentUser = Auth::user(); + $requestData = $request->all(); + Util::fillObject($requestData, $getCenterRequest); $getCenterRequest->currentUserId = $currentUser['id']; $getCenterRequest->code = $code; - $requestData = $request->all(); - $getCenterRequest = Util::fillObject($requestData, $getCenterRequest); $getCenter->execute($getCenterRequest, $getCenterResponse); return $this->getJsonResponse($getCenterResponse->body, $getCenterResponse->status, $getCenterResponse->statusText); } @@ -38,11 +38,12 @@ public function getCenter(Request $request, GetCenterRequest $getCenterRequest, public function modifyCenter(Request $request, ModifyCenterRequest $modifyCenterRequest, ModifyCenterResponse $modifyCenterResponse, ModifyCenter $modifyCenter, int $code) { $currentUser = Auth::user(); + $requestData = $request->all(); + + Util::fillObject($requestData, $modifyCenterRequest); $modifyCenterRequest->currentUserId = $currentUser['id']; $modifyCenterRequest->code = $code; - $requestData = $request->all(); - $modifyCenterRequest = Util::fillObject($requestData, $modifyCenterRequest); $modifyCenter->execute($modifyCenterRequest, $modifyCenterResponse); return $this->getJsonResponse($modifyCenterResponse->body, $modifyCenterResponse->status, $modifyCenterResponse->statusText); } @@ -54,7 +55,7 @@ public function createCenter(Request $request, CreateCenter $createCenter, Creat $createCenterRequest->currentUserId = $currentUser['id']; $requestData = $request->all(); - $createCenterRequest = Util::fillObject($requestData, $createCenterRequest); + Util::fillObject($requestData, $createCenterRequest); $createCenter->execute($createCenterRequest, $createCenterResponse); return $this->getJsonResponse($createCenterResponse->body, $createCenterResponse->status, $createCenterResponse->statusText); diff --git a/GaelO2/app/Http/Controllers/DicomController.php b/GaelO2/app/Http/Controllers/DicomController.php index 105893185..e5263151e 100644 --- a/GaelO2/app/Http/Controllers/DicomController.php +++ b/GaelO2/app/Http/Controllers/DicomController.php @@ -70,11 +70,11 @@ public function deleteSeries(Request $request, DeleteSeries $deleteSeries, Delet $queryParam = $request->query(); $requestData = $request->all(); + Util::fillObject($requestData, $deleteSeriesRequest); $deleteSeriesRequest->seriesInstanceUID = $seriesInstanceUID; $deleteSeriesRequest->role = $queryParam['role']; $deleteSeriesRequest->studyName = $queryParam['studyName']; $deleteSeriesRequest->currentUserId = $currentUser['id']; - $deleteSeriesRequest = Util::fillObject($requestData, $deleteSeriesRequest); $deleteSeries->execute($deleteSeriesRequest, $deleteSeriesResponse); @@ -87,11 +87,11 @@ public function reactivateSeries(Request $request, ReactivateDicomSeries $reacti $requestData = $request->all(); $queryParam = $request->query(); + Util::fillObject($requestData, $reactivateDicomSeriesRequest); $reactivateDicomSeriesRequest->studyName = $queryParam['studyName']; $reactivateDicomSeriesRequest->seriesInstanceUID = $seriesInstanceUID; $reactivateDicomSeriesRequest->currentUserId = $currentUser['id']; $reactivateDicomSeriesRequest->role = $queryParam['role']; - $reactivateDicomSeriesRequest = Util::fillObject($requestData, $reactivateDicomSeriesRequest); $reactivateDicomSeries->execute($reactivateDicomSeriesRequest, $reactivateDicomSeriesResponse); @@ -105,10 +105,10 @@ public function reactivateStudy(Request $request, ReactivateDicomStudy $reactiva $requestData = $request->all(); $queryParam = $request->query(); + Util::fillObject($requestData, $reactivateDicomStudyRequest); $reactivateDicomStudyRequest->studyName = $queryParam['studyName']; $reactivateDicomStudyRequest->studyInstanceUID = $studyInstanceUID; $reactivateDicomStudyRequest->currentUserId = $currentUser['id']; - $reactivateDicomStudyRequest = Util::fillObject($requestData, $reactivateDicomStudyRequest); $reactivateDicomStudy->execute($reactivateDicomStudyRequest, $reactivateDicomStudyResponse); @@ -120,9 +120,9 @@ public function getSupervisorDicomsFile(Request $request, GetDicomsFileSuperviso $currentUser = Auth::user(); $requestData = $request->all(); + Util::fillObject($requestData, $getDicomsFileSupervisorRequest); $getDicomsFileSupervisorRequest->currentUserId = $currentUser['id']; $getDicomsFileSupervisorRequest->studyName = $studyName; - $getDicomsFileSupervisorRequest = Util::fillObject($requestData, $getDicomsFileSupervisorRequest); $getDicomsFileSupervisor->execute($getDicomsFileSupervisorRequest, $getDicomsFileSupervisorResponse); diff --git a/GaelO2/app/Http/Controllers/DocumentationController.php b/GaelO2/app/Http/Controllers/DocumentationController.php index 2ef5bc831..1865ab4db 100644 --- a/GaelO2/app/Http/Controllers/DocumentationController.php +++ b/GaelO2/app/Http/Controllers/DocumentationController.php @@ -36,10 +36,12 @@ public function createDocumentation(Request $request, CreateDocumentation $creat { $currentUser = Auth::user(); $requestData = $request->all(); - $createDocumentationRequest = Util::fillObject($requestData, $createDocumentationRequest); + + Util::fillObject($requestData, $createDocumentationRequest); $createDocumentationRequest->currentUserId = $currentUser['id']; $createDocumentationRequest->studyName = $studyName; $createDocumentation->execute($createDocumentationRequest, $createDocumentationResponse); + return $this->getJsonResponse($createDocumentationResponse->body, $createDocumentationResponse->status, $createDocumentationResponse->statusText); } @@ -95,10 +97,12 @@ public function modifyDocumentation(Request $request, ModifyDocumentation $modif { $currentUser = Auth::user(); $requestData = $request->all(); - $modifyDocumentationRequest = Util::fillObject($requestData, $modifyDocumentationRequest); + + Util::fillObject($requestData, $modifyDocumentationRequest); $modifyDocumentationRequest->id = $documentationId; $modifyDocumentationRequest->currentUserId = $currentUser['id']; $modifyDocumentation->execute($modifyDocumentationRequest, $modifyDocumentationResponse); + return $this->getJsonResponse($modifyDocumentationResponse->body, $modifyDocumentationResponse->status, $modifyDocumentationResponse->statusText); } diff --git a/GaelO2/app/Http/Controllers/ExportDBController.php b/GaelO2/app/Http/Controllers/ExportDBController.php index c6eb34cbf..c58cae518 100644 --- a/GaelO2/app/Http/Controllers/ExportDBController.php +++ b/GaelO2/app/Http/Controllers/ExportDBController.php @@ -13,9 +13,11 @@ class ExportDBController extends Controller { public function exportDB(Request $request, ExportDatabase $exportDatabase, ExportDatabaseRequest $exportDatabaseRequest, ExportDatabaseResponse $exportDatabaseResponse) { $currentUser = Auth::user(); - $exportDatabaseRequest->currentUserId = $currentUser['id']; $requestData = $request->all(); - $exportDatabaseRequest = Util::fillObject($requestData, $exportDatabaseRequest); + + Util::fillObject($requestData, $exportDatabaseRequest); + $exportDatabaseRequest->currentUserId = $currentUser['id']; + $exportDatabase->execute($exportDatabaseRequest, $exportDatabaseResponse); if($exportDatabaseResponse->status === 200){ return response()->download($exportDatabaseResponse->zipFile, $exportDatabaseResponse->fileName, diff --git a/GaelO2/app/Http/Controllers/PatientController.php b/GaelO2/app/Http/Controllers/PatientController.php index daaf180d6..2d472db39 100644 --- a/GaelO2/app/Http/Controllers/PatientController.php +++ b/GaelO2/app/Http/Controllers/PatientController.php @@ -61,10 +61,11 @@ public function modifyPatient(Request $request, ModifyPatient $modifyPatient, Mo $requestData = $request->all(); $queryParam = $request->query(); - $modifyPatientRequest = Util::fillObject($requestData, $modifyPatientRequest); + Util::fillObject($requestData, $modifyPatientRequest); $modifyPatientRequest->studyName = $queryParam['studyName']; $modifyPatientRequest->patientId = $patientId; $modifyPatientRequest->currentUserId = $currentUser['id']; + $modifyPatient->execute($modifyPatientRequest, $modifyPatientResponse); return $this->getJsonResponse($modifyPatientResponse->body, $modifyPatientResponse->status, $modifyPatientResponse->statusText); @@ -89,7 +90,8 @@ public function addPatientTags(Request $request, CreatePatientTags $createPatien $currentUser = Auth::user(); $requestData = $request->all(); $queryParam = $request->query(); - $createPatientTagsRequest = Util::fillObject($requestData, $createPatientTagsRequest); + + Util::fillObject($requestData, $createPatientTagsRequest); $createPatientTagsRequest->studyName = $queryParam['studyName']; $createPatientTagsRequest->patientId = $patientId; $createPatientTagsRequest->currentUserId = $currentUser['id']; diff --git a/GaelO2/app/Http/Controllers/RequestController.php b/GaelO2/app/Http/Controllers/RequestController.php index 93917953f..f8cf6dea2 100644 --- a/GaelO2/app/Http/Controllers/RequestController.php +++ b/GaelO2/app/Http/Controllers/RequestController.php @@ -14,7 +14,7 @@ class RequestController extends Controller public function sendRequest(Request $request, RequestRequest $requestRequest, RequestResponse $requestResponse, SendRequest $sendRequest) { $requestData = $request->all(); - $requestRequest = Util::fillObject($requestData, $requestRequest); + Util::fillObject($requestData, $requestRequest); $sendRequest->execute($requestRequest, $requestResponse); return $this->getJsonResponse($requestResponse->body, $requestResponse->status, $requestResponse->statusText); } diff --git a/GaelO2/app/Http/Controllers/ReviewController.php b/GaelO2/app/Http/Controllers/ReviewController.php index 63fccc940..e3594384a 100644 --- a/GaelO2/app/Http/Controllers/ReviewController.php +++ b/GaelO2/app/Http/Controllers/ReviewController.php @@ -5,6 +5,9 @@ use App\GaelO\UseCases\CreateFileToForm\CreateFileToForm; use App\GaelO\UseCases\CreateFileToForm\CreateFileToFormRequest; use App\GaelO\UseCases\CreateFileToForm\CreateFileToFormResponse; +use App\GaelO\UseCases\CreateFileToFormFromTus\CreateFileToFormFromTus; +use App\GaelO\UseCases\CreateFileToFormFromTus\CreateFileToFormFromTusRequest; +use App\GaelO\UseCases\CreateFileToFormFromTus\CreateFileToFormFromTusResponse; use App\GaelO\UseCases\CreateInvestigatorForm\CreateInvestigatorForm; use App\GaelO\UseCases\CreateInvestigatorForm\CreateInvestigatorFormRequest; use App\GaelO\UseCases\CreateInvestigatorForm\CreateInvestigatorFormResponse; @@ -78,14 +81,13 @@ public function deleteInvestigatorForm(Request $request, DeleteInvestigatorForm { $currentUser = Auth::user(); $queryParam = $request->query(); + $requestData = $request->all(); + Util::fillObject($requestData, $deleteInvestigatorFormRequest); $deleteInvestigatorFormRequest->currentUserId = $currentUser['id']; $deleteInvestigatorFormRequest->studyName = $queryParam['studyName']; $deleteInvestigatorFormRequest->visitId = $visitId; - $requestData = $request->all(); - $deleteInvestigatorFormRequest = Util::fillObject($requestData, $deleteInvestigatorFormRequest); - $deleteInvestigatorForm->execute($deleteInvestigatorFormRequest, $deleteInvestigatorFormResponse); return $this->getJsonResponse($deleteInvestigatorFormResponse->body, $deleteInvestigatorFormResponse->status, $deleteInvestigatorFormResponse->statusText); @@ -95,14 +97,13 @@ public function unlockInvestigatorForm(Request $request, UnlockInvestigatorForm { $currentUser = Auth::user(); $queryParam = $request->query(); + $requestData = $request->all(); + Util::fillObject($requestData, $unlockInvestigatorFormRequest); $unlockInvestigatorFormRequest->currentUserId = $currentUser['id']; $unlockInvestigatorFormRequest->studyName = $queryParam['studyName']; $unlockInvestigatorFormRequest->visitId = $visitId; - $requestData = $request->all(); - $unlockInvestigatorFormRequest = Util::fillObject($requestData, $unlockInvestigatorFormRequest); - $unlockInvestigatorForm->execute($unlockInvestigatorFormRequest, $unlockInvestigatorFormResponse); return $this->getJsonResponse($unlockInvestigatorFormResponse->body, $unlockInvestigatorFormResponse->status, $unlockInvestigatorFormResponse->statusText); @@ -112,13 +113,12 @@ public function createInvestigatorForm(Request $request, CreateInvestigatorForm { $currentUser = Auth::user(); - + $requestData = $request->all(); + + Util::fillObject($requestData, $createInvestigatorFormRequest); $createInvestigatorFormRequest->currentUserId = $currentUser['id']; $createInvestigatorFormRequest->visitId = $visitId; - $requestData = $request->all(); - $createInvestigatorFormRequest = Util::fillObject($requestData, $createInvestigatorFormRequest); - $createInvestigatorForm->execute($createInvestigatorFormRequest, $createInvestigatorFormResponse); return $this->getJsonResponse($createInvestigatorFormResponse->body, $createInvestigatorFormResponse->status, $createInvestigatorFormResponse->statusText); @@ -128,14 +128,13 @@ public function modifyInvestigatorForm(Request $request, ModifyInvestigatorForm { $currentUser = Auth::user(); + $requestData = $request->all(); + Util::fillObject($requestData, $modifyInvestigatorFormRequest); $modifyInvestigatorFormRequest->currentUserId = $currentUser['id']; $modifyInvestigatorFormRequest->visitId = $visitId; - $requestData = $request->all(); - $deleteInvestigatorFormRequest = Util::fillObject($requestData, $modifyInvestigatorFormRequest); - - $modifyInvestigatorForm->execute($deleteInvestigatorFormRequest, $modifyInvestigatorFormResponse); + $modifyInvestigatorForm->execute($modifyInvestigatorFormRequest, $modifyInvestigatorFormResponse); return $this->getJsonResponse($modifyInvestigatorFormResponse->body, $modifyInvestigatorFormResponse->status, $modifyInvestigatorFormResponse->statusText); } @@ -147,11 +146,10 @@ public function createReviewForm(Request $request, CreateReviewForm $createRevie $queryParam = $request->query(); $createReviewFormRequest->studyName = $queryParam['studyName']; + Util::fillObject($requestData, $createReviewFormRequest); $createReviewFormRequest->visitId = $visitId; $createReviewFormRequest->currentUserId = $currentUser['id']; - $createReviewFormRequest = Util::fillObject($requestData, $createReviewFormRequest); - $createReviewForm->execute($createReviewFormRequest, $createReviewFormResponse); return $this->getJsonResponse($createReviewFormResponse->body, $createReviewFormResponse->status, $createReviewFormResponse->statusText); @@ -163,9 +161,9 @@ public function modifyReviewForm(Request $request, ModifyReviewForm $modifyRevie $currentUser = Auth::user(); $requestData = $request->all(); + Util::fillObject($requestData, $modifyReviewFormRequest); $modifyReviewFormRequest->reviewId = $reviewId; $modifyReviewFormRequest->currentUserId = $currentUser['id']; - $modifyReviewFormRequest = Util::fillObject($requestData, $modifyReviewFormRequest); $modifyReviewForm->execute($modifyReviewFormRequest, $modifyReviewFormResponse); @@ -205,11 +203,10 @@ public function deleteReviewForm(Request $request, DeleteReviewForm $deleteRevie $currentUser = Auth::user(); $requestData = $request->all(); + Util::fillObject($requestData, $deleteReviewFormRequest); $deleteReviewFormRequest->currentUserId = $currentUser['id']; $deleteReviewFormRequest->reviewId = $reviewId; - $deleteReviewFormRequest = Util::fillObject($requestData, $deleteReviewFormRequest); - $deleteReviewForm->execute($deleteReviewFormRequest, $deleteReviewFormResponse); return $this->getJsonResponse($deleteReviewFormResponse->body, $deleteReviewFormResponse->status, $deleteReviewFormResponse->statusText); @@ -220,12 +217,11 @@ public function unlockReviewForm(Request $request, UnlockReviewForm $unlockRevie $currentUser = Auth::user(); $requestData = $request->all(); + Util::fillObject($requestData, $unlockReviewFormRequest); $unlockReviewFormRequest->currentUserId = $currentUser['id']; $unlockReviewFormRequest->reviewId = $reviewId; - $deleteReviewFormRequest = Util::fillObject($requestData, $unlockReviewFormRequest); - - $unlockReviewForm->execute($deleteReviewFormRequest, $unlockReviewFormResponse); + $unlockReviewForm->execute($unlockReviewFormRequest, $unlockReviewFormResponse); return $this->getJsonResponse($unlockReviewFormResponse->body, $unlockReviewFormResponse->status, $unlockReviewFormResponse->statusText); } @@ -247,6 +243,19 @@ public function createReviewFile(Request $request, CreateFileToForm $createFileT return $this->getJsonResponse($createFileToFormResponse->body, $createFileToFormResponse->status, $createFileToFormResponse->statusText); } + public function createReviewFileFromTus(Request $request, CreateFileToFormFromTus $createFileToFormFromTus, CreateFileToFormFromTusRequest $createFileToFormFromTusRequest, CreateFileToFormFromTusResponse $createFileToFormFromTusResponse) + { + $currentUser = Auth::user(); + $requestData = $request->all(); + + Util::fillObject($requestData, $createFileToFormFromTusRequest); + $createFileToFormFromTusRequest->currentUserId = $currentUser['id']; + + $createFileToFormFromTus->execute($createFileToFormFromTusRequest, $createFileToFormFromTusResponse); + + return $this->getJsonResponse($createFileToFormFromTusResponse->body, $createFileToFormFromTusResponse->status, $createFileToFormFromTusResponse->statusText); + } + public function deleteReviewFile(DeleteFileOfForm $deleteFileOfForm, DeleteFileOfFormRequest $deleteFileOfFormRequest, DeleteFileOfFormResponse $deleteFileOfFormResponse, int $reviewId, string $key) { diff --git a/GaelO2/app/Http/Controllers/StudyController.php b/GaelO2/app/Http/Controllers/StudyController.php index 9a66a5770..5429f96e6 100644 --- a/GaelO2/app/Http/Controllers/StudyController.php +++ b/GaelO2/app/Http/Controllers/StudyController.php @@ -11,6 +11,9 @@ use App\GaelO\UseCases\ExportStudyData\ExportStudyData; use App\GaelO\UseCases\ExportStudyData\ExportStudyDataRequest; use App\GaelO\UseCases\ExportStudyData\ExportStudyDataResponse; +use App\GaelO\UseCases\GetCreatablePatients\GetCreatablePatients; +use App\GaelO\UseCases\GetCreatablePatients\GetCreatablePatientsRequest; +use App\GaelO\UseCases\GetCreatablePatients\GetCreatablePatientsResponse; use App\GaelO\UseCases\GetDicomsStudiesFromStudy\GetDicomsStudiesFromStudy; use App\GaelO\UseCases\GetDicomsStudiesFromStudy\GetDicomsStudiesFromStudyRequest; use App\GaelO\UseCases\GetDicomsStudiesFromStudy\GetDicomsStudiesFromStudyResponse; @@ -74,17 +77,16 @@ use App\GaelO\Util; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Log; - class StudyController extends Controller { public function createStudy(Request $request, CreateStudy $createStudy, CreateStudyRequest $createStudyRequest, CreateStudyResponse $createStudyResponse) { $currentUser = Auth::user(); - $createStudyRequest->currentUserId = $currentUser['id']; $requestData = $request->all(); - $createStudyRequest = Util::fillObject($requestData, $createStudyRequest); + + Util::fillObject($requestData, $createStudyRequest); + $createStudyRequest->currentUserId = $currentUser['id']; $createStudy->execute($createStudyRequest, $createStudyResponse); @@ -119,12 +121,11 @@ public function getStudyVisitTypes(GetStudyVisitTypes $getStudyVisitTypes, GetSt public function deleteStudy(Request $request, DeleteStudy $deleteStudy, DeleteStudyRequest $deleteStudyRequest, DeleteStudyResponse $deleteStudyResponse, String $studyName) { $currentUser = Auth::user(); - $requestData = $request->all(); + Util::fillObject($requestData, $deleteStudyRequest); $deleteStudyRequest->currentUserId = $currentUser['id']; $deleteStudyRequest->studyName = $studyName; - $deleteStudyRequest = Util::fillObject($requestData, $deleteStudyRequest); $deleteStudy->execute($deleteStudyRequest, $deleteStudyResponse); @@ -136,10 +137,10 @@ public function reactivateStudy(Request $request, ReactivateStudy $reactivateStu $currentUser = Auth::user(); $requestData = $request->all(); - + Util::fillObject($requestData, $reactivateStudyRequest); $reactivateStudyRequest->currentUserId = $currentUser['id']; $reactivateStudyRequest->studyName = $studyName; - $reactivateStudyRequest = Util::fillObject($requestData, $reactivateStudyRequest); + $reactivateStudy->execute($reactivateStudyRequest, $reactivateStudyResponse); return $this->getJsonResponse($reactivateStudyResponse->body, $reactivateStudyResponse->status, $reactivateStudyResponse->statusText); } @@ -148,8 +149,11 @@ public function importPatients(Request $request, ImportPatients $importPatients, { $currentUser = Auth::user(); - $importPatientsRequest->patients = $request->all(); + + $queryParam = $request->query(); + $importPatientsRequest->patients = $request->post(); $importPatientsRequest->studyName = $studyName; + $importPatientsRequest->role = $queryParam['role']; $importPatientsRequest->currentUserId = $currentUser['id']; $importPatients->execute($importPatientsRequest, $importPatientsResponse); return $this->getJsonResponse($importPatientsResponse->body, $importPatientsResponse->status, $importPatientsResponse->statusText); @@ -307,10 +311,12 @@ public function sendReminder(Request $request, SendReminder $sendReminder, SendR { $currentUser = Auth::user(); $requestData = $request->all(); + + Util::fillObject($requestData, $sendReminderRequest); $sendReminderRequest->currentUserId = $currentUser['id']; $sendReminderRequest->studyName = $studyName; - $reminderRequest = Util::fillObject($requestData, $sendReminderRequest); - $sendReminder->execute($reminderRequest, $sendReminderResponse); + + $sendReminder->execute($sendReminderRequest, $sendReminderResponse); return $this->getJsonResponse($sendReminderResponse->body, $sendReminderResponse->status, $sendReminderResponse->statusText); } @@ -320,11 +326,11 @@ public function requestPatientCreation(Request $request, RequestPatientCreation $requestData = $request->all(); $queryParam = $request->query(); + Util::fillObject($requestData, $requestPatientCreationRequest); $requestPatientCreationRequest->studyName = $studyName; $requestPatientCreationRequest->role = $queryParam['role']; $requestPatientCreationRequest->currentUserId = $currentUser['id']; - $requestPatientCreationRequest = Util::fillObject($requestData, $requestPatientCreationRequest); $requestPatientCreation->execute($requestPatientCreationRequest, $requestPatientCreationResponse); return $this->getJsonResponse($requestPatientCreationResponse->body, $requestPatientCreationResponse->status, $requestPatientCreationResponse->statusText); @@ -336,9 +342,10 @@ public function sendMail(Request $request, SendMail $sendMail, SendMailRequest $ $requestData = $request->all(); $queryParam = $request->query(); + Util::fillObject($requestData, $sendMailRequest); $sendMailRequest->currentUserId = $currentUser['id']; $sendMailRequest->studyName = key_exists('studyName', $queryParam) ? $queryParam['studyName'] : null; - $sendMailRequest = Util::fillObject($requestData, $sendMailRequest); + $sendMail->execute($sendMailRequest, $sendMailResponse); return $this->getJsonResponse($sendMailResponse->body, $sendMailResponse->status, $sendMailResponse->statusText); } @@ -363,4 +370,16 @@ public function getStudy(GetStudy $getStudy, GetStudyRequest $getStudyRequest, G $getStudy->execute($getStudyRequest, $getStudyResponse); return $this->getJsonResponse($getStudyResponse->body, $getStudyResponse->status, $getStudyResponse->statusText); } + + public function getCreatablePatients(Request $request, GetCreatablePatients $getCreatablePatients, GetCreatablePatientsRequest $getCreatablePatientsRequest, GetCreatablePatientsResponse $getCreatablePatientsResponse, string $studyName) + { + $currentUser = Auth::user(); + $queryParam = $request->query(); + + $getCreatablePatientsRequest->currentUserId = $currentUser['id']; + $getCreatablePatientsRequest->studyName = $studyName; + $getCreatablePatientsRequest->role = $queryParam['role']; + $getCreatablePatients->execute($getCreatablePatientsRequest, $getCreatablePatientsResponse); + return $this->getJsonResponse($getCreatablePatientsResponse->body, $getCreatablePatientsResponse->status, $getCreatablePatientsResponse->statusText); + } } diff --git a/GaelO2/app/Http/Controllers/ToolsController.php b/GaelO2/app/Http/Controllers/ToolsController.php index d261ebb55..bfb2d68d1 100644 --- a/GaelO2/app/Http/Controllers/ToolsController.php +++ b/GaelO2/app/Http/Controllers/ToolsController.php @@ -30,9 +30,10 @@ public function getPatientsInStudyFromCenters(Request $request, GetPatientsInStudyFromCentersResponse $getPatientsInStudyFromCentersResponse) { $currentUser = Auth::user(); - $getPatientsInStudyFromCentersRequest->currentUserId = $currentUser['id']; $requestData = $request->all(); - $getPatientsInStudyFromCentersRequest = Util::fillObject($requestData, $getPatientsInStudyFromCentersRequest); + + Util::fillObject($requestData, $getPatientsInStudyFromCentersRequest); + $getPatientsInStudyFromCentersRequest->currentUserId = $currentUser['id']; $getPatientsInStudyFromCenters->execute($getPatientsInStudyFromCentersRequest, $getPatientsInStudyFromCentersResponse); return $this->getJsonResponse($getPatientsInStudyFromCentersResponse->body, $getPatientsInStudyFromCentersResponse->status, $getPatientsInStudyFromCentersResponse->statusText); @@ -43,13 +44,14 @@ public function getPatientsVisitsInStudy(Request $request, GetPatientsVisitsInStudyRequest $getPatientsVisitsInStudyRequest, GetPatientsVisitsInStudyResponse $getPatientsVisitsInStudyResponse) { - $currentUser = Auth::user(); - - $getPatientsVisitsInStudyRequest->currentUserId = $currentUser['id']; + $currentUser = Auth::user(); $requestData = $request->all(); - $getPatientsVisitsInStudyRequest = Util::fillObject($requestData, $getPatientsVisitsInStudyRequest); $queryParam = $request->query(); + + Util::fillObject($requestData, $getPatientsVisitsInStudyRequest); + $getPatientsVisitsInStudyRequest->currentUserId = $currentUser['id']; $getPatientsVisitsInStudyRequest->studyName = $queryParam['studyName']; + $getPatientsVisitsInStudy->execute($getPatientsVisitsInStudyRequest, $getPatientsVisitsInStudyResponse); return $this->getJsonResponse($getPatientsVisitsInStudyResponse->body, $getPatientsVisitsInStudyResponse->status, $getPatientsVisitsInStudyResponse->statusText); } @@ -60,12 +62,13 @@ public function findUser(Request $request, FindUserResponse $findUserResponse) { $currentUser = Auth::user(); - - $findUserRequest->currentUserId = $currentUser['id']; $requestData = $request->all(); - $findUserRequest = Util::fillObject($requestData, $findUserRequest); $queryParam = $request->query(); + + Util::fillObject($requestData, $findUserRequest); + $findUserRequest->currentUserId = $currentUser['id']; $findUserRequest->studyName = $queryParam['studyName']; + $findUser->execute($findUserRequest, $findUserResponse); return $this->getJsonResponse($findUserResponse->body, $findUserResponse->status, $findUserResponse->statusText); @@ -82,7 +85,7 @@ public function createFormFileFromTus(Request $request, CreateFileToFormFromTus $currentUser = Auth::user(); $requestData = $request->all(); - $createFileToFormFromTusRequest = Util::fillObject($requestData, $createFileToFormFromTusRequest); + Util::fillObject($requestData, $createFileToFormFromTusRequest); $createFileToFormFromTusRequest->currentUserId = $currentUser['id']; $createFileToFormFromTus->execute($createFileToFormFromTusRequest, $createFileToFormFromTusResponse); diff --git a/GaelO2/app/Http/Controllers/UserController.php b/GaelO2/app/Http/Controllers/UserController.php index a50ebfa8b..163a9b66c 100644 --- a/GaelO2/app/Http/Controllers/UserController.php +++ b/GaelO2/app/Http/Controllers/UserController.php @@ -85,8 +85,8 @@ class UserController extends Controller public function forgotPassword(Request $request, ForgotPasswordRequest $forgotPasswordRequest, ForgotPasswordResponse $forgotPasswordResponse, ForgotPassword $forgotPassword) { $requestData = $request->all(); - $requestRequest = Util::fillObject($requestData, $forgotPasswordRequest); - $forgotPassword->execute($requestRequest, $forgotPasswordResponse); + Util::fillObject($requestData, $forgotPasswordRequest); + $forgotPassword->execute($forgotPasswordRequest, $forgotPasswordResponse); return $this->getJsonResponse($forgotPasswordResponse->body, $forgotPasswordResponse->status, $forgotPasswordResponse->statusText); } @@ -108,7 +108,12 @@ public function updatePassword(Request $request) ]); $status = FacadePassword::reset( - $request->only('email', 'password', 'password_confirmation', 'token'), + [ + 'email' => strtolower($request->input('email')), + 'password' => $request->input('password'), + 'password_confirmation' => $request->input('password_confirmation'), + 'token' => $request->input('token') + ], function ($user, $password) { $user->forceFill([ 'password' => Hash::make($password) @@ -147,13 +152,12 @@ public function createUser(Request $request, CreateUserRequest $createUserReques { //Get current user requesting the API $currentUser = Auth::user(); - //Add current user ID in Request DTO - $createUserRequest->currentUserId = $currentUser['id']; $requestData = $request->all(); $queryParam = $request->query(); - //Fill DTO with all other request data + //Fill DTO + Util::fillObject($requestData, $createUserRequest); $createUserRequest->studyName = $queryParam['studyName'] ?? null; - $createUserRequest = Util::fillObject($requestData, $createUserRequest); + $createUserRequest->currentUserId = $currentUser['id']; //Execute use case $createUser->execute($createUserRequest, $createUserResponse); //Output result comming from usecase, here no content has to be shown (only http status code and text) @@ -163,10 +167,10 @@ public function createUser(Request $request, CreateUserRequest $createUserReques public function modifyUser(Request $request, ModifyUserRequest $modifyUserRequest, ModifyUserResponse $modifyUserResponse, ModifyUser $modifyUser, int $id) { $currentUser = Auth::user(); - $modifyUserRequest->currentUserId = $currentUser['id']; $requestData = $request->all(); $requestData['userId'] = $id; - $modifyUserRequest = Util::fillObject($requestData, $modifyUserRequest); + Util::fillObject($requestData, $modifyUserRequest); + $modifyUserRequest->currentUserId = $currentUser['id']; $modifyUser->execute($modifyUserRequest, $modifyUserResponse); return $this->getJsonResponse($modifyUserResponse->body, $modifyUserResponse->status, $modifyUserResponse->statusText); } @@ -174,10 +178,12 @@ public function modifyUser(Request $request, ModifyUserRequest $modifyUserReques public function modifyUserIdentification(Request $request, ModifyUserIdentificationRequest $modifyUserRequest, ModifyUserIdentificationResponse $modifyUserResponse, ModifyUserIdentification $modifyUser, int $id) { $currentUser = Auth::user(); - $modifyUserRequest->currentUserId = $currentUser['id']; $requestData = $request->all(); $requestData['userId'] = $id; - $modifyUserRequest = Util::fillObject($requestData, $modifyUserRequest); + + Util::fillObject($requestData, $modifyUserRequest); + $modifyUserRequest->currentUserId = $currentUser['id']; + $modifyUser->execute($modifyUserRequest, $modifyUserResponse); return $this->getJsonResponse($modifyUserResponse->body, $modifyUserResponse->status, $modifyUserResponse->statusText); } @@ -187,9 +193,11 @@ public function deleteUser(Request $request, DeleteUserRequest $deleteUserReques $user = Auth::user(); $requestData = get_object_vars($request); + + Util::fillObject($requestData, $deleteUserRequest); $deleteUserRequest->id = $id; $deleteUserRequest->currentUserId = $user['id']; - $deleteUserRequest = Util::fillObject($requestData, $deleteUserRequest); + $deleteUser->execute($deleteUserRequest, $deleteUserResponse); return $this->getJsonResponse($deleteUserResponse->body, $deleteUserResponse->status, $deleteUserResponse->statusText); } @@ -221,10 +229,12 @@ public function createRole(Request $request, CreateUserRoles $createUserRole, Cr $currentUser = Auth::user(); $requestData = $request->all(); $queryParam = $request->query(); + + Util::fillObject($requestData, $createUserRoleRequest); $createUserRoleRequest->studyName = $queryParam['studyName']; $createUserRoleRequest->userId = $id; $createUserRoleRequest->currentUserId = $currentUser['id']; - $createUserRoleRequest = Util::fillObject($requestData, $createUserRoleRequest); + $createUserRole->execute($createUserRoleRequest, $createUserRoleResponse); return $this->getJsonResponse($createUserRoleResponse->body, $createUserRoleResponse->status, $createUserRoleResponse->statusText); } @@ -257,11 +267,13 @@ public function modifyValidatedDocumentationForRole(Request $request, ModifyVali { $currentUser = Auth::user(); $requestData = $request->all(); - $modifyValidatedDocumentationForRoleRequest = Util::fillObject($requestData, $modifyValidatedDocumentationForRoleRequest); + + Util::fillObject($requestData, $modifyValidatedDocumentationForRoleRequest); $modifyValidatedDocumentationForRoleRequest->currentUserId = $currentUser['id']; $modifyValidatedDocumentationForRoleRequest->userId = $userId; $modifyValidatedDocumentationForRoleRequest->studyName = $studyName; $modifyValidatedDocumentationForRoleRequest->role = $roleName; + $modifyValidatedDocumentationForRole->execute($modifyValidatedDocumentationForRoleRequest, $modifyValidatedDocumentationForRoleResponse); return $this->getJsonResponse($modifyValidatedDocumentationForRoleResponse->body, $modifyValidatedDocumentationForRoleResponse->status, $modifyValidatedDocumentationForRoleResponse->statusText); } @@ -269,8 +281,9 @@ public function modifyValidatedDocumentationForRole(Request $request, ModifyVali public function addAffiliatedCenter(Request $request, AddAffiliatedCenter $addAffiliatedCenter, AddAffiliatedCenterRequest $addAffiliatedCenterRequest, AddAffiliatedCenterResponse $addAffiliatedCenterResponse, int $userId) { $requestData = $request->all(); - $addAffiliatedCenterRequest = Util::fillObject($requestData, $addAffiliatedCenterRequest); $currentUser = Auth::user(); + + Util::fillObject($requestData, $addAffiliatedCenterRequest); $addAffiliatedCenterRequest->currentUserId = $currentUser['id']; $addAffiliatedCenterRequest->userId = $userId; @@ -333,9 +346,11 @@ public function modifyUserOnboarding(Request $request, ModifyUserOnboarding $mod { $currentUser = Auth::user(); $requestData = $request->all(); - $modifyUserOnboardingRequest = Util::fillObject($requestData, $modifyUserOnboardingRequest); + + Util::fillObject($requestData, $modifyUserOnboardingRequest); $modifyUserOnboardingRequest->currentUserId = $currentUser['id']; $modifyUserOnboardingRequest->userId = $id; + $modifyUserOnboarding->execute($modifyUserOnboardingRequest, $modifyUserOnboardingResponse); return $this->getJsonResponse($modifyUserOnboardingResponse->body, $modifyUserOnboardingResponse->status, $modifyUserOnboardingResponse->statusText); } @@ -346,7 +361,7 @@ public function getUserNotifications(Request $request, GetUserNotifications $get $queryParam = $request->query(); $getUserNotificationsRequest->currentUserId = $currentUser['id']; $getUserNotificationsRequest->userId = $userId; - if (key_exists('unread', $queryParam) ) { + if (key_exists('unread', $queryParam)) { $getUserNotificationsRequest->onlyUnread = true; } else { $getUserNotificationsRequest->onlyUnread = false; @@ -359,9 +374,11 @@ public function modifyUserNotifications(Request $request, ModifyUserNotification { $currentUser = Auth::user(); $requestData = $request->all(); - $modifyUserNotificationsRequest = Util::fillObject($requestData, $modifyUserNotificationsRequest); + + Util::fillObject($requestData, $modifyUserNotificationsRequest); $modifyUserNotificationsRequest->currentUserId = $currentUser['id']; $modifyUserNotificationsRequest->userId = $userId; + $modifyUserNotifications->execute($modifyUserNotificationsRequest, $modifyUserNotificationsResponse); return $this->getJsonResponse($modifyUserNotificationsResponse->body, $modifyUserNotificationsResponse->status, $modifyUserNotificationsResponse->statusText); } @@ -370,9 +387,11 @@ public function deleteUserNotifications(Request $request, DeleteUserNotification { $currentUser = Auth::user(); $requestData = $request->all(); - $deleteUserNotificationsRequest = Util::fillObject($requestData, $deleteUserNotificationsRequest); + + Util::fillObject($requestData, $deleteUserNotificationsRequest); $deleteUserNotificationsRequest->currentUserId = $currentUser['id']; $deleteUserNotificationsRequest->userId = $userId; + $deleteUserNotifications->execute($deleteUserNotificationsRequest, $deleteUserNotificationsResponse); return $this->getJsonResponse($deleteUserNotificationsResponse->body, $deleteUserNotificationsResponse->status, $deleteUserNotificationsResponse->statusText); } diff --git a/GaelO2/app/Http/Controllers/VisitController.php b/GaelO2/app/Http/Controllers/VisitController.php index 880310a1e..0378d882a 100644 --- a/GaelO2/app/Http/Controllers/VisitController.php +++ b/GaelO2/app/Http/Controllers/VisitController.php @@ -2,12 +2,21 @@ namespace App\Http\Controllers; +use App\GaelO\UseCases\CreateFileToVisit\CreateFileToVisit; +use App\GaelO\UseCases\CreateFileToVisit\CreateFileToVisitRequest; +use App\GaelO\UseCases\CreateFileToVisit\CreateFileToVisitResponse; use App\GaelO\UseCases\CreateVisit\CreateVisit; use App\GaelO\UseCases\CreateVisit\CreateVisitRequest; use App\GaelO\UseCases\CreateVisit\CreateVisitResponse; +use App\GaelO\UseCases\DeleteFileOfVisit\DeleteFileOfVisit; +use App\GaelO\UseCases\DeleteFileOfVisit\DeleteFileOfVisitRequest; +use App\GaelO\UseCases\DeleteFileOfVisit\DeleteFileOfVisitResponse; use App\GaelO\UseCases\DeleteVisit\DeleteVisit; use App\GaelO\UseCases\DeleteVisit\DeleteVisitRequest; use App\GaelO\UseCases\DeleteVisit\DeleteVisitResponse; +use App\GaelO\UseCases\GetFileOfVisit\GetFileOfVisit; +use App\GaelO\UseCases\GetFileOfVisit\GetFileOfVisitRequest; +use App\GaelO\UseCases\GetFileOfVisit\GetFileOfVisitResponse; use App\GaelO\UseCases\GetVisit\GetVisit; use App\GaelO\UseCases\GetVisit\GetVisitRequest; use App\GaelO\UseCases\GetVisit\GetVisitResponse; @@ -38,21 +47,23 @@ use App\GaelO\Util; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Storage; class VisitController extends Controller { public function createVisit(Request $request, CreateVisit $createVisit, CreateVisitRequest $createVisitRequest, CreateVisitResponse $createVisitResponse, String $visitTypeId) { $currentUser = Auth::user(); + $queryParam = $request->query(); + $requestData = $request->all(); + Util::fillObject($requestData, $createVisitRequest); $createVisitRequest->currentUserId = $currentUser['id']; $createVisitRequest->visitTypeId = $visitTypeId; - $queryParam = $request->query(); $createVisitRequest->role = $queryParam['role']; $createVisitRequest->studyName = $queryParam['studyName']; - $requestData = $request->all(); - $createVisitRequest = Util::fillObject($requestData, $createVisitRequest); $createVisit->execute($createVisitRequest, $createVisitResponse); return $this->getJsonResponse($createVisitResponse->body, $createVisitResponse->status, $createVisitResponse->statusText); } @@ -80,7 +91,7 @@ public function getVisitsFromStudy(Request $request, GetVisitsFromStudy $getVisi $queryParam = $request->query(); $getVisitsFromStudyRequest->currentUserId = $currentUser['id']; $getVisitsFromStudyRequest->studyName = $studyName; - if(isset($queryParam['visitType'])) $getVisitsFromStudyRequest->visitTypeId = $queryParam['visitType']; + if (isset($queryParam['visitType'])) $getVisitsFromStudyRequest->visitTypeId = $queryParam['visitType']; $getVisitsFromStudy->execute($getVisitsFromStudyRequest, $getVisitsFromStudyResponse); return $this->getJsonResponse($getVisitsFromStudyResponse->body, $getVisitsFromStudyResponse->status, $getVisitsFromStudyResponse->statusText); @@ -90,10 +101,12 @@ public function validateDicom(Request $request, ValidateDicomUpload $validateDic { $currentUser = Auth::user(); + $requestData = $request->all(); + + Util::fillObject($requestData, $validateDicomUploadRequest); $validateDicomUploadRequest->currentUserId = $currentUser['id']; $validateDicomUploadRequest->visitId = $visitId; - $requestData = $request->all(); - $validateDicomUploadRequest = Util::fillObject($requestData, $validateDicomUploadRequest); + $validateDicomUpload->execute($validateDicomUploadRequest, $validateDicomUploadResponse); return $this->getJsonResponse($validateDicomUploadResponse->body, $validateDicomUploadResponse->status, $validateDicomUploadResponse->statusText); @@ -104,7 +117,8 @@ public function deleteVisit(Request $request, DeleteVisit $deleteVisit, DeleteVi $currentUser = Auth::user(); $requestData = $request->all(); $queryParam = $request->query(); - $deleteVisitRequest = Util::fillObject($requestData, $deleteVisitRequest); + + Util::fillObject($requestData, $deleteVisitRequest); $deleteVisitRequest->currentUserId = $currentUser['id']; $deleteVisitRequest->visitId = $visitId; $deleteVisitRequest->role = $queryParam['role']; @@ -121,7 +135,7 @@ public function modifyQualityControl(Request $request, ModifyQualityControl $mod $requestData = $request->all(); $queryParam = $request->query(); - $modifyQualityControlRequest = Util::fillObject($requestData, $modifyQualityControlRequest); + Util::fillObject($requestData, $modifyQualityControlRequest); $modifyQualityControlRequest->currentUserId = $currentUser['id']; $modifyQualityControlRequest->visitId = $visitId; $modifyQualityControlRequest->studyName = $queryParam['studyName']; @@ -137,10 +151,10 @@ public function modifyQualityControlReset(Request $request, ModifyQualityControl $requestData = $request->all(); $queryParam = $request->query(); + Util::fillObject($requestData, $modifyQualityControlResetRequest); $modifyQualityControlResetRequest->currentUserId = $currentUser['id']; $modifyQualityControlResetRequest->visitId = $visitId; $modifyQualityControlResetRequest->studyName = $queryParam['studyName']; - $modifyQualityControlResetRequest = Util::fillObject($requestData, $modifyQualityControlResetRequest); $modifyQualityControlReset->execute($modifyQualityControlResetRequest, $modifyQualityControlResetResponse); @@ -153,7 +167,7 @@ public function modifyCorrectiveAction(Request $request, ModifyCorrectiveAction $requestData = $request->all(); $queryParam = $request->query(); - $modifyCorrectiveActionRequest = Util::fillObject($requestData, $modifyCorrectiveActionRequest); + Util::fillObject($requestData, $modifyCorrectiveActionRequest); $modifyCorrectiveActionRequest->currentUserId = $currentUser['id']; $modifyCorrectiveActionRequest->studyName = $queryParam['studyName']; $modifyCorrectiveActionRequest->visitId = $visitId; @@ -163,15 +177,16 @@ public function modifyCorrectiveAction(Request $request, ModifyCorrectiveAction return $this->getJsonResponse($modifyCorrectiveActionResponse->body, $modifyCorrectiveActionResponse->status, $modifyCorrectiveActionResponse->statusText); } - public function modifyVisitDate(Request $request, ModifyVisitDate $modifyVisitDate, ModifyVisitDateRequest $modifyVisitDateRequest, ModifyVisitDateResponse $modifyVisitDateResponse, int $visitId){ + public function modifyVisitDate(Request $request, ModifyVisitDate $modifyVisitDate, ModifyVisitDateRequest $modifyVisitDateRequest, ModifyVisitDateResponse $modifyVisitDateResponse, int $visitId) + { $currentUser = Auth::user(); $requestData = $request->all(); $queryParam = $request->query(); - $modifyCorrectiveActionRequest = Util::fillObject($requestData, $modifyVisitDateRequest); - $modifyCorrectiveActionRequest->currentUserId = $currentUser['id']; - $modifyCorrectiveActionRequest->visitId = $visitId; - $modifyCorrectiveActionRequest->studyName = $queryParam['studyName']; + Util::fillObject($requestData, $modifyVisitDateRequest); + $modifyVisitDateRequest->currentUserId = $currentUser['id']; + $modifyVisitDateRequest->visitId = $visitId; + $modifyVisitDateRequest->studyName = $queryParam['studyName']; $modifyVisitDate->execute($modifyVisitDateRequest, $modifyVisitDateResponse); @@ -190,18 +205,78 @@ public function reactivateVisit(ReactivateVisit $reactivateVisit, ReactivateVisi return $this->getJsonResponse($reactivateVisitResponse->body, $reactivateVisitResponse->status, $reactivateVisitResponse->statusText); } - public function unlockQc(Request $request, RequestUnlockQC $requestUnlockQC, RequestUnlockQCRequest $requestUnlockQCRequest, RequestUnlockQCResponse $requestUnlockQCResponse, int $visitId){ + public function unlockQc(Request $request, RequestUnlockQC $requestUnlockQC, RequestUnlockQCRequest $requestUnlockQCRequest, RequestUnlockQCResponse $requestUnlockQCResponse, int $visitId) + { $currentUser = Auth::user(); $requestData = $request->all(); - $requestUnlockRequest = Util::fillObject($requestData, $requestUnlockQCRequest); - $requestUnlockRequest->currentUserId = $currentUser['id']; - $requestUnlockRequest->visitId = $visitId; + Util::fillObject($requestData, $requestUnlockQCRequest); + $requestUnlockQCRequest->currentUserId = $currentUser['id']; + $requestUnlockQCRequest->visitId = $visitId; - $requestUnlockQC->execute($requestUnlockRequest, $requestUnlockQCResponse); + $requestUnlockQC->execute($requestUnlockQCRequest, $requestUnlockQCResponse); return $this->getJsonResponse($requestUnlockQCResponse->body, $requestUnlockQCResponse->status, $requestUnlockQCResponse->statusText); + } + + public function createFileOfVisit(Request $request, CreateFileToVisit $createFileToVisit, CreateFileToVisitRequest $createFileToVisitRequest, CreateFileToVisitResponse $createFileToVisitResponse, int $visitId, string $key) + { + $currentUser = Auth::user(); + $requestData = $request->all(); + $queryParam = $request->query(); + + Util::fillObject($requestData, $createFileToVisitRequest); + $createFileToVisitRequest->currentUserId = $currentUser['id']; + $createFileToVisitRequest->studyName = $queryParam['studyName']; + $createFileToVisitRequest->visitId = $visitId; + $createFileToVisitRequest->key = $key; + + $createFileToVisit->execute($createFileToVisitRequest, $createFileToVisitResponse); + + return response()->json($createFileToVisitResponse->body) + ->setStatusCode($createFileToVisitResponse->status, $createFileToVisitResponse->statusText); + } + + public function getFileOfVisit(Request $request, GetFileOfVisitRequest $getFileOfVisitRequest, GetFileOfVisitResponse $getFileOfVisitResponse, GetFileOfVisit $getFileOfVisit, int $visitId, string $key) + { + $currentUser = Auth::user(); + $requestData = $request->all(); + $queryParam = $request->query(); + + Util::fillObject($requestData, $getFileOfVisitRequest); + $getFileOfVisitRequest->visitId = $visitId; + $getFileOfVisitRequest->key = $key; + $getFileOfVisitRequest->currentUserId = $currentUser['id']; + $getFileOfVisitRequest->role = $queryParam['role']; + $getFileOfVisitRequest->studyName = $queryParam['studyName']; + + $getFileOfVisit->execute($getFileOfVisitRequest, $getFileOfVisitResponse); + + if ($getFileOfVisitResponse->status === 200) { + return Storage::download($getFileOfVisitResponse->filePath, $getFileOfVisitResponse->filename); + } else { + return response()->json($getFileOfVisitResponse->body) + ->setStatusCode($getFileOfVisitResponse->status, $getFileOfVisitResponse->statusText); + } + } + + public function deleteFileOfVisit(Request $request, DeleteFileOfVisitRequest $deleteFileOfVisitRequest, DeleteFileOfVisitResponse $deleteFileOfVisitResponse, DeleteFileOfVisit $deleteFileOfVisit, int $visitId, string $key) + { + $currentUser = Auth::user(); + $requestData = $request->all(); + $queryParam = $request->query(); + + Util::fillObject($requestData, $deleteFileOfVisitRequest); + $deleteFileOfVisitRequest->currentUserId = $currentUser['id']; + $deleteFileOfVisitRequest->key = $key; + $deleteFileOfVisitRequest->visitId = $visitId; + $deleteFileOfVisitRequest->role = $queryParam['role']; + $deleteFileOfVisitRequest->studyName = $queryParam['studyName']; + + $deleteFileOfVisit->execute($deleteFileOfVisitRequest, $deleteFileOfVisitResponse); + return $this->getJsonResponse($deleteFileOfVisitResponse->body, $deleteFileOfVisitResponse->status, $deleteFileOfVisitResponse->statusText); } + } diff --git a/GaelO2/app/Http/Controllers/VisitGroupController.php b/GaelO2/app/Http/Controllers/VisitGroupController.php index e57df7809..c6f82f8f3 100644 --- a/GaelO2/app/Http/Controllers/VisitGroupController.php +++ b/GaelO2/app/Http/Controllers/VisitGroupController.php @@ -19,10 +19,12 @@ class VisitGroupController extends Controller { public function createVisitGroup(Request $request, CreateVisitGroup $createVisitGroup, CreateVisitGroupRequest $createVisitGroupRequest, CreateVisitGroupResponse $createVisitGroupResponse, String $studyName) { $currentUser = Auth::user(); + $requestData = $request->all(); + + Util::fillObject($requestData, $createVisitGroupRequest); $createVisitGroupRequest->currentUserId = $currentUser['id']; $createVisitGroupRequest->studyName = $studyName; - $requestData = $request->all(); - $createVisitGroupRequest = Util::fillObject($requestData, $createVisitGroupRequest); + $createVisitGroup->execute($createVisitGroupRequest, $createVisitGroupResponse); return $this->getJsonResponse($createVisitGroupResponse->body, $createVisitGroupResponse->status, $createVisitGroupResponse->statusText); diff --git a/GaelO2/app/Http/Controllers/VisitTypeController.php b/GaelO2/app/Http/Controllers/VisitTypeController.php index 424404bb2..fa28fd20b 100644 --- a/GaelO2/app/Http/Controllers/VisitTypeController.php +++ b/GaelO2/app/Http/Controllers/VisitTypeController.php @@ -8,6 +8,9 @@ use App\GaelO\UseCases\DeleteVisitType\DeleteVisitType; use App\GaelO\UseCases\DeleteVisitType\DeleteVisitTypeRequest; use App\GaelO\UseCases\DeleteVisitType\DeleteVisitTypeResponse; +use App\GaelO\UseCases\GetFilesMetadataFromVisitType\GetFilesMetadataFromVisitTypeRequest; +use App\GaelO\UseCases\GetFilesMetadataFromVisitType\GetFilesMetadataFromVisitTypeResponse; +use App\GaelO\UseCases\GetFilesMetadataFromVisitType\GetFilesMetadataFromVisitType; use App\GaelO\UseCases\GetVisitType\GetVisitType; use App\GaelO\UseCases\GetVisitType\GetVisitTypeRequest; use App\GaelO\UseCases\GetVisitType\GetVisitTypeResponse; @@ -17,21 +20,27 @@ class VisitTypeController extends Controller { - public function createVisitType(Request $request, CreateVisitType $createVisitType, - CreateVisitTypeRequest $createVisitTypeRequest, CreateVisitTypeResponse $createVisitTypeResponse, int $visitGroupId){ + public function createVisitType( + Request $request, + CreateVisitType $createVisitType, + CreateVisitTypeRequest $createVisitTypeRequest, + CreateVisitTypeResponse $createVisitTypeResponse, + int $visitGroupId + ) { $currentUser = Auth::user(); + $requestData = $request->all(); + + Util::fillObject($requestData, $createVisitTypeRequest); $createVisitTypeRequest->currentUserId = $currentUser['id']; $createVisitTypeRequest->visitGroupId = $visitGroupId; - $requestData = $request->all(); - $createVisitTypeRequest = Util::fillObject($requestData, $createVisitTypeRequest); - $createVisitType->execute($createVisitTypeRequest, $createVisitTypeResponse); return $this->getJsonResponse($createVisitTypeResponse->body, $createVisitTypeResponse->status, $createVisitTypeResponse->statusText); } - public function getVisitType(GetVisitType $getVisitType, GetVisitTypeRequest $getVisitTypeRequest, GetVisitTypeResponse $getVisitTypeResponse, int $visitTypeId){ + public function getVisitType(GetVisitType $getVisitType, GetVisitTypeRequest $getVisitTypeRequest, GetVisitTypeResponse $getVisitTypeResponse, int $visitTypeId) + { $currentUser = Auth::user(); $getVisitTypeRequest->currentUserId = $currentUser['id']; $getVisitTypeRequest->visitTypeId = $visitTypeId; @@ -39,11 +48,25 @@ public function getVisitType(GetVisitType $getVisitType, GetVisitTypeRequest $ge return $this->getJsonResponse($getVisitTypeResponse->body, $getVisitTypeResponse->status, $getVisitTypeResponse->statusText); } - public function deleteVisitType(DeleteVisitType $deleteVisitType, DeleteVisitTypeRequest $deleteVisitTypeRequest, DeleteVisitTypeResponse $deleteVisitTypeResponse, int $visitTypeId){ + public function deleteVisitType(DeleteVisitType $deleteVisitType, DeleteVisitTypeRequest $deleteVisitTypeRequest, DeleteVisitTypeResponse $deleteVisitTypeResponse, int $visitTypeId) + { $currentUser = Auth::user(); $deleteVisitTypeRequest->currentUserId = $currentUser['id']; $deleteVisitTypeRequest->visitTypeId = $visitTypeId; $deleteVisitType->execute($deleteVisitTypeRequest, $deleteVisitTypeResponse); return $this->getJsonResponse($deleteVisitTypeResponse->body, $deleteVisitTypeResponse->status, $deleteVisitTypeResponse->statusText); } + + public function getFileMetadataFromVisitType(Request $request, GetFilesMetadataFromVisitType $getFilesMetadataFromVisitType, GetFilesMetadataFromVisitTypeRequest $getFilesMetadataFromVisitTypeRequest, GetFilesMetadataFromVisitTypeResponse $getFilesMetadataFromVisitTypeResponse, int $visitTypeId) + { + $currentUser = Auth::user(); + + $queryParam = $request->query(); + $getFilesMetadataFromVisitTypeRequest->studyName = $queryParam['studyName']; + $getFilesMetadataFromVisitTypeRequest->role = $queryParam['role']; + $getFilesMetadataFromVisitTypeRequest->currentUserId = $currentUser['id']; + $getFilesMetadataFromVisitTypeRequest->visitTypeId = $visitTypeId; + $getFilesMetadataFromVisitType->execute($getFilesMetadataFromVisitTypeRequest, $getFilesMetadataFromVisitTypeResponse); + return $this->getJsonResponse($getFilesMetadataFromVisitTypeResponse->body, $getFilesMetadataFromVisitTypeResponse->status, $getFilesMetadataFromVisitTypeResponse->statusText); + } } diff --git a/GaelO2/app/Jobs/JobGaelOProcessing.php b/GaelO2/app/Jobs/JobGaelOProcessing.php deleted file mode 100644 index 4493596a6..000000000 --- a/GaelO2/app/Jobs/JobGaelOProcessing.php +++ /dev/null @@ -1,46 +0,0 @@ -onQueue('processing'); - $this->orthancSeriesID = $orthancSeriesID; - $this->processingName = $processingName; - $this->host = $host; - } - - /** - * Execute the job. - * - * @return void - */ - public function handle(GaelOProcessingService $gaelOProcessingService) - { - $gaelOProcessingService->setHost($this->host); - $gaelOProcessingService->sendDicom($this->orthancSeriesID); - } -} diff --git a/GaelO2/app/Jobs/JobQcReport.php b/GaelO2/app/Jobs/JobQcReport.php index 4dca64f8b..46dbed014 100644 --- a/GaelO2/app/Jobs/JobQcReport.php +++ b/GaelO2/app/Jobs/JobQcReport.php @@ -15,14 +15,16 @@ use App\Jobs\QcReport\SeriesReport; use App\Jobs\QcReport\VisitReport; use Illuminate\Bus\Queueable; +use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Log; use Throwable; -class JobQcReport implements ShouldQueue +class JobQcReport implements ShouldQueue, ShouldBeUnique { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; private int $visitId; @@ -151,6 +153,7 @@ public function handle( } } + private function convertDate(string $visitDate): \DateTime { return new \DateTime($visitDate); @@ -158,6 +161,7 @@ private function convertDate(string $visitDate): \DateTime public function failed(Throwable $exception) { - Log::error($exception); + $mailServices = App::make(MailServices::class); + $mailServices->sendJobFailure('QcReport', ['visitId' => $this->visitId], $exception->getMessage()); } } diff --git a/GaelO2/app/Jobs/JobRadiomicsReport.php b/GaelO2/app/Jobs/JobRadiomicsReport.php new file mode 100644 index 000000000..5c2bc0d63 --- /dev/null +++ b/GaelO2/app/Jobs/JobRadiomicsReport.php @@ -0,0 +1,212 @@ +onQueue('processing'); + $this->visitId = $visitId; + $this->behalfUserId = $behalfUserId; + } + + public function handle( + VisitRepositoryInterface $visitRepositoryInterface, + DicomStudyRepositoryInterface $dicomStudyRepositoryInterface, + OrthancService $orthancService, + GaelOProcessingService $gaelOProcessingService, + MailServices $mailServices, + PdfServices $pdfServices, + GaelOClientService $gaeloClientService + ): void { + + $this->gaelOProcessingService = $gaelOProcessingService; + $this->orthancService = $orthancService; + $this->orthancService->setOrthancServer(true); + + $visitEntity = $visitRepositoryInterface->getVisitContext($this->visitId); + $studyName = $visitEntity['patient']['study_name']; + $visitType = $visitEntity['visit_type']['name']; + $patientCode = $visitEntity['patient']['code']; + $creatorUserId = $visitEntity['creator_user_id']; + $existingFiles = $visitEntity['sent_files']; + $visitDate = new DateTime($visitEntity['visit_date']); + $formattedVisitDate = $visitDate->format('m/d/Y'); + $dicomStudyEntity = $dicomStudyRepositoryInterface->getDicomsDataFromVisit($this->visitId, false, false); + + $orthancIds = $this->getSeriesOrthancIds($dicomStudyEntity); + $orthancSeriesIdPt = $orthancIds['orthancSeriesIdPt']; + $orthancSeriesIdCt = $orthancIds['orthancSeriesIdCt']; + + $this->sendDicomToProcessing($orthancSeriesIdPt); + $this->sendDicomToProcessing($orthancSeriesIdCt); + + $idPT = $this->gaelOProcessingService->createSeriesFromOrthanc($orthancSeriesIdPt, true, true); + $this->addCreatedRessource('series', $idPT); + $idCT = $this->gaelOProcessingService->createSeriesFromOrthanc($orthancSeriesIdCt); + $this->addCreatedRessource('series', $idCT); + + $inferencePayload = [ + 'idPT' => $idPT, + 'idCT' => $idCT + ]; + $inferenceResponse = $this->gaelOProcessingService->executeInference('unet_model', $inferencePayload); + $maskId = $inferenceResponse['id_mask']; + $this->addCreatedRessource('masks', $maskId); + + #Do Mask Fragmentation and threshold + $fragmentedMaskId = $this->gaelOProcessingService->fragmentMask($idPT, $maskId, true); + $this->addCreatedRessource('masks', $fragmentedMaskId); + $threshold41MaskId = $this->gaelOProcessingService->thresholdMask($fragmentedMaskId, $idPT, "41%"); + $this->addCreatedRessource('masks', $threshold41MaskId); + + #Fragmented Mip + $mipFragmentedPayload = ['maskId' => $threshold41MaskId, 'delay' => 0.3, 'min' => 0, 'max' => 5, 'inverted' => true, 'orientation' => 'LPI']; + $mipMask = $this->gaelOProcessingService->createMIPForSeries($idPT, $mipFragmentedPayload); + + #Download Rtss + #$rtssId = $this->gaelOProcessingService->createRtssFromMask($orthancSeriesIdPt, $threshold41MaskId); + #$this->addCreatedRessource('rtss', $rtssId); + #$rtssFile = $this->gaelOProcessingService->getRtss($rtssId); + + #Download Seg + #$segId = $this->gaelOProcessingService->createSegFromMask($orthancSeriesIdPt, $threshold41MaskId); + #$this->addCreatedRessource('seg', $segId); + #$segFile = $this->gaelOProcessingService->getSeg($segId); + + #Download .nii.gz Mask Dicom (not thrsholded) + $maskdicom = $this->gaelOProcessingService->getMaskDicomOrientation($fragmentedMaskId, 'LPI', true); + + #get Stats + $stats = $this->gaelOProcessingService->getStatsMask($threshold41MaskId); + $statValue = ['tmtv 41%' => $stats['volume'], 'DmaxVox' => $stats['dMax']]; + + $mailServices->sendRadiomicsReport( + $studyName, + $patientCode, + $visitType, + $formattedVisitDate, + $mipMask, + $statValue, + $creatorUserId + ); + + $pdfReport = $pdfServices->saveRadiomicsPdf( + $studyName, + $patientCode, + $visitType, + $formattedVisitDate, + $statValue + ); + + //Send file to store using API as job worker may not access to the storage backend + $user = User::find($this->behalfUserId); + $tokenResult = $user->createToken('GaelO')->plainTextToken; + $gaeloClientService->loadUrl(); + $gaeloClientService->setAuthorizationToken($tokenResult); + //In case of changed upload remove the last mask + if (array_key_exists('tmtv41', $existingFiles)) { + $gaeloClientService->deleteFileToVisit($studyName, $this->visitId, 'tmtv41'); + } + if (array_key_exists('tmtvReport', $existingFiles)) { + $gaeloClientService->deleteFileToVisit($studyName, $this->visitId, 'tmtvReport'); + } + if (array_key_exists('mipSegmentation', $existingFiles)) { + $gaeloClientService->deleteFileToVisit($studyName, $this->visitId, 'mipSegmentation'); + } + //Store the file for review availability + $gaeloClientService->createFileToVisit($studyName, $this->visitId, 'tmtv41', 'nii.gz', $maskdicom); + $gaeloClientService->createFileToVisit($studyName, $this->visitId, 'tmtvReport', 'pdf', $pdfReport); + $gaeloClientService->createFileToVisit($studyName, $this->visitId, 'mipSegmentation', 'gif', $mipMask); + + $this->deleteCreatedRessources(); + } + + private function sendDicomToProcessing(string $orthancSeriesIdPt) + { + $temporaryZipDicom = tempnam(ini_get('upload_tmp_dir'), 'TMP_Inference_'); + $temporaryZipDicomHandle = fopen($temporaryZipDicom, 'r+'); + + $this->orthancService->getZipStreamToFile([$orthancSeriesIdPt], $temporaryZipDicomHandle); + $this->gaelOProcessingService->createDicom($temporaryZipDicom); + $this->addCreatedRessource('dicoms', $orthancSeriesIdPt); + + unlink($temporaryZipDicom); + } + + private function getSeriesOrthancIds(array $dicomStudyEntity) + { + $idPT = null; + $idCT = null; + foreach ($dicomStudyEntity[0]['dicom_series'] as $series) { + if ($series['modality'] == 'PT') { + if ($idPT) throw new GaelOException('Multiple PET Series, unable to perform segmentation'); + $idPT = $series['orthanc_id']; + } + if ($series['modality'] == 'CT') { + if ($idCT) throw new GaelOException('Multiple CT Series, unable to perform segmentation'); + $idCT = $series['orthanc_id']; + } + } + + return [ + 'orthancSeriesIdPt' => $idPT, + 'orthancSeriesIdCt' => $idCT + ]; + } + + private function addCreatedRessource(string $type, string $id) + { + $this->createdFiles[] = new GaelOProcessingFile($type, $id); + } + + private function deleteCreatedRessources() + { + foreach ($this->createdFiles as $gaeloProcessingFile) { + try { + $this->gaelOProcessingService->deleteRessource($gaeloProcessingFile->getType(), $gaeloProcessingFile->getId()); + } catch (Exception) { + } + } + } + + public function failed(Throwable $exception) + { + $mailServices = App::make(MailServices::class); + $mailServices->sendJobFailure('RadiomicsReport', ['visitId' => $this->visitId, 'behalfUserId' => $this->behalfUserId], $exception->getMessage()); + } +} diff --git a/GaelO2/app/Jobs/LoadGaelOProcessing.php b/GaelO2/app/Jobs/LoadGaelOProcessing.php index f0f49d464..056b4ca81 100644 --- a/GaelO2/app/Jobs/LoadGaelOProcessing.php +++ b/GaelO2/app/Jobs/LoadGaelOProcessing.php @@ -47,7 +47,7 @@ public function handle(AzureService $azureService) //Get IP of the ACI $ip = $azureService->getIP(); - + /* Bus::batch( [ new JobGaelOProcessing( @@ -68,6 +68,7 @@ public function handle(AzureService $azureService) }) ->allowFailures() ->dispatch(); + */ } /** diff --git a/GaelO2/app/Jobs/QcReport/SeriesReport.php b/GaelO2/app/Jobs/QcReport/SeriesReport.php index 26c3af20c..05a835524 100644 --- a/GaelO2/app/Jobs/QcReport/SeriesReport.php +++ b/GaelO2/app/Jobs/QcReport/SeriesReport.php @@ -3,6 +3,7 @@ namespace App\Jobs\QcReport; use App\GaelO\DicomUtils; +use App\GaelO\Exceptions\GaelOException; use App\GaelO\Services\OrthancService; use App\GaelO\Services\StoreObjects\OrthancMetaData; use Throwable; @@ -108,21 +109,31 @@ public function getStudyDetails(): array private function getPreviewType(): ImageType { + if (!$this->instanceReport) { + throw new GaelOException("Instance Report need to be set first"); + } + $mosaicIDs = ['1.2.840.10008.5.1.4.1.1.4', '1.2.840.10008.5.1.4.1.1.4.1']; $gifIDs = [ '1.2.840.10008.5.1.4.1.1.2', '1.2.840.10008.5.1.4.1.1.2.1', '1.2.840.10008.5.1.4.1.1.20', '1.2.840.10008.5.1.4.1.1.128', '1.2.840.10008.5.1.4.1.1.130', '1.2.840.10008.5.1.4.1.1.128.1' ]; - if ($this->instanceReport != null && $this->instanceReport->numberOfFrames > 1) { + if ($this->instanceReport->numberOfFrames > 1) { + //Multi frame image series return ImageType::MULTIFRAME; - } else if (in_array($this->SOPClassUID, $mosaicIDs)) { - return ImageType::MOSAIC; - } else if (in_array($this->SOPClassUID, $gifIDs)) { - return ImageType::MIP; - } else { - return ImageType::DEFAULT; } + + if (sizeof($this->orthancInstanceIds) > 1) { + if (in_array($this->SOPClassUID, $mosaicIDs)) { + return ImageType::MOSAIC; + } else if (in_array($this->SOPClassUID, $gifIDs)) { + return ImageType::MIP; + } + } + + #All others case use default rendering of the first instance + return ImageType::DEFAULT; } public function loadSeriesPreview(OrthancService $orthancService): void @@ -134,7 +145,7 @@ public function loadSeriesPreview(OrthancService $orthancService): void try { switch ($imageType) { case ImageType::MIP: - //Mosaic for now + //Mosaic for now as mip need significant computation and memory backend $imagePath[] = $orthancService->getMosaic('series', $this->seriesOrthancId); break; case ImageType::MOSAIC: @@ -149,7 +160,8 @@ public function loadSeriesPreview(OrthancService $orthancService): void $imagePath[] = $orthancService->getInstancePreview($this->orthancInstanceIds[0]); break; } - } catch (Throwable $t) { } + } catch (Throwable $t) { + } $this->previewImagePath = $imagePath; } diff --git a/GaelO2/app/Jobs/RadiomicsReport/GaelOProcessingFile.php b/GaelO2/app/Jobs/RadiomicsReport/GaelOProcessingFile.php new file mode 100644 index 000000000..36c371c4f --- /dev/null +++ b/GaelO2/app/Jobs/RadiomicsReport/GaelOProcessingFile.php @@ -0,0 +1,24 @@ +type = $type; + $this->id = $id; + } + + public function getType() :string{ + return $this->type; + } + + public function getId() :string { + return $this->id; + } +} diff --git a/GaelO2/app/Mail/JobFailure.php b/GaelO2/app/Mail/JobFailure.php new file mode 100644 index 000000000..93708d045 --- /dev/null +++ b/GaelO2/app/Mail/JobFailure.php @@ -0,0 +1,48 @@ +parameters = $parameters; + /* + array('jobType' =>'', + 'details' => '', + 'errorMessage'=>''); + */ + } + + public function envelope(): Envelope + { + return new Envelope( + subject: "Failed Job - ".$this->parameters['jobType'] + ); + } + + public function content(): Content + { + return new Content( + view: 'mails.mail_job_failure', + with: $this->parameters + ); + } + +} diff --git a/GaelO2/app/Mail/RadiomicsReport.php b/GaelO2/app/Mail/RadiomicsReport.php new file mode 100644 index 000000000..083d89ff4 --- /dev/null +++ b/GaelO2/app/Mail/RadiomicsReport.php @@ -0,0 +1,41 @@ +parameters = $parameters; + } + + public function envelope(): Envelope + { + return new Envelope( + subject: $this->parameters['studyName'] . " - Radiomics Report Patient - " . $this->parameters['patientCode'] . " - Visit - " . $this->parameters['visitType'] + ); + } + + public function content(): Content + { + return new Content( + view: 'mails.mail_radiomics_report', + with: $this->parameters + ); + } +} diff --git a/GaelO2/app/Models/Visit.php b/GaelO2/app/Models/Visit.php index dc60397ab..3298e0813 100644 --- a/GaelO2/app/Models/Visit.php +++ b/GaelO2/app/Models/Visit.php @@ -39,7 +39,13 @@ class Visit extends Model 'corrective_action_new_upload' => 'boolean', 'corrective_action_investigator_form' => 'boolean', 'corrective_action_comment' => 'string', - 'corrective_action_applied' => 'boolean' + 'corrective_action_applied' => 'boolean', + 'sent_files' => 'array' + ]; + + //Default value because db does not accept default value json + protected $attributes = [ + 'sent_files' => '{}' ]; public function reviews() diff --git a/GaelO2/app/Providers/AdapterProvider.php b/GaelO2/app/Providers/AdapterProvider.php index 830bda56e..72c6c919f 100644 --- a/GaelO2/app/Providers/AdapterProvider.php +++ b/GaelO2/app/Providers/AdapterProvider.php @@ -7,13 +7,14 @@ use App\GaelO\Adapters\HttpClientAdapter; use App\GaelO\Adapters\JobAdapter; use App\GaelO\Adapters\MimeAdapter; +use App\GaelO\Adapters\PdfAdapter; use App\GaelO\Adapters\PhoneNumberAdapter; use App\GaelO\Interfaces\Adapters\DatabaseDumperInterface; use App\GaelO\Interfaces\Adapters\FrameworkInterface; -use App\GaelO\Interfaces\Adapters\HashInterface; use App\GaelO\Interfaces\Adapters\HttpClientInterface; use App\GaelO\Interfaces\Adapters\JobInterface; use App\GaelO\Interfaces\Adapters\MimeInterface; +use App\GaelO\Interfaces\Adapters\PdfInterface; use App\GaelO\Interfaces\Adapters\PhoneNumberInterface; use Illuminate\Support\ServiceProvider; @@ -32,6 +33,7 @@ public function register() $this->app->bind(MimeInterface::class, MimeAdapter::class); $this->app->bind(PhoneNumberInterface::class, PhoneNumberAdapter::class); $this->app->bind(JobInterface::class, JobAdapter::class); + $this->app->bind(PdfInterface::class, PdfAdapter::class); } /** diff --git a/GaelO2/composer.json b/GaelO2/composer.json index 772e4145d..8843ca984 100644 --- a/GaelO2/composer.json +++ b/GaelO2/composer.json @@ -10,6 +10,7 @@ "license": "AGPL-3.0-only", "require": { "php": "^8.1", + "barryvdh/laravel-dompdf": "^2.0", "doctrine/dbal": "^3.6", "giggsey/libphonenumber-for-php": "^8.13", "guzzlehttp/guzzle": "^7.5", @@ -18,8 +19,8 @@ "laravel/tinker": "^2.8", "league/flysystem-ftp": "^3.0", "league/flysystem-sftp-v3": "^3.0", + "league/mime-type-detection": "^1.13", "phpoffice/phpspreadsheet": "^1.27", - "ralouphie/mimey": "^1.0", "sentry/sentry-laravel": "^3.2", "spatie/db-dumper": "^3.3", "staudenmeir/eloquent-has-many-deep": "^1.18" diff --git a/GaelO2/composer.lock b/GaelO2/composer.lock index 10722cffe..b10c702ed 100644 --- a/GaelO2/composer.lock +++ b/GaelO2/composer.lock @@ -4,8 +4,85 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e8d12132f320ba395d950d91a79a3b8f", + "content-hash": "a457335d0a3f6e7f0872e83627087293", "packages": [ + { + "name": "barryvdh/laravel-dompdf", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/laravel-dompdf.git", + "reference": "9843d2be423670fb434f4c978b3c0f4dd92c87a6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/9843d2be423670fb434f4c978b3c0f4dd92c87a6", + "reference": "9843d2be423670fb434f4c978b3c0f4dd92c87a6", + "shasum": "" + }, + "require": { + "dompdf/dompdf": "^2.0.1", + "illuminate/support": "^6|^7|^8|^9|^10", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "nunomaduro/larastan": "^1|^2", + "orchestra/testbench": "^4|^5|^6|^7|^8", + "phpro/grumphp": "^1", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + }, + "laravel": { + "providers": [ + "Barryvdh\\DomPDF\\ServiceProvider" + ], + "aliases": { + "Pdf": "Barryvdh\\DomPDF\\Facade\\Pdf", + "PDF": "Barryvdh\\DomPDF\\Facade\\Pdf" + } + } + }, + "autoload": { + "psr-4": { + "Barryvdh\\DomPDF\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "A DOMPDF Wrapper for Laravel", + "keywords": [ + "dompdf", + "laravel", + "pdf" + ], + "support": { + "issues": "https://github.com/barryvdh/laravel-dompdf/issues", + "source": "https://github.com/barryvdh/laravel-dompdf/tree/v2.0.1" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2023-01-12T15:12:49+00:00" + }, { "name": "brick/math", "version": "0.11.0", @@ -297,16 +374,16 @@ }, { "name": "doctrine/dbal", - "version": "3.6.4", + "version": "3.7.1", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "19f0dec95edd6a3c3c5ff1d188ea94c6b7fc903f" + "reference": "5b7bd66c9ff58c04c5474ab85edce442f8081cb2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/19f0dec95edd6a3c3c5ff1d188ea94c6b7fc903f", - "reference": "19f0dec95edd6a3c3c5ff1d188ea94c6b7fc903f", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/5b7bd66c9ff58c04c5474ab85edce442f8081cb2", + "reference": "5b7bd66c9ff58c04c5474ab85edce442f8081cb2", "shasum": "" }, "require": { @@ -321,11 +398,12 @@ "require-dev": { "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", - "jetbrains/phpstorm-stubs": "2022.3", - "phpstan/phpstan": "1.10.14", + "jetbrains/phpstorm-stubs": "2023.1", + "phpstan/phpstan": "1.10.35", "phpstan/phpstan-strict-rules": "^1.5", - "phpunit/phpunit": "9.6.7", + "phpunit/phpunit": "9.6.13", "psalm/plugin-phpunit": "0.18.4", + "slevomat/coding-standard": "8.13.1", "squizlabs/php_codesniffer": "3.7.2", "symfony/cache": "^5.4|^6.0", "symfony/console": "^4.4|^5.4|^6.0", @@ -389,7 +467,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.6.4" + "source": "https://github.com/doctrine/dbal/tree/3.7.1" }, "funding": [ { @@ -405,20 +483,20 @@ "type": "tidelift" } ], - "time": "2023-06-15T07:40:12+00:00" + "time": "2023-10-06T05:06:20+00:00" }, { "name": "doctrine/deprecations", - "version": "v1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3" + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", - "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", "shasum": "" }, "require": { @@ -450,9 +528,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.1.1" + "source": "https://github.com/doctrine/deprecations/tree/1.1.2" }, - "time": "2023-06-03T09:27:29+00:00" + "time": "2023-09-27T20:04:15+00:00" }, { "name": "doctrine/event-manager", @@ -713,18 +791,80 @@ ], "time": "2022-12-15T16:57:16+00:00" }, + { + "name": "dompdf/dompdf", + "version": "v2.0.3", + "source": { + "type": "git", + "url": "https://github.com/dompdf/dompdf.git", + "reference": "e8d2d5e37e8b0b30f0732a011295ab80680d7e85" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/dompdf/zipball/e8d2d5e37e8b0b30f0732a011295ab80680d7e85", + "reference": "e8d2d5e37e8b0b30f0732a011295ab80680d7e85", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "masterminds/html5": "^2.0", + "phenx/php-font-lib": ">=0.5.4 <1.0.0", + "phenx/php-svg-lib": ">=0.3.3 <1.0.0", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "ext-json": "*", + "ext-zip": "*", + "mockery/mockery": "^1.3", + "phpunit/phpunit": "^7.5 || ^8 || ^9", + "squizlabs/php_codesniffer": "^3.5" + }, + "suggest": { + "ext-gd": "Needed to process images", + "ext-gmagick": "Improves image processing performance", + "ext-imagick": "Improves image processing performance", + "ext-zlib": "Needed for pdf stream compression" + }, + "type": "library", + "autoload": { + "psr-4": { + "Dompdf\\": "src/" + }, + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "authors": [ + { + "name": "The Dompdf Community", + "homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md" + } + ], + "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter", + "homepage": "https://github.com/dompdf/dompdf", + "support": { + "issues": "https://github.com/dompdf/dompdf/issues", + "source": "https://github.com/dompdf/dompdf/tree/v2.0.3" + }, + "time": "2023-02-07T12:51:48+00:00" + }, { "name": "dragonmantank/cron-expression", - "version": "v3.3.2", + "version": "v3.3.3", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8" + "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", + "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", "shasum": "" }, "require": { @@ -764,7 +904,7 @@ ], "support": { "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2" + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.3" }, "funding": [ { @@ -772,20 +912,20 @@ "type": "github" } ], - "time": "2022-09-10T18:51:20+00:00" + "time": "2023-08-10T19:36:49+00:00" }, { "name": "egulias/email-validator", - "version": "4.0.1", + "version": "4.0.2", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "3a85486b709bc384dae8eb78fb2eec649bdb64ff" + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/3a85486b709bc384dae8eb78fb2eec649bdb64ff", - "reference": "3a85486b709bc384dae8eb78fb2eec649bdb64ff", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ebaaf5be6c0286928352e054f2d5125608e5405e", + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e", "shasum": "" }, "require": { @@ -794,8 +934,8 @@ "symfony/polyfill-intl-idn": "^1.26" }, "require-dev": { - "phpunit/phpunit": "^9.5.27", - "vimeo/psalm": "^4.30" + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" }, "suggest": { "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" @@ -831,7 +971,7 @@ ], "support": { "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/4.0.1" + "source": "https://github.com/egulias/EmailValidator/tree/4.0.2" }, "funding": [ { @@ -839,7 +979,7 @@ "type": "github" } ], - "time": "2023-01-14T14:17:03+00:00" + "time": "2023-10-06T06:47:41+00:00" }, { "name": "ezyang/htmlpurifier", @@ -904,21 +1044,21 @@ }, { "name": "fruitcake/php-cors", - "version": "v1.2.0", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/fruitcake/php-cors.git", - "reference": "58571acbaa5f9f462c9c77e911700ac66f446d4e" + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/58571acbaa5f9f462c9c77e911700ac66f446d4e", - "reference": "58571acbaa5f9f462c9c77e911700ac66f446d4e", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/3d158f36e7875e2f040f37bc0573956240a5a38b", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b", "shasum": "" }, "require": { "php": "^7.4|^8.0", - "symfony/http-foundation": "^4.4|^5.4|^6" + "symfony/http-foundation": "^4.4|^5.4|^6|^7" }, "require-dev": { "phpstan/phpstan": "^1.4", @@ -928,7 +1068,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.1-dev" + "dev-master": "1.2-dev" } }, "autoload": { @@ -959,7 +1099,7 @@ ], "support": { "issues": "https://github.com/fruitcake/php-cors/issues", - "source": "https://github.com/fruitcake/php-cors/tree/v1.2.0" + "source": "https://github.com/fruitcake/php-cors/tree/v1.3.0" }, "funding": [ { @@ -971,20 +1111,20 @@ "type": "github" } ], - "time": "2022-02-20T15:07:15+00:00" + "time": "2023-10-12T05:21:21+00:00" }, { "name": "giggsey/libphonenumber-for-php", - "version": "8.13.15", + "version": "8.13.24", "source": { "type": "git", "url": "https://github.com/giggsey/libphonenumber-for-php.git", - "reference": "b294846e26ea985e6b1fbdfaf15387daca60c2de" + "reference": "746ca6a565b9d4167c94c80824f43fa6fb463fd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php/zipball/b294846e26ea985e6b1fbdfaf15387daca60c2de", - "reference": "b294846e26ea985e6b1fbdfaf15387daca60c2de", + "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php/zipball/746ca6a565b9d4167c94c80824f43fa6fb463fd1", + "reference": "746ca6a565b9d4167c94c80824f43fa6fb463fd1", "shasum": "" }, "require": { @@ -1043,7 +1183,7 @@ "issues": "https://github.com/giggsey/libphonenumber-for-php/issues", "source": "https://github.com/giggsey/libphonenumber-for-php" }, - "time": "2023-06-23T07:47:45+00:00" + "time": "2023-10-31T08:12:54+00:00" }, { "name": "giggsey/locale", @@ -1163,22 +1303,22 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.7.0", + "version": "7.8.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "fb7566caccf22d74d1ab270de3551f72a58399f5" + "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/fb7566caccf22d74d1ab270de3551f72a58399f5", - "reference": "fb7566caccf22d74d1ab270de3551f72a58399f5", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1110f66a6530a40fe7aea0378fe608ee2b2248f9", + "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0", - "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", + "guzzlehttp/promises": "^1.5.3 || ^2.0.1", + "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -1269,7 +1409,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.7.0" + "source": "https://github.com/guzzle/guzzle/tree/7.8.0" }, "funding": [ { @@ -1285,20 +1425,20 @@ "type": "tidelift" } ], - "time": "2023-05-21T14:04:53+00:00" + "time": "2023-08-27T10:20:53+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6" + "reference": "111166291a0f8130081195ac4556a5587d7f1b5d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/3a494dc7dc1d7d12e511890177ae2d0e6c107da6", - "reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6", + "url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d", + "reference": "111166291a0f8130081195ac4556a5587d7f1b5d", "shasum": "" }, "require": { @@ -1352,7 +1492,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.0" + "source": "https://github.com/guzzle/promises/tree/2.0.1" }, "funding": [ { @@ -1368,20 +1508,20 @@ "type": "tidelift" } ], - "time": "2023-05-21T13:50:22+00:00" + "time": "2023-08-03T15:11:55+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.5.0", + "version": "2.6.1", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "b635f279edd83fc275f822a1188157ffea568ff6" + "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/b635f279edd83fc275f822a1188157ffea568ff6", - "reference": "b635f279edd83fc275f822a1188157ffea568ff6", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/be45764272e8873c72dbe3d2edcfdfcc3bc9f727", + "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727", "shasum": "" }, "require": { @@ -1468,7 +1608,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.5.0" + "source": "https://github.com/guzzle/psr7/tree/2.6.1" }, "funding": [ { @@ -1484,20 +1624,20 @@ "type": "tidelift" } ], - "time": "2023-04-17T16:11:26+00:00" + "time": "2023-08-27T10:13:57+00:00" }, { "name": "guzzlehttp/uri-template", - "version": "v1.0.1", + "version": "v1.0.2", "source": { "type": "git", "url": "https://github.com/guzzle/uri-template.git", - "reference": "b945d74a55a25a949158444f09ec0d3c120d69e2" + "reference": "61bf437fc2197f587f6857d3ff903a24f1731b5d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/b945d74a55a25a949158444f09ec0d3c120d69e2", - "reference": "b945d74a55a25a949158444f09ec0d3c120d69e2", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/61bf437fc2197f587f6857d3ff903a24f1731b5d", + "reference": "61bf437fc2197f587f6857d3ff903a24f1731b5d", "shasum": "" }, "require": { @@ -1505,15 +1645,11 @@ "symfony/polyfill-php80": "^1.17" }, "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", "phpunit/phpunit": "^8.5.19 || ^9.5.8", "uri-template/tests": "1.0.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, "autoload": { "psr-4": { "GuzzleHttp\\UriTemplate\\": "src" @@ -1552,7 +1688,7 @@ ], "support": { "issues": "https://github.com/guzzle/uri-template/issues", - "source": "https://github.com/guzzle/uri-template/tree/v1.0.1" + "source": "https://github.com/guzzle/uri-template/tree/v1.0.2" }, "funding": [ { @@ -1568,7 +1704,7 @@ "type": "tidelift" } ], - "time": "2021-10-07T12:57:01+00:00" + "time": "2023-08-27T10:19:19+00:00" }, { "name": "http-interop/http-factory-guzzle", @@ -1689,16 +1825,16 @@ }, { "name": "laravel/framework", - "version": "v10.13.5", + "version": "v10.30.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "03106ae9ba2ec4b36dc973b7bdca6fad81e032b4" + "reference": "3dd85d9dbea82b937f8eaf344b50d613c5d1127a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/03106ae9ba2ec4b36dc973b7bdca6fad81e032b4", - "reference": "03106ae9ba2ec4b36dc973b7bdca6fad81e032b4", + "url": "https://api.github.com/repos/laravel/framework/zipball/3dd85d9dbea82b937f8eaf344b50d613c5d1127a", + "reference": "3dd85d9dbea82b937f8eaf344b50d613c5d1127a", "shasum": "" }, "require": { @@ -1716,11 +1852,12 @@ "ext-tokenizer": "*", "fruitcake/php-cors": "^1.2", "guzzlehttp/uri-template": "^1.0", + "laravel/prompts": "^0.1.9", "laravel/serializable-closure": "^1.3", "league/commonmark": "^2.2.1", "league/flysystem": "^3.8.0", "monolog/monolog": "^3.0", - "nesbot/carbon": "^2.62.1", + "nesbot/carbon": "^2.67", "nunomaduro/termwind": "^1.13", "php": "^8.1", "psr/container": "^1.1.1|^2.0.1", @@ -1730,7 +1867,7 @@ "symfony/console": "^6.2", "symfony/error-handler": "^6.2", "symfony/finder": "^6.2", - "symfony/http-foundation": "^6.2", + "symfony/http-foundation": "^6.3", "symfony/http-kernel": "^6.2", "symfony/mailer": "^6.2", "symfony/mime": "^6.2", @@ -1797,14 +1934,15 @@ "league/flysystem-read-only": "^3.3", "league/flysystem-sftp-v3": "^3.0", "mockery/mockery": "^1.5.1", - "orchestra/testbench-core": "^8.4", + "nyholm/psr7": "^1.2", + "orchestra/testbench-core": "^8.12", "pda/pheanstalk": "^4.0", - "phpstan/phpdoc-parser": "^1.15", "phpstan/phpstan": "^1.4.7", "phpunit/phpunit": "^10.0.7", "predis/predis": "^2.0.2", "symfony/cache": "^6.2", - "symfony/http-client": "^6.2.4" + "symfony/http-client": "^6.2.4", + "symfony/psr-http-message-bridge": "^2.0" }, "suggest": { "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", @@ -1885,20 +2023,77 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2023-06-08T20:25:36+00:00" + "time": "2023-10-31T13:19:45+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.1.13", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "e1379d8ead15edd6cc4369c22274345982edc95a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/e1379d8ead15edd6cc4369c22274345982edc95a", + "reference": "e1379d8ead15edd6cc4369c22274345982edc95a", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "illuminate/collections": "^10.0|^11.0", + "php": "^8.1", + "symfony/console": "^6.2|^7.0" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-mockery": "^1.1" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "support": { + "issues": "https://github.com/laravel/prompts/issues", + "source": "https://github.com/laravel/prompts/tree/v0.1.13" + }, + "time": "2023-10-27T13:53:59+00:00" }, { "name": "laravel/sanctum", - "version": "v3.2.5", + "version": "v3.3.1", "source": { "type": "git", "url": "https://github.com/laravel/sanctum.git", - "reference": "8ebda85d59d3c414863a7f4d816ef8302faad876" + "reference": "338f633e6487e76b255470d3373fbc29228aa971" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sanctum/zipball/8ebda85d59d3c414863a7f4d816ef8302faad876", - "reference": "8ebda85d59d3c414863a7f4d816ef8302faad876", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/338f633e6487e76b255470d3373fbc29228aa971", + "reference": "338f633e6487e76b255470d3373fbc29228aa971", "shasum": "" }, "require": { @@ -1911,9 +2106,9 @@ }, "require-dev": { "mockery/mockery": "^1.0", - "orchestra/testbench": "^7.0|^8.0", + "orchestra/testbench": "^7.28.2|^8.8.3", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.6" }, "type": "library", "extra": { @@ -1951,20 +2146,20 @@ "issues": "https://github.com/laravel/sanctum/issues", "source": "https://github.com/laravel/sanctum" }, - "time": "2023-05-01T19:39:51+00:00" + "time": "2023-09-07T15:46:33+00:00" }, { "name": "laravel/serializable-closure", - "version": "v1.3.0", + "version": "v1.3.2", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "f23fe9d4e95255dacee1bf3525e0810d1a1b0f37" + "reference": "076fe2cf128bd54b4341cdc6d49b95b34e101e4c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/f23fe9d4e95255dacee1bf3525e0810d1a1b0f37", - "reference": "f23fe9d4e95255dacee1bf3525e0810d1a1b0f37", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/076fe2cf128bd54b4341cdc6d49b95b34e101e4c", + "reference": "076fe2cf128bd54b4341cdc6d49b95b34e101e4c", "shasum": "" }, "require": { @@ -2011,20 +2206,20 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2023-01-30T18:31:20+00:00" + "time": "2023-10-17T13:38:16+00:00" }, { "name": "laravel/tinker", - "version": "v2.8.1", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/laravel/tinker.git", - "reference": "04a2d3bd0d650c0764f70bf49d1ee39393e4eb10" + "reference": "b936d415b252b499e8c3b1f795cd4fc20f57e1f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/04a2d3bd0d650c0764f70bf49d1ee39393e4eb10", - "reference": "04a2d3bd0d650c0764f70bf49d1ee39393e4eb10", + "url": "https://api.github.com/repos/laravel/tinker/zipball/b936d415b252b499e8c3b1f795cd4fc20f57e1f3", + "reference": "b936d415b252b499e8c3b1f795cd4fc20f57e1f3", "shasum": "" }, "require": { @@ -2037,6 +2232,7 @@ }, "require-dev": { "mockery/mockery": "~1.3.3|^1.4.2", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^8.5.8|^9.3.3" }, "suggest": { @@ -2077,22 +2273,22 @@ ], "support": { "issues": "https://github.com/laravel/tinker/issues", - "source": "https://github.com/laravel/tinker/tree/v2.8.1" + "source": "https://github.com/laravel/tinker/tree/v2.8.2" }, - "time": "2023-02-15T16:40:09+00:00" + "time": "2023-08-15T14:27:00+00:00" }, { "name": "league/commonmark", - "version": "2.4.0", + "version": "2.4.1", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "d44a24690f16b8c1808bf13b1bd54ae4c63ea048" + "reference": "3669d6d5f7a47a93c08ddff335e6d945481a1dd5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d44a24690f16b8c1808bf13b1bd54ae4c63ea048", - "reference": "d44a24690f16b8c1808bf13b1bd54ae4c63ea048", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/3669d6d5f7a47a93c08ddff335e6d945481a1dd5", + "reference": "3669d6d5f7a47a93c08ddff335e6d945481a1dd5", "shasum": "" }, "require": { @@ -2185,7 +2381,7 @@ "type": "tidelift" } ], - "time": "2023-03-24T15:16:10+00:00" + "time": "2023-08-30T16:55:00+00:00" }, { "name": "league/config", @@ -2271,16 +2467,16 @@ }, { "name": "league/flysystem", - "version": "3.15.1", + "version": "3.18.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "a141d430414fcb8bf797a18716b09f759a385bed" + "reference": "015633a05aee22490495159237a5944091d8281e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/a141d430414fcb8bf797a18716b09f759a385bed", - "reference": "a141d430414fcb8bf797a18716b09f759a385bed", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/015633a05aee22490495159237a5944091d8281e", + "reference": "015633a05aee22490495159237a5944091d8281e", "shasum": "" }, "require": { @@ -2289,6 +2485,8 @@ "php": "^8.0.2" }, "conflict": { + "async-aws/core": "<1.19.0", + "async-aws/s3": "<1.14.0", "aws/aws-sdk-php": "3.209.31 || 3.210.0", "guzzlehttp/guzzle": "<7.0", "guzzlehttp/ringphp": "<1.1.1", @@ -2296,8 +2494,8 @@ "symfony/http-client": "<5.2" }, "require-dev": { - "async-aws/s3": "^1.5", - "async-aws/simple-s3": "^1.1", + "async-aws/s3": "^1.5 || ^2.0", + "async-aws/simple-s3": "^1.1 || ^2.0", "aws/aws-sdk-php": "^3.220.0", "composer/semver": "^3.0", "ext-fileinfo": "*", @@ -2307,8 +2505,8 @@ "google/cloud-storage": "^1.23", "microsoft/azure-storage-blob": "^1.1", "phpseclib/phpseclib": "^3.0.14", - "phpstan/phpstan": "^0.12.26", - "phpunit/phpunit": "^9.5.11", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.11|^10.0", "sabre/dav": "^4.3.1" }, "type": "library", @@ -2343,7 +2541,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.15.1" + "source": "https://github.com/thephpleague/flysystem/tree/3.18.0" }, "funding": [ { @@ -2355,20 +2553,20 @@ "type": "github" } ], - "time": "2023-05-04T09:04:26+00:00" + "time": "2023-10-20T17:59:40+00:00" }, { "name": "league/flysystem-ftp", - "version": "3.15.0", + "version": "3.16.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-ftp.git", - "reference": "8b288ff74e85b6d7a247aa7898490e4bd4215795" + "reference": "60b6c44194ee94d53eb81971637ef017e123fb20" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-ftp/zipball/8b288ff74e85b6d7a247aa7898490e4bd4215795", - "reference": "8b288ff74e85b6d7a247aa7898490e4bd4215795", + "url": "https://api.github.com/repos/thephpleague/flysystem-ftp/zipball/60b6c44194ee94d53eb81971637ef017e123fb20", + "reference": "60b6c44194ee94d53eb81971637ef017e123fb20", "shasum": "" }, "require": { @@ -2403,7 +2601,7 @@ "ftpd" ], "support": { - "source": "https://github.com/thephpleague/flysystem-ftp/tree/3.15.0" + "source": "https://github.com/thephpleague/flysystem-ftp/tree/3.16.0" }, "funding": [ { @@ -2415,20 +2613,20 @@ "type": "github" } ], - "time": "2023-05-02T20:02:14+00:00" + "time": "2023-08-30T10:17:23+00:00" }, { "name": "league/flysystem-local", - "version": "3.15.0", + "version": "3.18.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-local.git", - "reference": "543f64c397fefdf9cfeac443ffb6beff602796b3" + "reference": "e7381ef7643f658b87efb7dbe98fe538fb1bbf32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/543f64c397fefdf9cfeac443ffb6beff602796b3", - "reference": "543f64c397fefdf9cfeac443ffb6beff602796b3", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/e7381ef7643f658b87efb7dbe98fe538fb1bbf32", + "reference": "e7381ef7643f658b87efb7dbe98fe538fb1bbf32", "shasum": "" }, "require": { @@ -2463,7 +2661,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem-local/issues", - "source": "https://github.com/thephpleague/flysystem-local/tree/3.15.0" + "source": "https://github.com/thephpleague/flysystem-local/tree/3.18.0" }, "funding": [ { @@ -2475,20 +2673,20 @@ "type": "github" } ], - "time": "2023-05-02T20:02:14+00:00" + "time": "2023-10-19T20:07:13+00:00" }, { "name": "league/flysystem-sftp-v3", - "version": "3.15.0", + "version": "3.16.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-sftp-v3.git", - "reference": "659d85c63489ef11130b648f2740277a17337c8b" + "reference": "1ba682def8e87fd7fa00883629553c0200d2e974" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-sftp-v3/zipball/659d85c63489ef11130b648f2740277a17337c8b", - "reference": "659d85c63489ef11130b648f2740277a17337c8b", + "url": "https://api.github.com/repos/thephpleague/flysystem-sftp-v3/zipball/1ba682def8e87fd7fa00883629553c0200d2e974", + "reference": "1ba682def8e87fd7fa00883629553c0200d2e974", "shasum": "" }, "require": { @@ -2523,7 +2721,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem-sftp-v3/issues", - "source": "https://github.com/thephpleague/flysystem-sftp-v3/tree/3.15.0" + "source": "https://github.com/thephpleague/flysystem-sftp-v3/tree/3.16.0" }, "funding": [ { @@ -2535,30 +2733,30 @@ "type": "github" } ], - "time": "2023-05-02T20:02:14+00:00" + "time": "2023-08-30T10:25:05+00:00" }, { "name": "league/mime-type-detection", - "version": "1.11.0", + "version": "1.14.0", "source": { "type": "git", "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd" + "reference": "b6a5854368533df0295c5761a0253656a2e52d9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ff6248ea87a9f116e78edd6002e39e5128a0d4dd", - "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/b6a5854368533df0295c5761a0253656a2e52d9e", + "reference": "b6a5854368533df0295c5761a0253656a2e52d9e", "shasum": "" }, "require": { "ext-fileinfo": "*", - "php": "^7.2 || ^8.0" + "php": "^7.4 || ^8.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.2", "phpstan/phpstan": "^0.12.68", - "phpunit/phpunit": "^8.5.8 || ^9.3" + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0" }, "type": "library", "autoload": { @@ -2579,7 +2777,7 @@ "description": "Mime-type detection for Flysystem", "support": { "issues": "https://github.com/thephpleague/mime-type-detection/issues", - "source": "https://github.com/thephpleague/mime-type-detection/tree/1.11.0" + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.14.0" }, "funding": [ { @@ -2591,7 +2789,7 @@ "type": "tidelift" } ], - "time": "2022-04-17T13:12:02+00:00" + "time": "2023-10-17T14:13:20+00:00" }, { "name": "maennchen/zipstream-php", @@ -2781,18 +2979,85 @@ }, "time": "2022-12-02T22:17:43+00:00" }, + { + "name": "masterminds/html5", + "version": "2.8.1", + "source": { + "type": "git", + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f47dcf3c70c584de14f21143c55d9939631bc6cf", + "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Masterminds\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", + "keywords": [ + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" + ], + "support": { + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.8.1" + }, + "time": "2023-05-10T11:58:31+00:00" + }, { "name": "monolog/monolog", - "version": "3.4.0", + "version": "3.5.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "e2392369686d420ca32df3803de28b5d6f76867d" + "reference": "c915e2634718dbc8a4a15c61b0e62e7a44e14448" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/e2392369686d420ca32df3803de28b5d6f76867d", - "reference": "e2392369686d420ca32df3803de28b5d6f76867d", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/c915e2634718dbc8a4a15c61b0e62e7a44e14448", + "reference": "c915e2634718dbc8a4a15c61b0e62e7a44e14448", "shasum": "" }, "require": { @@ -2868,7 +3133,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.4.0" + "source": "https://github.com/Seldaek/monolog/tree/3.5.0" }, "funding": [ { @@ -2880,29 +3145,33 @@ "type": "tidelift" } ], - "time": "2023-06-21T08:46:11+00:00" + "time": "2023-10-27T15:32:31+00:00" }, { "name": "nesbot/carbon", - "version": "2.67.0", + "version": "2.71.0", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "c1001b3bc75039b07f38a79db5237c4c529e04c8" + "reference": "98276233188583f2ff845a0f992a235472d9466a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/c1001b3bc75039b07f38a79db5237c4c529e04c8", - "reference": "c1001b3bc75039b07f38a79db5237c4c529e04c8", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/98276233188583f2ff845a0f992a235472d9466a", + "reference": "98276233188583f2ff845a0f992a235472d9466a", "shasum": "" }, "require": { "ext-json": "*", "php": "^7.1.8 || ^8.0", + "psr/clock": "^1.0", "symfony/polyfill-mbstring": "^1.0", "symfony/polyfill-php80": "^1.16", "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" }, + "provide": { + "psr/clock-implementation": "1.0" + }, "require-dev": { "doctrine/dbal": "^2.0 || ^3.1.4", "doctrine/orm": "^2.7", @@ -2982,25 +3251,25 @@ "type": "tidelift" } ], - "time": "2023-05-25T22:09:47+00:00" + "time": "2023-09-25T11:31:05+00:00" }, { "name": "nette/schema", - "version": "v1.2.3", + "version": "v1.2.5", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f" + "reference": "0462f0166e823aad657c9224d0f849ecac1ba10a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/abbdbb70e0245d5f3bf77874cea1dfb0c930d06f", - "reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f", + "url": "https://api.github.com/repos/nette/schema/zipball/0462f0166e823aad657c9224d0f849ecac1ba10a", + "reference": "0462f0166e823aad657c9224d0f849ecac1ba10a", "shasum": "" }, "require": { "nette/utils": "^2.5.7 || ^3.1.5 || ^4.0", - "php": ">=7.1 <8.3" + "php": "7.1 - 8.3" }, "require-dev": { "nette/tester": "^2.3 || ^2.4", @@ -3042,26 +3311,26 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.2.3" + "source": "https://github.com/nette/schema/tree/v1.2.5" }, - "time": "2022-10-13T01:24:26+00:00" + "time": "2023-10-05T20:37:59+00:00" }, { "name": "nette/utils", - "version": "v4.0.0", + "version": "v4.0.3", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "cacdbf5a91a657ede665c541eda28941d4b09c1e" + "reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/cacdbf5a91a657ede665c541eda28941d4b09c1e", - "reference": "cacdbf5a91a657ede665c541eda28941d4b09c1e", + "url": "https://api.github.com/repos/nette/utils/zipball/a9d127dd6a203ce6d255b2e2db49759f7506e015", + "reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015", "shasum": "" }, "require": { - "php": ">=8.0 <8.3" + "php": ">=8.0 <8.4" }, "conflict": { "nette/finder": "<3", @@ -3069,7 +3338,7 @@ }, "require-dev": { "jetbrains/phpstorm-attributes": "dev-master", - "nette/tester": "^2.4", + "nette/tester": "^2.5", "phpstan/phpstan": "^1.0", "tracy/tracy": "^2.9" }, @@ -3079,8 +3348,7 @@ "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", "ext-json": "to use Nette\\Utils\\Json", "ext-mbstring": "to use Strings::lower() etc...", - "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()", - "ext-xml": "to use Strings::length() etc. when mbstring is not available" + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" }, "type": "library", "extra": { @@ -3129,22 +3397,22 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.0" + "source": "https://github.com/nette/utils/tree/v4.0.3" }, - "time": "2023-02-02T10:41:53+00:00" + "time": "2023-10-29T21:02:13+00:00" }, { "name": "nikic/php-parser", - "version": "v4.16.0", + "version": "v4.17.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "19526a33fb561ef417e822e85f08a00db4059c17" + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/19526a33fb561ef417e822e85f08a00db4059c17", - "reference": "19526a33fb561ef417e822e85f08a00db4059c17", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", "shasum": "" }, "require": { @@ -3185,9 +3453,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.16.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" }, - "time": "2023-06-25T14:52:30+00:00" + "time": "2023-08-13T19:53:39+00:00" }, { "name": "nunomaduro/termwind", @@ -3470,6 +3738,96 @@ }, "time": "2020-10-15T08:29:30+00:00" }, + { + "name": "phenx/php-font-lib", + "version": "0.5.4", + "source": { + "type": "git", + "url": "https://github.com/dompdf/php-font-lib.git", + "reference": "dd448ad1ce34c63d09baccd05415e361300c35b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/dd448ad1ce34c63d09baccd05415e361300c35b4", + "reference": "dd448ad1ce34c63d09baccd05415e361300c35b4", + "shasum": "" + }, + "require": { + "ext-mbstring": "*" + }, + "require-dev": { + "symfony/phpunit-bridge": "^3 || ^4 || ^5" + }, + "type": "library", + "autoload": { + "psr-4": { + "FontLib\\": "src/FontLib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0" + ], + "authors": [ + { + "name": "Fabien Ménager", + "email": "fabien.menager@gmail.com" + } + ], + "description": "A library to read, parse, export and make subsets of different types of font files.", + "homepage": "https://github.com/PhenX/php-font-lib", + "support": { + "issues": "https://github.com/dompdf/php-font-lib/issues", + "source": "https://github.com/dompdf/php-font-lib/tree/0.5.4" + }, + "time": "2021-12-17T19:44:54+00:00" + }, + { + "name": "phenx/php-svg-lib", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/dompdf/php-svg-lib.git", + "reference": "76876c6cf3080bcb6f249d7d59705108166a6685" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/76876c6cf3080bcb6f249d7d59705108166a6685", + "reference": "76876c6cf3080bcb6f249d7d59705108166a6685", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^7.1 || ^8.0", + "sabberworm/php-css-parser": "^8.4" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Svg\\": "src/Svg" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0" + ], + "authors": [ + { + "name": "Fabien Ménager", + "email": "fabien.menager@gmail.com" + } + ], + "description": "A library to read, parse and export to PDF SVG files.", + "homepage": "https://github.com/PhenX/php-svg-lib", + "support": { + "issues": "https://github.com/dompdf/php-svg-lib/issues", + "source": "https://github.com/dompdf/php-svg-lib/tree/0.5.0" + }, + "time": "2022-09-06T12:16:56+00:00" + }, { "name": "php-http/client-common", "version": "2.7.0", @@ -3541,16 +3899,16 @@ }, { "name": "php-http/discovery", - "version": "1.19.0", + "version": "1.19.1", "source": { "type": "git", "url": "https://github.com/php-http/discovery.git", - "reference": "1856a119a0b0ba8da8b5c33c080aa7af8fac25b4" + "reference": "57f3de01d32085fea20865f9b16fb0e69347c39e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/discovery/zipball/1856a119a0b0ba8da8b5c33c080aa7af8fac25b4", - "reference": "1856a119a0b0ba8da8b5c33c080aa7af8fac25b4", + "url": "https://api.github.com/repos/php-http/discovery/zipball/57f3de01d32085fea20865f9b16fb0e69347c39e", + "reference": "57f3de01d32085fea20865f9b16fb0e69347c39e", "shasum": "" }, "require": { @@ -3613,9 +3971,9 @@ ], "support": { "issues": "https://github.com/php-http/discovery/issues", - "source": "https://github.com/php-http/discovery/tree/1.19.0" + "source": "https://github.com/php-http/discovery/tree/1.19.1" }, - "time": "2023-06-19T08:45:36+00:00" + "time": "2023-07-11T07:02:26+00:00" }, { "name": "php-http/httplug", @@ -3800,31 +4158,26 @@ }, { "name": "php-http/promise", - "version": "1.1.0", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/php-http/promise.git", - "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88" + "reference": "ef4905bfb492ff389eb7f12e26925a0f20073050" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/promise/zipball/4c4c1f9b7289a2ec57cde7f1e9762a5789506f88", - "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88", + "url": "https://api.github.com/repos/php-http/promise/zipball/ef4905bfb492ff389eb7f12e26925a0f20073050", + "reference": "ef4905bfb492ff389eb7f12e26925a0f20073050", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "friends-of-phpspec/phpspec-code-coverage": "^4.3.2", - "phpspec/phpspec": "^5.1.2 || ^6.2" + "friends-of-phpspec/phpspec-code-coverage": "^4.3.2 || ^6.3", + "phpspec/phpspec": "^5.1.2 || ^6.2 || ^7.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, "autoload": { "psr-4": { "Http\\Promise\\": "src/" @@ -3851,9 +4204,9 @@ ], "support": { "issues": "https://github.com/php-http/promise/issues", - "source": "https://github.com/php-http/promise/tree/1.1.0" + "source": "https://github.com/php-http/promise/tree/1.2.0" }, - "time": "2020-07-07T09:29:14+00:00" + "time": "2023-10-24T09:20:26+00:00" }, { "name": "phpoffice/phpspreadsheet", @@ -4037,16 +4390,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "3.0.20", + "version": "3.0.33", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "543a1da81111a0bfd6ae7bbc2865c5e89ed3fc67" + "reference": "33fa69b2514a61138dd48e7a49f99445711e0ad0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/543a1da81111a0bfd6ae7bbc2865c5e89ed3fc67", - "reference": "543a1da81111a0bfd6ae7bbc2865c5e89ed3fc67", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/33fa69b2514a61138dd48e7a49f99445711e0ad0", + "reference": "33fa69b2514a61138dd48e7a49f99445711e0ad0", "shasum": "" }, "require": { @@ -4127,7 +4480,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/3.0.20" + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.33" }, "funding": [ { @@ -4143,7 +4496,7 @@ "type": "tidelift" } ], - "time": "2023-06-13T06:30:34+00:00" + "time": "2023-10-21T14:00:39+00:00" }, { "name": "psr/cache", @@ -4194,6 +4547,54 @@ }, "time": "2021-02-03T23:26:27+00:00" }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, { "name": "psr/container", "version": "2.0.2", @@ -4299,16 +4700,16 @@ }, { "name": "psr/http-client", - "version": "1.0.2", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/php-fig/http-client.git", - "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31" + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/0955afe48220520692d2d09f7ab7e0f93ffd6a31", - "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", "shasum": "" }, "require": { @@ -4345,9 +4746,9 @@ "psr-18" ], "support": { - "source": "https://github.com/php-fig/http-client/tree/1.0.2" + "source": "https://github.com/php-fig/http-client" }, - "time": "2023-04-10T20:12:12+00:00" + "time": "2023-09-23T14:17:50+00:00" }, { "name": "psr/http-factory", @@ -4560,16 +4961,16 @@ }, { "name": "psy/psysh", - "version": "v0.11.18", + "version": "v0.11.22", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "4f00ee9e236fa6a48f4560d1300b9c961a70a7ec" + "reference": "128fa1b608be651999ed9789c95e6e2a31b5802b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/4f00ee9e236fa6a48f4560d1300b9c961a70a7ec", - "reference": "4f00ee9e236fa6a48f4560d1300b9c961a70a7ec", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/128fa1b608be651999ed9789c95e6e2a31b5802b", + "reference": "128fa1b608be651999ed9789c95e6e2a31b5802b", "shasum": "" }, "require": { @@ -4598,7 +4999,11 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "0.11.x-dev" + "dev-0.11": "0.11.x-dev" + }, + "bamarni-bin": { + "bin-links": false, + "forward-command": false } }, "autoload": { @@ -4630,9 +5035,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.11.18" + "source": "https://github.com/bobthecow/psysh/tree/v0.11.22" }, - "time": "2023-05-23T02:31:11+00:00" + "time": "2023-10-14T21:56:36+00:00" }, { "name": "ralouphie/getallheaders", @@ -4678,50 +5083,6 @@ }, "time": "2019-03-08T08:55:37+00:00" }, - { - "name": "ralouphie/mimey", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/mimey.git", - "reference": "2a0e997c733b7c2f9f8b61cafb006fd5fb9fa15a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/mimey/zipball/2a0e997c733b7c2f9f8b61cafb006fd5fb9fa15a", - "reference": "2a0e997c733b7c2f9f8b61cafb006fd5fb9fa15a", - "shasum": "" - }, - "require": { - "php": ">=5.3" - }, - "require-dev": { - "phpunit/phpunit": "~3.7.0", - "satooshi/php-coveralls": ">=1.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Mimey\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "PHP package for converting file extensions to MIME types and vice versa.", - "support": { - "issues": "https://github.com/ralouphie/mimey/issues", - "source": "https://github.com/ralouphie/mimey/tree/master" - }, - "time": "2016-09-28T03:36:23+00:00" - }, { "name": "ramsey/collection", "version": "2.0.0", @@ -4903,6 +5264,59 @@ ], "time": "2023-04-15T23:01:58+00:00" }, + { + "name": "sabberworm/php-css-parser", + "version": "8.4.0", + "source": { + "type": "git", + "url": "https://github.com/sabberworm/PHP-CSS-Parser.git", + "reference": "e41d2140031d533348b2192a83f02d8dd8a71d30" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/e41d2140031d533348b2192a83f02d8dd8a71d30", + "reference": "e41d2140031d533348b2192a83f02d8dd8a71d30", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=5.6.20" + }, + "require-dev": { + "codacy/coverage": "^1.4", + "phpunit/phpunit": "^4.8.36" + }, + "suggest": { + "ext-mbstring": "for parsing UTF-8 CSS" + }, + "type": "library", + "autoload": { + "psr-4": { + "Sabberworm\\CSS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Raphael Schweikert" + } + ], + "description": "Parser for CSS Files written in PHP", + "homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser", + "keywords": [ + "css", + "parser", + "stylesheet" + ], + "support": { + "issues": "https://github.com/sabberworm/PHP-CSS-Parser/issues", + "source": "https://github.com/sabberworm/PHP-CSS-Parser/tree/8.4.0" + }, + "time": "2021-12-11T13:40:54+00:00" + }, { "name": "sentry/sdk", "version": "3.5.0", @@ -4962,16 +5376,16 @@ }, { "name": "sentry/sentry", - "version": "3.20.1", + "version": "3.22.0", "source": { "type": "git", "url": "https://github.com/getsentry/sentry-php.git", - "reference": "644ad9768c18139a80ac510090fad000d9ffd8a4" + "reference": "c0e3df5a5c1d133cd9461e7672568ff07042c19d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/644ad9768c18139a80ac510090fad000d9ffd8a4", - "reference": "644ad9768c18139a80ac510090fad000d9ffd8a4", + "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/c0e3df5a5c1d133cd9461e7672568ff07042c19d", + "reference": "c0e3df5a5c1d133cd9461e7672568ff07042c19d", "shasum": "" }, "require": { @@ -4989,7 +5403,7 @@ "psr/http-factory": "^1.0", "psr/http-factory-implementation": "^1.0", "psr/log": "^1.0|^2.0|^3.0", - "symfony/options-resolver": "^3.4.43|^4.4.30|^5.0.11|^6.0", + "symfony/options-resolver": "^3.4.43|^4.4.30|^5.0.11|^6.0|^7.0", "symfony/polyfill-php80": "^1.17" }, "conflict": { @@ -5015,11 +5429,6 @@ "monolog/monolog": "Allow sending log messages to Sentry by using the included Monolog handler." }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.13.x-dev" - } - }, "autoload": { "files": [ "src/functions.php" @@ -5051,7 +5460,7 @@ ], "support": { "issues": "https://github.com/getsentry/sentry-php/issues", - "source": "https://github.com/getsentry/sentry-php/tree/3.20.1" + "source": "https://github.com/getsentry/sentry-php/tree/3.22.0" }, "funding": [ { @@ -5063,20 +5472,20 @@ "type": "custom" } ], - "time": "2023-06-26T11:01:40+00:00" + "time": "2023-10-23T20:34:53+00:00" }, { "name": "sentry/sentry-laravel", - "version": "3.5.1", + "version": "3.8.2", "source": { "type": "git", "url": "https://github.com/getsentry/sentry-laravel.git", - "reference": "85c2168e469ca73ee90de5e71cef53153e949e64" + "reference": "1293e5732f8405e12f000cdf5dee78c927a18de0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/getsentry/sentry-laravel/zipball/85c2168e469ca73ee90de5e71cef53153e949e64", - "reference": "85c2168e469ca73ee90de5e71cef53153e949e64", + "url": "https://api.github.com/repos/getsentry/sentry-laravel/zipball/1293e5732f8405e12f000cdf5dee78c927a18de0", + "reference": "1293e5732f8405e12f000cdf5dee78c927a18de0", "shasum": "" }, "require": { @@ -5084,14 +5493,16 @@ "nyholm/psr7": "^1.0", "php": "^7.2 | ^8.0", "sentry/sdk": "^3.4", - "sentry/sentry": "^3.19", + "sentry/sentry": "^3.20.1", "symfony/psr-http-message-bridge": "^1.0 | ^2.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.11", + "laravel/folio": "^1.0", "laravel/framework": "^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0", "mockery/mockery": "^1.3", "orchestra/testbench": "^4.7 | ^5.1 | ^6.0 | ^7.0 | ^8.0", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^8.4 | ^9.3" }, "type": "library", @@ -5141,7 +5552,7 @@ ], "support": { "issues": "https://github.com/getsentry/sentry-laravel/issues", - "source": "https://github.com/getsentry/sentry-laravel/tree/3.5.1" + "source": "https://github.com/getsentry/sentry-laravel/tree/3.8.2" }, "funding": [ { @@ -5153,20 +5564,20 @@ "type": "custom" } ], - "time": "2023-06-20T13:19:32+00:00" + "time": "2023-10-12T14:38:46+00:00" }, { "name": "spatie/db-dumper", - "version": "3.3.1", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/spatie/db-dumper.git", - "reference": "3b9fd47899bf6a59d3452392121c9ce675d55d34" + "reference": "bbd5ae0f331d47e6534eb307e256c11a65c8e24a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/db-dumper/zipball/3b9fd47899bf6a59d3452392121c9ce675d55d34", - "reference": "3b9fd47899bf6a59d3452392121c9ce675d55d34", + "url": "https://api.github.com/repos/spatie/db-dumper/zipball/bbd5ae0f331d47e6534eb307e256c11a65c8e24a", + "reference": "bbd5ae0f331d47e6534eb307e256c11a65c8e24a", "shasum": "" }, "require": { @@ -5204,7 +5615,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/db-dumper/tree/3.3.1" + "source": "https://github.com/spatie/db-dumper/tree/3.4.0" }, "funding": [ { @@ -5216,20 +5627,20 @@ "type": "github" } ], - "time": "2023-05-02T11:05:31+00:00" + "time": "2023-06-27T08:34:52+00:00" }, { "name": "staudenmeir/eloquent-has-many-deep", - "version": "v1.18.1", + "version": "v1.18.3", "source": { "type": "git", "url": "https://github.com/staudenmeir/eloquent-has-many-deep.git", - "reference": "b3421d637de05d2cc62166c5decb9727a029185c" + "reference": "04cdf25eac68bd02c3aa382ddbf437585bd76708" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/staudenmeir/eloquent-has-many-deep/zipball/b3421d637de05d2cc62166c5decb9727a029185c", - "reference": "b3421d637de05d2cc62166c5decb9727a029185c", + "url": "https://api.github.com/repos/staudenmeir/eloquent-has-many-deep/zipball/04cdf25eac68bd02c3aa382ddbf437585bd76708", + "reference": "04cdf25eac68bd02c3aa382ddbf437585bd76708", "shasum": "" }, "require": { @@ -5239,12 +5650,15 @@ }, "require-dev": { "awobaz/compoships": "^2.2", + "barryvdh/laravel-ide-helper": "^2.13", "illuminate/pagination": "^10.0", "korridor/laravel-has-many-merged": "^1.0", + "mockery/mockery": "^1.6", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^10.1", "staudenmeir/eloquent-eager-limit": "^1.8", - "staudenmeir/eloquent-json-relations": "^1.8", - "staudenmeir/laravel-adjacency-list": "^1.13" + "staudenmeir/eloquent-json-relations": "^1.8.2", + "staudenmeir/laravel-adjacency-list": "^1.13.7" }, "type": "library", "autoload": { @@ -5265,7 +5679,7 @@ "description": "Laravel Eloquent HasManyThrough relationships with unlimited levels", "support": { "issues": "https://github.com/staudenmeir/eloquent-has-many-deep/issues", - "source": "https://github.com/staudenmeir/eloquent-has-many-deep/tree/v1.18.1" + "source": "https://github.com/staudenmeir/eloquent-has-many-deep/tree/v1.18.3" }, "funding": [ { @@ -5273,7 +5687,7 @@ "type": "custom" } ], - "time": "2023-06-20T18:09:39+00:00" + "time": "2023-08-20T17:00:39+00:00" }, { "name": "staudenmeir/eloquent-has-many-deep-contracts", @@ -5318,16 +5732,16 @@ }, { "name": "symfony/console", - "version": "v6.3.0", + "version": "v6.3.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7" + "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7", - "reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7", + "url": "https://api.github.com/repos/symfony/console/zipball/eca495f2ee845130855ddf1cf18460c38966c8b6", + "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6", "shasum": "" }, "require": { @@ -5388,7 +5802,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.3.0" + "source": "https://github.com/symfony/console/tree/v6.3.4" }, "funding": [ { @@ -5404,20 +5818,20 @@ "type": "tidelift" } ], - "time": "2023-05-29T12:49:39+00:00" + "time": "2023-08-16T10:10:12+00:00" }, { "name": "symfony/css-selector", - "version": "v6.3.0", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "88453e64cd86c5b60e8d2fb2c6f953bbc353ffbf" + "reference": "883d961421ab1709877c10ac99451632a3d6fa57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/88453e64cd86c5b60e8d2fb2c6f953bbc353ffbf", - "reference": "88453e64cd86c5b60e8d2fb2c6f953bbc353ffbf", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/883d961421ab1709877c10ac99451632a3d6fa57", + "reference": "883d961421ab1709877c10ac99451632a3d6fa57", "shasum": "" }, "require": { @@ -5453,7 +5867,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v6.3.0" + "source": "https://github.com/symfony/css-selector/tree/v6.3.2" }, "funding": [ { @@ -5469,7 +5883,7 @@ "type": "tidelift" } ], - "time": "2023-03-20T16:43:42+00:00" + "time": "2023-07-12T16:00:22+00:00" }, { "name": "symfony/deprecation-contracts", @@ -5540,16 +5954,16 @@ }, { "name": "symfony/error-handler", - "version": "v6.3.0", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "99d2d814a6351461af350ead4d963bd67451236f" + "reference": "1f69476b64fb47105c06beef757766c376b548c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/99d2d814a6351461af350ead4d963bd67451236f", - "reference": "99d2d814a6351461af350ead4d963bd67451236f", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/1f69476b64fb47105c06beef757766c376b548c4", + "reference": "1f69476b64fb47105c06beef757766c376b548c4", "shasum": "" }, "require": { @@ -5594,7 +6008,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v6.3.0" + "source": "https://github.com/symfony/error-handler/tree/v6.3.5" }, "funding": [ { @@ -5610,20 +6024,20 @@ "type": "tidelift" } ], - "time": "2023-05-10T12:03:13+00:00" + "time": "2023-09-12T06:57:20+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v6.3.0", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa" + "reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa", - "reference": "3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/adb01fe097a4ee930db9258a3cc906b5beb5cf2e", + "reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e", "shasum": "" }, "require": { @@ -5674,7 +6088,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v6.3.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.3.2" }, "funding": [ { @@ -5690,7 +6104,7 @@ "type": "tidelift" } ], - "time": "2023-04-21T14:41:17+00:00" + "time": "2023-07-06T06:56:43+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -5770,16 +6184,16 @@ }, { "name": "symfony/finder", - "version": "v6.3.0", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "d9b01ba073c44cef617c7907ce2419f8d00d75e2" + "reference": "a1b31d88c0e998168ca7792f222cbecee47428c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/d9b01ba073c44cef617c7907ce2419f8d00d75e2", - "reference": "d9b01ba073c44cef617c7907ce2419f8d00d75e2", + "url": "https://api.github.com/repos/symfony/finder/zipball/a1b31d88c0e998168ca7792f222cbecee47428c4", + "reference": "a1b31d88c0e998168ca7792f222cbecee47428c4", "shasum": "" }, "require": { @@ -5814,7 +6228,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.3.0" + "source": "https://github.com/symfony/finder/tree/v6.3.5" }, "funding": [ { @@ -5830,20 +6244,20 @@ "type": "tidelift" } ], - "time": "2023-04-02T01:25:41+00:00" + "time": "2023-09-26T12:56:25+00:00" }, { "name": "symfony/http-client", - "version": "v6.3.1", + "version": "v6.3.7", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "1c828a06aef2f5eeba42026dfc532d4fc5406123" + "reference": "cd67fcaf4524ec6ae5d9b2d9497682d7ad3ce57d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/1c828a06aef2f5eeba42026dfc532d4fc5406123", - "reference": "1c828a06aef2f5eeba42026dfc532d4fc5406123", + "url": "https://api.github.com/repos/symfony/http-client/zipball/cd67fcaf4524ec6ae5d9b2d9497682d7ad3ce57d", + "reference": "cd67fcaf4524ec6ae5d9b2d9497682d7ad3ce57d", "shasum": "" }, "require": { @@ -5906,7 +6320,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v6.3.1" + "source": "https://github.com/symfony/http-client/tree/v6.3.7" }, "funding": [ { @@ -5922,7 +6336,7 @@ "type": "tidelift" } ], - "time": "2023-06-24T11:51:27+00:00" + "time": "2023-10-29T12:41:36+00:00" }, { "name": "symfony/http-client-contracts", @@ -6004,16 +6418,16 @@ }, { "name": "symfony/http-foundation", - "version": "v6.3.1", + "version": "v6.3.7", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "e0ad0d153e1c20069250986cd9e9dd1ccebb0d66" + "reference": "59d1837d5d992d16c2628cd0d6b76acf8d69b33e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e0ad0d153e1c20069250986cd9e9dd1ccebb0d66", - "reference": "e0ad0d153e1c20069250986cd9e9dd1ccebb0d66", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/59d1837d5d992d16c2628cd0d6b76acf8d69b33e", + "reference": "59d1837d5d992d16c2628cd0d6b76acf8d69b33e", "shasum": "" }, "require": { @@ -6023,12 +6437,12 @@ "symfony/polyfill-php83": "^1.27" }, "conflict": { - "symfony/cache": "<6.2" + "symfony/cache": "<6.3" }, "require-dev": { - "doctrine/dbal": "^2.13.1|^3.0", + "doctrine/dbal": "^2.13.1|^3|^4", "predis/predis": "^1.1|^2.0", - "symfony/cache": "^5.4|^6.0", + "symfony/cache": "^6.3", "symfony/dependency-injection": "^5.4|^6.0", "symfony/expression-language": "^5.4|^6.0", "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4", @@ -6061,7 +6475,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.3.1" + "source": "https://github.com/symfony/http-foundation/tree/v6.3.7" }, "funding": [ { @@ -6077,20 +6491,20 @@ "type": "tidelift" } ], - "time": "2023-06-24T11:51:27+00:00" + "time": "2023-10-28T23:55:27+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.3.1", + "version": "v6.3.7", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "161e16fd2e35fb4881a43bc8b383dfd5be4ac374" + "reference": "6d4098095f93279d9536a0e9124439560cc764d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/161e16fd2e35fb4881a43bc8b383dfd5be4ac374", - "reference": "161e16fd2e35fb4881a43bc8b383dfd5be4ac374", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/6d4098095f93279d9536a0e9124439560cc764d0", + "reference": "6d4098095f93279d9536a0e9124439560cc764d0", "shasum": "" }, "require": { @@ -6099,7 +6513,7 @@ "symfony/deprecation-contracts": "^2.5|^3", "symfony/error-handler": "^6.3", "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/http-foundation": "^6.2.7", + "symfony/http-foundation": "^6.3.4", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -6107,7 +6521,7 @@ "symfony/cache": "<5.4", "symfony/config": "<6.1", "symfony/console": "<5.4", - "symfony/dependency-injection": "<6.3", + "symfony/dependency-injection": "<6.3.4", "symfony/doctrine-bridge": "<5.4", "symfony/form": "<5.4", "symfony/http-client": "<5.4", @@ -6131,7 +6545,7 @@ "symfony/config": "^6.1", "symfony/console": "^5.4|^6.0", "symfony/css-selector": "^5.4|^6.0", - "symfony/dependency-injection": "^6.3", + "symfony/dependency-injection": "^6.3.4", "symfony/dom-crawler": "^5.4|^6.0", "symfony/expression-language": "^5.4|^6.0", "symfony/finder": "^5.4|^6.0", @@ -6174,7 +6588,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.3.1" + "source": "https://github.com/symfony/http-kernel/tree/v6.3.7" }, "funding": [ { @@ -6190,20 +6604,20 @@ "type": "tidelift" } ], - "time": "2023-06-26T06:07:32+00:00" + "time": "2023-10-29T14:31:45+00:00" }, { "name": "symfony/mailer", - "version": "v6.3.0", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "7b03d9be1dea29bfec0a6c7b603f5072a4c97435" + "reference": "d89611a7830d51b5e118bca38e390dea92f9ea06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/7b03d9be1dea29bfec0a6c7b603f5072a4c97435", - "reference": "7b03d9be1dea29bfec0a6c7b603f5072a4c97435", + "url": "https://api.github.com/repos/symfony/mailer/zipball/d89611a7830d51b5e118bca38e390dea92f9ea06", + "reference": "d89611a7830d51b5e118bca38e390dea92f9ea06", "shasum": "" }, "require": { @@ -6254,7 +6668,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v6.3.0" + "source": "https://github.com/symfony/mailer/tree/v6.3.5" }, "funding": [ { @@ -6270,24 +6684,25 @@ "type": "tidelift" } ], - "time": "2023-05-29T12:49:39+00:00" + "time": "2023-09-06T09:47:15+00:00" }, { "name": "symfony/mime", - "version": "v6.3.0", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "7b5d2121858cd6efbed778abce9cfdd7ab1f62ad" + "reference": "d5179eedf1cb2946dbd760475ebf05c251ef6a6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/7b5d2121858cd6efbed778abce9cfdd7ab1f62ad", - "reference": "7b5d2121858cd6efbed778abce9cfdd7ab1f62ad", + "url": "https://api.github.com/repos/symfony/mime/zipball/d5179eedf1cb2946dbd760475ebf05c251ef6a6e", + "reference": "d5179eedf1cb2946dbd760475ebf05c251ef6a6e", "shasum": "" }, "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-intl-idn": "^1.10", "symfony/polyfill-mbstring": "^1.0" }, @@ -6296,7 +6711,7 @@ "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", "symfony/mailer": "<5.4", - "symfony/serializer": "<6.2" + "symfony/serializer": "<6.2.13|>=6.3,<6.3.2" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3.1|^4", @@ -6305,7 +6720,7 @@ "symfony/dependency-injection": "^5.4|^6.0", "symfony/property-access": "^5.4|^6.0", "symfony/property-info": "^5.4|^6.0", - "symfony/serializer": "^6.2" + "symfony/serializer": "~6.2.13|^6.3.2" }, "type": "library", "autoload": { @@ -6337,7 +6752,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v6.3.0" + "source": "https://github.com/symfony/mime/tree/v6.3.5" }, "funding": [ { @@ -6353,7 +6768,7 @@ "type": "tidelift" } ], - "time": "2023-04-28T15:57:00+00:00" + "time": "2023-09-29T06:59:36+00:00" }, { "name": "symfony/options-resolver", @@ -6424,16 +6839,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", "shasum": "" }, "require": { @@ -6448,7 +6863,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6486,7 +6901,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" }, "funding": [ { @@ -6502,20 +6917,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354" + "reference": "875e90aeea2777b6f135677f618529449334a612" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", + "reference": "875e90aeea2777b6f135677f618529449334a612", "shasum": "" }, "require": { @@ -6527,7 +6942,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6567,7 +6982,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0" }, "funding": [ { @@ -6583,20 +6998,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "639084e360537a19f9ee352433b84ce831f3d2da" + "reference": "ecaafce9f77234a6a449d29e49267ba10499116d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/639084e360537a19f9ee352433b84ce831f3d2da", - "reference": "639084e360537a19f9ee352433b84ce831f3d2da", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/ecaafce9f77234a6a449d29e49267ba10499116d", + "reference": "ecaafce9f77234a6a449d29e49267ba10499116d", "shasum": "" }, "require": { @@ -6610,7 +7025,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6654,7 +7069,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.28.0" }, "funding": [ { @@ -6670,20 +7085,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:30:37+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", "shasum": "" }, "require": { @@ -6695,7 +7110,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6738,7 +7153,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0" }, "funding": [ { @@ -6754,20 +7169,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + "reference": "42292d99c55abe617799667f454222c54c60e229" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", "shasum": "" }, "require": { @@ -6782,7 +7197,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6821,7 +7236,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" }, "funding": [ { @@ -6837,20 +7252,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-07-28T09:04:16+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "869329b1e9894268a8a61dabb69153029b7a8c97" + "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97", - "reference": "869329b1e9894268a8a61dabb69153029b7a8c97", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/70f4aebd92afca2f865444d30a4d2151c13c3179", + "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179", "shasum": "" }, "require": { @@ -6859,7 +7274,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6897,7 +7312,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php72/tree/v1.28.0" }, "funding": [ { @@ -6913,20 +7328,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", "shasum": "" }, "require": { @@ -6935,7 +7350,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6980,7 +7395,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" }, "funding": [ { @@ -6996,20 +7411,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-php83", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "508c652ba3ccf69f8c97f251534f229791b52a57" + "reference": "b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/508c652ba3ccf69f8c97f251534f229791b52a57", - "reference": "508c652ba3ccf69f8c97f251534f229791b52a57", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11", + "reference": "b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11", "shasum": "" }, "require": { @@ -7019,7 +7434,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -7032,7 +7447,10 @@ ], "psr-4": { "Symfony\\Polyfill\\Php83\\": "" - } + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -7057,7 +7475,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.28.0" }, "funding": [ { @@ -7073,20 +7491,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-08-16T06:22:46+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", - "reference": "f3cf1a645c2734236ed1e2e671e273eeb3586166" + "reference": "9c44518a5aff8da565c8a55dbe85d2769e6f630e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/f3cf1a645c2734236ed1e2e671e273eeb3586166", - "reference": "f3cf1a645c2734236ed1e2e671e273eeb3586166", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/9c44518a5aff8da565c8a55dbe85d2769e6f630e", + "reference": "9c44518a5aff8da565c8a55dbe85d2769e6f630e", "shasum": "" }, "require": { @@ -7101,7 +7519,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -7139,7 +7557,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.28.0" }, "funding": [ { @@ -7155,20 +7573,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/process", - "version": "v6.3.0", + "version": "v6.3.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "8741e3ed7fe2e91ec099e02446fb86667a0f1628" + "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/8741e3ed7fe2e91ec099e02446fb86667a0f1628", - "reference": "8741e3ed7fe2e91ec099e02446fb86667a0f1628", + "url": "https://api.github.com/repos/symfony/process/zipball/0b5c29118f2e980d455d2e34a5659f4579847c54", + "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54", "shasum": "" }, "require": { @@ -7200,7 +7618,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.3.0" + "source": "https://github.com/symfony/process/tree/v6.3.4" }, "funding": [ { @@ -7216,25 +7634,26 @@ "type": "tidelift" } ], - "time": "2023-05-19T08:06:44+00:00" + "time": "2023-08-07T10:39:22+00:00" }, { "name": "symfony/psr-http-message-bridge", - "version": "v2.2.0", + "version": "v2.3.1", "source": { "type": "git", "url": "https://github.com/symfony/psr-http-message-bridge.git", - "reference": "28a732c05bbad801304ad5a5c674cf2970508993" + "reference": "581ca6067eb62640de5ff08ee1ba6850a0ee472e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/28a732c05bbad801304ad5a5c674cf2970508993", - "reference": "28a732c05bbad801304ad5a5c674cf2970508993", + "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/581ca6067eb62640de5ff08ee1ba6850a0ee472e", + "reference": "581ca6067eb62640de5ff08ee1ba6850a0ee472e", "shasum": "" }, "require": { "php": ">=7.2.5", "psr/http-message": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", "symfony/http-foundation": "^5.4 || ^6.0" }, "require-dev": { @@ -7253,7 +7672,7 @@ "type": "symfony-bridge", "extra": { "branch-alias": { - "dev-main": "2.2-dev" + "dev-main": "2.3-dev" } }, "autoload": { @@ -7288,7 +7707,7 @@ ], "support": { "issues": "https://github.com/symfony/psr-http-message-bridge/issues", - "source": "https://github.com/symfony/psr-http-message-bridge/tree/v2.2.0" + "source": "https://github.com/symfony/psr-http-message-bridge/tree/v2.3.1" }, "funding": [ { @@ -7304,24 +7723,25 @@ "type": "tidelift" } ], - "time": "2023-04-21T08:40:19+00:00" + "time": "2023-07-26T11:53:26+00:00" }, { "name": "symfony/routing", - "version": "v6.3.1", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "d37ad1779c38b8eb71996d17dc13030dcb7f9cf5" + "reference": "82616e59acd3e3d9c916bba798326cb7796d7d31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/d37ad1779c38b8eb71996d17dc13030dcb7f9cf5", - "reference": "d37ad1779c38b8eb71996d17dc13030dcb7f9cf5", + "url": "https://api.github.com/repos/symfony/routing/zipball/82616e59acd3e3d9c916bba798326cb7796d7d31", + "reference": "82616e59acd3e3d9c916bba798326cb7796d7d31", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "doctrine/annotations": "<1.12", @@ -7370,7 +7790,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v6.3.1" + "source": "https://github.com/symfony/routing/tree/v6.3.5" }, "funding": [ { @@ -7386,7 +7806,7 @@ "type": "tidelift" } ], - "time": "2023-06-05T15:30:22+00:00" + "time": "2023-09-20T16:05:51+00:00" }, { "name": "symfony/service-contracts", @@ -7472,16 +7892,16 @@ }, { "name": "symfony/string", - "version": "v6.3.0", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f" + "reference": "13d76d0fb049051ed12a04bef4f9de8715bea339" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f2e190ee75ff0f5eced645ec0be5c66fac81f51f", - "reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f", + "url": "https://api.github.com/repos/symfony/string/zipball/13d76d0fb049051ed12a04bef4f9de8715bea339", + "reference": "13d76d0fb049051ed12a04bef4f9de8715bea339", "shasum": "" }, "require": { @@ -7538,7 +7958,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.3.0" + "source": "https://github.com/symfony/string/tree/v6.3.5" }, "funding": [ { @@ -7554,24 +7974,25 @@ "type": "tidelift" } ], - "time": "2023-03-21T21:06:29+00:00" + "time": "2023-09-18T10:38:32+00:00" }, { "name": "symfony/translation", - "version": "v6.3.0", + "version": "v6.3.7", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "f72b2cba8f79dd9d536f534f76874b58ad37876f" + "reference": "30212e7c87dcb79c83f6362b00bde0e0b1213499" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/f72b2cba8f79dd9d536f534f76874b58ad37876f", - "reference": "f72b2cba8f79dd9d536f534f76874b58ad37876f", + "url": "https://api.github.com/repos/symfony/translation/zipball/30212e7c87dcb79c83f6362b00bde0e0b1213499", + "reference": "30212e7c87dcb79c83f6362b00bde0e0b1213499", "shasum": "" }, "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/translation-contracts": "^2.5|^3.0" }, @@ -7632,7 +8053,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.3.0" + "source": "https://github.com/symfony/translation/tree/v6.3.7" }, "funding": [ { @@ -7648,7 +8069,7 @@ "type": "tidelift" } ], - "time": "2023-05-19T12:46:45+00:00" + "time": "2023-10-28T23:11:45+00:00" }, { "name": "symfony/translation-contracts", @@ -7804,20 +8225,21 @@ }, { "name": "symfony/var-dumper", - "version": "v6.3.1", + "version": "v6.3.6", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "c81268d6960ddb47af17391a27d222bd58cf0515" + "reference": "999ede244507c32b8e43aebaa10e9fce20de7c97" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/c81268d6960ddb47af17391a27d222bd58cf0515", - "reference": "c81268d6960ddb47af17391a27d222bd58cf0515", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/999ede244507c32b8e43aebaa10e9fce20de7c97", + "reference": "999ede244507c32b8e43aebaa10e9fce20de7c97", "shasum": "" }, "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { @@ -7826,6 +8248,7 @@ "require-dev": { "ext-iconv": "*", "symfony/console": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", "symfony/process": "^5.4|^6.0", "symfony/uid": "^5.4|^6.0", "twig/twig": "^2.13|^3.0.4" @@ -7866,7 +8289,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.3.1" + "source": "https://github.com/symfony/var-dumper/tree/v6.3.6" }, "funding": [ { @@ -7882,7 +8305,7 @@ "type": "tidelift" } ], - "time": "2023-06-21T12:08:28+00:00" + "time": "2023-10-12T18:45:56+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -8225,16 +8648,16 @@ }, { "name": "filp/whoops", - "version": "2.15.2", + "version": "2.15.3", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "aac9304c5ed61bf7b1b7a6064bf9806ab842ce73" + "reference": "c83e88a30524f9360b11f585f71e6b17313b7187" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/aac9304c5ed61bf7b1b7a6064bf9806ab842ce73", - "reference": "aac9304c5ed61bf7b1b7a6064bf9806ab842ce73", + "url": "https://api.github.com/repos/filp/whoops/zipball/c83e88a30524f9360b11f585f71e6b17313b7187", + "reference": "c83e88a30524f9360b11f585f71e6b17313b7187", "shasum": "" }, "require": { @@ -8284,7 +8707,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.15.2" + "source": "https://github.com/filp/whoops/tree/2.15.3" }, "funding": [ { @@ -8292,7 +8715,7 @@ "type": "github" } ], - "time": "2023-04-12T12:00:00+00:00" + "time": "2023-07-13T12:00:00+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -8347,16 +8770,16 @@ }, { "name": "laravel/telescope", - "version": "v4.15.0", + "version": "v4.17.0", "source": { "type": "git", "url": "https://github.com/laravel/telescope.git", - "reference": "572a19b4c9b09295848de9a2352737a756a0fb05" + "reference": "7afb0f6f82399c488fe6d3bf8e9489f92004504a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/telescope/zipball/572a19b4c9b09295848de9a2352737a756a0fb05", - "reference": "572a19b4c9b09295848de9a2352737a756a0fb05", + "url": "https://api.github.com/repos/laravel/telescope/zipball/7afb0f6f82399c488fe6d3bf8e9489f92004504a", + "reference": "7afb0f6f82399c488fe6d3bf8e9489f92004504a", "shasum": "" }, "require": { @@ -8412,43 +8835,39 @@ ], "support": { "issues": "https://github.com/laravel/telescope/issues", - "source": "https://github.com/laravel/telescope/tree/v4.15.0" + "source": "https://github.com/laravel/telescope/tree/v4.17.0" }, - "time": "2023-06-08T13:57:22+00:00" + "time": "2023-10-31T15:36:43+00:00" }, { "name": "mockery/mockery", - "version": "1.6.2", + "version": "1.6.6", "source": { "type": "git", "url": "https://github.com/mockery/mockery.git", - "reference": "13a7fa2642c76c58fa2806ef7f565344c817a191" + "reference": "b8e0bb7d8c604046539c1115994632c74dcb361e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/13a7fa2642c76c58fa2806ef7f565344c817a191", - "reference": "13a7fa2642c76c58fa2806ef7f565344c817a191", + "url": "https://api.github.com/repos/mockery/mockery/zipball/b8e0bb7d8c604046539c1115994632c74dcb361e", + "reference": "b8e0bb7d8c604046539c1115994632c74dcb361e", "shasum": "" }, "require": { "hamcrest/hamcrest-php": "^2.0.1", "lib-pcre": ">=7.0", - "php": "^7.4 || ^8.0" + "php": ">=7.3" }, "conflict": { "phpunit/phpunit": "<8.0" }, "require-dev": { - "phpunit/phpunit": "^8.5 || ^9.3", - "psalm/plugin-phpunit": "^0.18", - "vimeo/psalm": "^5.9" + "phpunit/phpunit": "^8.5 || ^9.6.10", + "psalm/plugin-phpunit": "^0.18.4", + "symplify/easy-coding-standard": "^11.5.0", + "vimeo/psalm": "^4.30" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.6.x-dev" - } - }, "autoload": { "files": [ "library/helpers.php", @@ -8466,12 +8885,20 @@ { "name": "Pádraic Brady", "email": "padraic.brady@gmail.com", - "homepage": "http://blog.astrumfutura.com" + "homepage": "https://github.com/padraic", + "role": "Author" }, { "name": "Dave Marshall", "email": "dave.marshall@atstsolutions.co.uk", - "homepage": "http://davedevelopment.co.uk" + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" } ], "description": "Mockery is a simple yet flexible PHP mock object framework", @@ -8489,10 +8916,13 @@ "testing" ], "support": { + "docs": "https://docs.mockery.io/", "issues": "https://github.com/mockery/mockery/issues", - "source": "https://github.com/mockery/mockery/tree/1.6.2" + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" }, - "time": "2023-06-07T09:07:52+00:00" + "time": "2023-08-09T00:03:52+00:00" }, { "name": "myclabs/deep-copy", @@ -8555,40 +8985,40 @@ }, { "name": "nunomaduro/collision", - "version": "v7.6.0", + "version": "v7.10.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "87faf7bc1c42d7fef7c60ec5c143050ce2a6189a" + "reference": "49ec67fa7b002712da8526678abd651c09f375b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/87faf7bc1c42d7fef7c60ec5c143050ce2a6189a", - "reference": "87faf7bc1c42d7fef7c60ec5c143050ce2a6189a", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/49ec67fa7b002712da8526678abd651c09f375b2", + "reference": "49ec67fa7b002712da8526678abd651c09f375b2", "shasum": "" }, "require": { - "filp/whoops": "^2.15.2", + "filp/whoops": "^2.15.3", "nunomaduro/termwind": "^1.15.1", "php": "^8.1.0", - "symfony/console": "^6.3.0" + "symfony/console": "^6.3.4" }, "conflict": { - "phpunit/phpunit": "<10.1.2" + "laravel/framework": ">=11.0.0" }, "require-dev": { - "brianium/paratest": "^7.2.0", - "laravel/framework": "^10.13.5", - "laravel/pint": "^1.10.2", - "laravel/sail": "^1.22.0", - "laravel/sanctum": "^3.2.5", - "laravel/tinker": "^2.8.1", - "nunomaduro/larastan": "^2.6.3", - "orchestra/testbench-core": "^8.5.7", - "pestphp/pest": "^2", - "phpunit/phpunit": "^10.2.2", + "brianium/paratest": "^7.3.0", + "laravel/framework": "^10.28.0", + "laravel/pint": "^1.13.3", + "laravel/sail": "^1.25.0", + "laravel/sanctum": "^3.3.1", + "laravel/tinker": "^2.8.2", + "nunomaduro/larastan": "^2.6.4", + "orchestra/testbench-core": "^8.13.0", + "pestphp/pest": "^2.23.2", + "phpunit/phpunit": "^10.4.1", "sebastian/environment": "^6.0.1", - "spatie/laravel-ignition": "^2.1.3" + "spatie/laravel-ignition": "^2.3.1" }, "type": "library", "extra": { @@ -8647,7 +9077,7 @@ "type": "patreon" } ], - "time": "2023-06-15T10:51:08+00:00" + "time": "2023-10-11T15:45:01+00:00" }, { "name": "phar-io/manifest", @@ -8762,16 +9192,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.26", + "version": "1.10.40", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "5d660cbb7e1b89253a47147ae44044f49832351f" + "reference": "93c84b5bf7669920d823631e39904d69b9c7dc5d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/5d660cbb7e1b89253a47147ae44044f49832351f", - "reference": "5d660cbb7e1b89253a47147ae44044f49832351f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/93c84b5bf7669920d823631e39904d69b9c7dc5d", + "reference": "93c84b5bf7669920d823631e39904d69b9c7dc5d", "shasum": "" }, "require": { @@ -8820,20 +9250,20 @@ "type": "tidelift" } ], - "time": "2023-07-19T12:44:37+00:00" + "time": "2023-10-30T14:48:31+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "10.1.2", + "version": "10.1.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e" + "reference": "355324ca4980b8916c18b9db29f3ef484078f26e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/db1497ec8dd382e82c962f7abbe0320e4882ee4e", - "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/355324ca4980b8916c18b9db29f3ef484078f26e", + "reference": "355324ca4980b8916c18b9db29f3ef484078f26e", "shasum": "" }, "require": { @@ -8890,7 +9320,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.2" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.7" }, "funding": [ { @@ -8898,20 +9328,20 @@ "type": "github" } ], - "time": "2023-05-22T09:04:27+00:00" + "time": "2023-10-04T15:34:17+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "4.0.2", + "version": "4.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "5647d65443818959172645e7ed999217360654b6" + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/5647d65443818959172645e7ed999217360654b6", - "reference": "5647d65443818959172645e7ed999217360654b6", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", "shasum": "" }, "require": { @@ -8951,7 +9381,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.0.2" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" }, "funding": [ { @@ -8959,7 +9389,7 @@ "type": "github" } ], - "time": "2023-05-07T09:13:23+00:00" + "time": "2023-08-31T06:24:48+00:00" }, { "name": "phpunit/php-invoker", @@ -9026,16 +9456,16 @@ }, { "name": "phpunit/php-text-template", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d" + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/9f3d3709577a527025f55bcf0f7ab8052c8bb37d", - "reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", "shasum": "" }, "require": { @@ -9073,7 +9503,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.0" + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" }, "funding": [ { @@ -9081,7 +9512,7 @@ "type": "github" } ], - "time": "2023-02-03T06:56:46+00:00" + "time": "2023-08-31T14:07:24+00:00" }, { "name": "phpunit/php-timer", @@ -9144,16 +9575,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.2.2", + "version": "10.4.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "1ab521b24b88b88310c40c26c0cc4a94ba40ff95" + "reference": "cacd8b9dd224efa8eb28beb69004126c7ca1a1a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1ab521b24b88b88310c40c26c0cc4a94ba40ff95", - "reference": "1ab521b24b88b88310c40c26c0cc4a94ba40ff95", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/cacd8b9dd224efa8eb28beb69004126c7ca1a1a1", + "reference": "cacd8b9dd224efa8eb28beb69004126c7ca1a1a1", "shasum": "" }, "require": { @@ -9167,7 +9598,7 @@ "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", "php": ">=8.1", - "phpunit/php-code-coverage": "^10.1.1", + "phpunit/php-code-coverage": "^10.1.5", "phpunit/php-file-iterator": "^4.0", "phpunit/php-invoker": "^4.0", "phpunit/php-text-template": "^3.0", @@ -9177,8 +9608,8 @@ "sebastian/comparator": "^5.0", "sebastian/diff": "^5.0", "sebastian/environment": "^6.0", - "sebastian/exporter": "^5.0", - "sebastian/global-state": "^6.0", + "sebastian/exporter": "^5.1", + "sebastian/global-state": "^6.0.1", "sebastian/object-enumerator": "^5.0", "sebastian/recursion-context": "^5.0", "sebastian/type": "^4.0", @@ -9193,7 +9624,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.2-dev" + "dev-main": "10.4-dev" } }, "autoload": { @@ -9225,7 +9656,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.2.2" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.4.2" }, "funding": [ { @@ -9241,7 +9672,7 @@ "type": "tidelift" } ], - "time": "2023-06-11T06:15:20+00:00" + "time": "2023-10-26T07:21:45+00:00" }, { "name": "sebastian/cli-parser", @@ -9412,16 +9843,16 @@ }, { "name": "sebastian/comparator", - "version": "5.0.0", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "72f01e6586e0caf6af81297897bd112eb7e9627c" + "reference": "2db5010a484d53ebf536087a70b4a5423c102372" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/72f01e6586e0caf6af81297897bd112eb7e9627c", - "reference": "72f01e6586e0caf6af81297897bd112eb7e9627c", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", + "reference": "2db5010a484d53ebf536087a70b4a5423c102372", "shasum": "" }, "require": { @@ -9432,7 +9863,7 @@ "sebastian/exporter": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^10.3" }, "type": "library", "extra": { @@ -9476,7 +9907,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" }, "funding": [ { @@ -9484,20 +9916,20 @@ "type": "github" } ], - "time": "2023-02-03T07:07:16+00:00" + "time": "2023-08-14T13:18:12+00:00" }, { "name": "sebastian/complexity", - "version": "3.0.0", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6" + "reference": "68cfb347a44871f01e33ab0ef8215966432f6957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/e67d240970c9dc7ea7b2123a6d520e334dd61dc6", - "reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957", + "reference": "68cfb347a44871f01e33ab0ef8215966432f6957", "shasum": "" }, "require": { @@ -9510,7 +9942,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.1-dev" } }, "autoload": { @@ -9533,7 +9965,8 @@ "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.0.0" + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0" }, "funding": [ { @@ -9541,7 +9974,7 @@ "type": "github" } ], - "time": "2023-02-03T06:59:47+00:00" + "time": "2023-09-28T11:50:59+00:00" }, { "name": "sebastian/diff", @@ -9676,16 +10109,16 @@ }, { "name": "sebastian/exporter", - "version": "5.0.0", + "version": "5.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0" + "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0", - "reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/64f51654862e0f5e318db7e9dcc2292c63cdbddc", + "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc", "shasum": "" }, "require": { @@ -9699,7 +10132,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -9741,7 +10174,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.1" }, "funding": [ { @@ -9749,20 +10183,20 @@ "type": "github" } ], - "time": "2023-02-03T07:06:49+00:00" + "time": "2023-09-24T13:22:09+00:00" }, { "name": "sebastian/global-state", - "version": "6.0.0", + "version": "6.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "aab257c712de87b90194febd52e4d184551c2d44" + "reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/aab257c712de87b90194febd52e4d184551c2d44", - "reference": "aab257c712de87b90194febd52e4d184551c2d44", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/7ea9ead78f6d380d2a667864c132c2f7b83055e4", + "reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4", "shasum": "" }, "require": { @@ -9802,7 +10236,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.0" + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.1" }, "funding": [ { @@ -9810,20 +10245,20 @@ "type": "github" } ], - "time": "2023-02-03T07:07:38+00:00" + "time": "2023-07-19T07:19:23+00:00" }, { "name": "sebastian/lines-of-code", - "version": "2.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130" + "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/17c4d940ecafb3d15d2cf916f4108f664e28b130", - "reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/649e40d279e243d985aa8fb6e74dd5bb28dc185d", + "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d", "shasum": "" }, "require": { @@ -9859,7 +10294,8 @@ "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.0" + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.1" }, "funding": [ { @@ -9867,7 +10303,7 @@ "type": "github" } ], - "time": "2023-02-03T07:08:02+00:00" + "time": "2023-08-31T09:25:50+00:00" }, { "name": "sebastian/object-enumerator", @@ -10155,16 +10591,16 @@ }, { "name": "spatie/backtrace", - "version": "1.4.1", + "version": "1.5.3", "source": { "type": "git", "url": "https://github.com/spatie/backtrace.git", - "reference": "47794d19e3215ace9e005a8f200cd7cc7be52572" + "reference": "483f76a82964a0431aa836b6ed0edde0c248e3ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/backtrace/zipball/47794d19e3215ace9e005a8f200cd7cc7be52572", - "reference": "47794d19e3215ace9e005a8f200cd7cc7be52572", + "url": "https://api.github.com/repos/spatie/backtrace/zipball/483f76a82964a0431aa836b6ed0edde0c248e3ab", + "reference": "483f76a82964a0431aa836b6ed0edde0c248e3ab", "shasum": "" }, "require": { @@ -10201,7 +10637,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/backtrace/tree/1.4.1" + "source": "https://github.com/spatie/backtrace/tree/1.5.3" }, "funding": [ { @@ -10213,43 +10649,44 @@ "type": "other" } ], - "time": "2023-06-13T14:35:04+00:00" + "time": "2023-06-28T12:59:17+00:00" }, { "name": "spatie/flare-client-php", - "version": "1.3.6", + "version": "1.4.3", "source": { "type": "git", "url": "https://github.com/spatie/flare-client-php.git", - "reference": "530ac81255af79f114344286e4275f8869c671e2" + "reference": "5db2fdd743c3ede33f2a5367d89ec1a7c9c1d1ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/530ac81255af79f114344286e4275f8869c671e2", - "reference": "530ac81255af79f114344286e4275f8869c671e2", + "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/5db2fdd743c3ede33f2a5367d89ec1a7c9c1d1ec", + "reference": "5db2fdd743c3ede33f2a5367d89ec1a7c9c1d1ec", "shasum": "" }, "require": { - "illuminate/pipeline": "^8.0|^9.0|^10.0", + "illuminate/pipeline": "^8.0|^9.0|^10.0|^11.0", + "nesbot/carbon": "^2.62.1", "php": "^8.0", - "spatie/backtrace": "^1.2", - "symfony/http-foundation": "^5.0|^6.0", - "symfony/mime": "^5.2|^6.0", - "symfony/process": "^5.2|^6.0", - "symfony/var-dumper": "^5.2|^6.0" + "spatie/backtrace": "^1.5.2", + "symfony/http-foundation": "^5.2|^6.0|^7.0", + "symfony/mime": "^5.2|^6.0|^7.0", + "symfony/process": "^5.2|^6.0|^7.0", + "symfony/var-dumper": "^5.2|^6.0|^7.0" }, "require-dev": { - "dms/phpunit-arraysubset-asserts": "^0.3.0", - "pestphp/pest": "^1.20", + "dms/phpunit-arraysubset-asserts": "^0.5.0", + "pestphp/pest": "^1.20|^2.0", "phpstan/extension-installer": "^1.1", "phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-phpunit": "^1.0", - "spatie/phpunit-snapshot-assertions": "^4.0" + "spatie/phpunit-snapshot-assertions": "^4.0|^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.1.x-dev" + "dev-main": "1.3.x-dev" } }, "autoload": { @@ -10274,7 +10711,7 @@ ], "support": { "issues": "https://github.com/spatie/flare-client-php/issues", - "source": "https://github.com/spatie/flare-client-php/tree/1.3.6" + "source": "https://github.com/spatie/flare-client-php/tree/1.4.3" }, "funding": [ { @@ -10282,41 +10719,41 @@ "type": "github" } ], - "time": "2023-04-12T07:57:12+00:00" + "time": "2023-10-17T15:54:07+00:00" }, { "name": "spatie/ignition", - "version": "1.8.1", + "version": "1.11.3", "source": { "type": "git", "url": "https://github.com/spatie/ignition.git", - "reference": "d8eb8ea1ed27f48a694405cff363746ffd37f13e" + "reference": "3d886de644ff7a5b42e4d27c1e1f67c8b5f00044" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/ignition/zipball/d8eb8ea1ed27f48a694405cff363746ffd37f13e", - "reference": "d8eb8ea1ed27f48a694405cff363746ffd37f13e", + "url": "https://api.github.com/repos/spatie/ignition/zipball/3d886de644ff7a5b42e4d27c1e1f67c8b5f00044", + "reference": "3d886de644ff7a5b42e4d27c1e1f67c8b5f00044", "shasum": "" }, "require": { "ext-json": "*", "ext-mbstring": "*", "php": "^8.0", - "spatie/backtrace": "^1.4", - "spatie/flare-client-php": "^1.1", - "symfony/console": "^5.4|^6.0", - "symfony/var-dumper": "^5.4|^6.0" + "spatie/backtrace": "^1.5.3", + "spatie/flare-client-php": "^1.4.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "require-dev": { - "illuminate/cache": "^9.52", + "illuminate/cache": "^9.52|^10.0|^11.0", "mockery/mockery": "^1.4", - "pestphp/pest": "^1.20", + "pestphp/pest": "^1.20|^2.0", "phpstan/extension-installer": "^1.1", "phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-phpunit": "^1.0", "psr/simple-cache-implementation": "*", - "symfony/cache": "^6.2", - "symfony/process": "^5.4|^6.0", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", "vlucas/phpdotenv": "^5.5" }, "suggest": { @@ -10365,20 +10802,20 @@ "type": "github" } ], - "time": "2023-06-06T14:14:58+00:00" + "time": "2023-10-18T14:09:40+00:00" }, { "name": "spatie/laravel-ignition", - "version": "2.1.3", + "version": "2.3.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-ignition.git", - "reference": "35711943d4725aa80f8033e4f1cb3a6775530b25" + "reference": "bf21cd15aa47fa4ec5d73bbc932005c70261efc8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/35711943d4725aa80f8033e4f1cb3a6775530b25", - "reference": "35711943d4725aa80f8033e4f1cb3a6775530b25", + "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/bf21cd15aa47fa4ec5d73bbc932005c70261efc8", + "reference": "bf21cd15aa47fa4ec5d73bbc932005c70261efc8", "shasum": "" }, "require": { @@ -10388,7 +10825,7 @@ "illuminate/support": "^10.0", "php": "^8.1", "spatie/flare-client-php": "^1.3.5", - "spatie/ignition": "^1.5.0", + "spatie/ignition": "^1.9", "symfony/console": "^6.2.3", "symfony/var-dumper": "^6.2.3" }, @@ -10457,7 +10894,7 @@ "type": "github" } ], - "time": "2023-05-25T11:30:27+00:00" + "time": "2023-10-09T12:55:26+00:00" }, { "name": "theseer/tokenizer", @@ -10519,5 +10956,5 @@ "php": "^8.1" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.2.0" } diff --git a/GaelO2/config/app.php b/GaelO2/config/app.php index 909a9baca..68c239dd5 100644 --- a/GaelO2/config/app.php +++ b/GaelO2/config/app.php @@ -165,9 +165,9 @@ 'orthanc_storage_login' => env('ORTHANC_STORAGE_LOGIN'), 'orthanc_storage_password' => env('ORTHANC_STORAGE_PASSWORD'), 'tus_url' => env('TUS_URL'), - 'gaelo_processing_protocol' => env('GAELO_PROCESSING_PROTOCOL'), - 'gaelo_processing_host' => env('GAELO_PROCESSING_HOST'), - 'gaelo_processing_port' => env('GAELO_PROCESSING_PORT'), + 'gaelo_processing_url' => env('GAELO_PROCESSING_URL'), + 'gaelo_processing_login' => env('GAELO_PROCESSING_LOGIN'), + 'gaelo_processing_password' => env('GAELO_PROCESSING_PASSWORD'), 'azure_directory_id' => env('AZURE_DIRECTORY_ID'), 'azure_client_id' => env('AZURE_CLIENT_ID'), 'azure_client_secret' => env('AZURE_CLIENT_SECRET'), diff --git a/GaelO2/config/dompdf.php b/GaelO2/config/dompdf.php new file mode 100644 index 000000000..8ad202243 --- /dev/null +++ b/GaelO2/config/dompdf.php @@ -0,0 +1,284 @@ + false, // Throw an Exception on warnings from dompdf + + 'public_path' => null, // Override the public path if needed + + /* + * Dejavu Sans font is missing glyphs for converted entities, turn it off if you need to show € and £. + */ + 'convert_entities' => true, + + 'options' => array( + /** + * The location of the DOMPDF font directory + * + * The location of the directory where DOMPDF will store fonts and font metrics + * Note: This directory must exist and be writable by the webserver process. + * *Please note the trailing slash.* + * + * Notes regarding fonts: + * Additional .afm font metrics can be added by executing load_font.php from command line. + * + * Only the original "Base 14 fonts" are present on all pdf viewers. Additional fonts must + * be embedded in the pdf file or the PDF may not display correctly. This can significantly + * increase file size unless font subsetting is enabled. Before embedding a font please + * review your rights under the font license. + * + * Any font specification in the source HTML is translated to the closest font available + * in the font directory. + * + * The pdf standard "Base 14 fonts" are: + * Courier, Courier-Bold, Courier-BoldOblique, Courier-Oblique, + * Helvetica, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique, + * Times-Roman, Times-Bold, Times-BoldItalic, Times-Italic, + * Symbol, ZapfDingbats. + */ + "font_dir" => storage_path('fonts'), // advised by dompdf (https://github.com/dompdf/dompdf/pull/782) + + /** + * The location of the DOMPDF font cache directory + * + * This directory contains the cached font metrics for the fonts used by DOMPDF. + * This directory can be the same as DOMPDF_FONT_DIR + * + * Note: This directory must exist and be writable by the webserver process. + */ + "font_cache" => storage_path('fonts'), + + /** + * The location of a temporary directory. + * + * The directory specified must be writeable by the webserver process. + * The temporary directory is required to download remote images and when + * using the PFDLib back end. + */ + "temp_dir" => sys_get_temp_dir(), + + /** + * ==== IMPORTANT ==== + * + * dompdf's "chroot": Prevents dompdf from accessing system files or other + * files on the webserver. All local files opened by dompdf must be in a + * subdirectory of this directory. DO NOT set it to '/' since this could + * allow an attacker to use dompdf to read any files on the server. This + * should be an absolute path. + * This is only checked on command line call by dompdf.php, but not by + * direct class use like: + * $dompdf = new DOMPDF(); $dompdf->load_html($htmldata); $dompdf->render(); $pdfdata = $dompdf->output(); + */ + "chroot" => realpath(base_path()), + + /** + * Protocol whitelist + * + * Protocols and PHP wrappers allowed in URIs, and the validation rules + * that determine if a resouce may be loaded. Full support is not guaranteed + * for the protocols/wrappers specified + * by this array. + * + * @var array + */ + 'allowed_protocols' => [ + "file://" => ["rules" => []], + "http://" => ["rules" => []], + "https://" => ["rules" => []] + ], + + /** + * @var string + */ + 'log_output_file' => null, + + /** + * Whether to enable font subsetting or not. + */ + "enable_font_subsetting" => false, + + /** + * The PDF rendering backend to use + * + * Valid settings are 'PDFLib', 'CPDF' (the bundled R&OS PDF class), 'GD' and + * 'auto'. 'auto' will look for PDFLib and use it if found, or if not it will + * fall back on CPDF. 'GD' renders PDFs to graphic files. {@link + * Canvas_Factory} ultimately determines which rendering class to instantiate + * based on this setting. + * + * Both PDFLib & CPDF rendering backends provide sufficient rendering + * capabilities for dompdf, however additional features (e.g. object, + * image and font support, etc.) differ between backends. Please see + * {@link PDFLib_Adapter} for more information on the PDFLib backend + * and {@link CPDF_Adapter} and lib/class.pdf.php for more information + * on CPDF. Also see the documentation for each backend at the links + * below. + * + * The GD rendering backend is a little different than PDFLib and + * CPDF. Several features of CPDF and PDFLib are not supported or do + * not make any sense when creating image files. For example, + * multiple pages are not supported, nor are PDF 'objects'. Have a + * look at {@link GD_Adapter} for more information. GD support is + * experimental, so use it at your own risk. + * + * @link http://www.pdflib.com + * @link http://www.ros.co.nz/pdf + * @link http://www.php.net/image + */ + "pdf_backend" => "CPDF", + + /** + * PDFlib license key + * + * If you are using a licensed, commercial version of PDFlib, specify + * your license key here. If you are using PDFlib-Lite or are evaluating + * the commercial version of PDFlib, comment out this setting. + * + * @link http://www.pdflib.com + * + * If pdflib present in web server and auto or selected explicitely above, + * a real license code must exist! + */ + //"DOMPDF_PDFLIB_LICENSE" => "your license key here", + + /** + * html target media view which should be rendered into pdf. + * List of types and parsing rules for future extensions: + * http://www.w3.org/TR/REC-html40/types.html + * screen, tty, tv, projection, handheld, print, braille, aural, all + * Note: aural is deprecated in CSS 2.1 because it is replaced by speech in CSS 3. + * Note, even though the generated pdf file is intended for print output, + * the desired content might be different (e.g. screen or projection view of html file). + * Therefore allow specification of content here. + */ + "default_media_type" => "screen", + + /** + * The default paper size. + * + * North America standard is "letter"; other countries generally "a4" + * + * @see CPDF_Adapter::PAPER_SIZES for valid sizes ('letter', 'legal', 'A4', etc.) + */ + "default_paper_size" => "a4", + + /** + * The default paper orientation. + * + * The orientation of the page (portrait or landscape). + * + * @var string + */ + 'default_paper_orientation' => "portrait", + + /** + * The default font family + * + * Used if no suitable fonts can be found. This must exist in the font folder. + * @var string + */ + "default_font" => "serif", + + /** + * Image DPI setting + * + * This setting determines the default DPI setting for images and fonts. The + * DPI may be overridden for inline images by explictly setting the + * image's width & height style attributes (i.e. if the image's native + * width is 600 pixels and you specify the image's width as 72 points, + * the image will have a DPI of 600 in the rendered PDF. The DPI of + * background images can not be overridden and is controlled entirely + * via this parameter. + * + * For the purposes of DOMPDF, pixels per inch (PPI) = dots per inch (DPI). + * If a size in html is given as px (or without unit as image size), + * this tells the corresponding size in pt. + * This adjusts the relative sizes to be similar to the rendering of the + * html page in a reference browser. + * + * In pdf, always 1 pt = 1/72 inch + * + * Rendering resolution of various browsers in px per inch: + * Windows Firefox and Internet Explorer: + * SystemControl->Display properties->FontResolution: Default:96, largefonts:120, custom:? + * Linux Firefox: + * about:config *resolution: Default:96 + * (xorg screen dimension in mm and Desktop font dpi settings are ignored) + * + * Take care about extra font/image zoom factor of browser. + * + * In images, size in pixel attribute, img css style, are overriding + * the real image dimension in px for rendering. + * + * @var int + */ + "dpi" => 96, + + /** + * Enable inline PHP + * + * If this setting is set to true then DOMPDF will automatically evaluate + * inline PHP contained within tags. + * + * Enabling this for documents you do not trust (e.g. arbitrary remote html + * pages) is a security risk. Set this option to false if you wish to process + * untrusted documents. + * + * @var bool + */ + "enable_php" => false, + + /** + * Enable inline Javascript + * + * If this setting is set to true then DOMPDF will automatically insert + * JavaScript code contained within tags. + * + * @var bool + */ + "enable_javascript" => true, + + /** + * Enable remote file access + * + * If this setting is set to true, DOMPDF will access remote sites for + * images and CSS files as required. + * This is required for part of test case www/test/image_variants.html through www/examples.php + * + * Attention! + * This can be a security risk, in particular in combination with DOMPDF_ENABLE_PHP and + * allowing remote access to dompdf.php or on allowing remote html code to be passed to + * $dompdf = new DOMPDF(, $dompdf->load_html(..., + * This allows anonymous users to download legally doubtful internet content which on + * tracing back appears to being downloaded by your server, or allows malicious php code + * in remote html pages to be executed by your server with your account privileges. + * + * @var bool + */ + "enable_remote" => true, + + /** + * A ratio applied to the fonts height to be more like browsers' line height + */ + "font_height_ratio" => 1.1, + + /** + * Use the HTML5 Lib parser + * + * @deprecated This feature is now always on in dompdf 2.x + * @var bool + */ + "enable_html5_parser" => true, + ), + + +); diff --git a/GaelO2/config/queue.php b/GaelO2/config/queue.php index 178d302bd..2315c9775 100644 --- a/GaelO2/config/queue.php +++ b/GaelO2/config/queue.php @@ -38,14 +38,14 @@ 'driver' => 'database', 'table' => 'jobs', 'queue' => 'default', - 'retry_after' => 90, + 'retry_after' => 1800, ], 'beanstalkd' => [ 'driver' => 'beanstalkd', 'host' => 'localhost', 'queue' => 'default', - 'retry_after' => 90, + 'retry_after' => 1800, 'block_for' => 0, ], @@ -63,7 +63,7 @@ 'driver' => 'redis', 'connection' => 'default', 'queue' => env('REDIS_QUEUE', 'default'), - 'retry_after' => 600, + 'retry_after' => 1800, 'block_for' => null, ], diff --git a/GaelO2/database/factories/StudyFactory.php b/GaelO2/database/factories/StudyFactory.php index 20d85149c..4933881a7 100644 --- a/GaelO2/database/factories/StudyFactory.php +++ b/GaelO2/database/factories/StudyFactory.php @@ -17,7 +17,8 @@ public function definition() 'controller_show_all' => false, 'monitor_show_all' => false, 'documentation_mandatory' => false, - 'ancillary_of' => null + 'ancillary_of' => null, + 'creatable_patients_investigator' => false, ]; } @@ -75,14 +76,21 @@ public function ancillaryOf(string $studyName) }); } - public function documentationMandatory() + public function documentationMandatory() { return $this->state(function (array $attributes) { return [ 'documentation_mandatory' => true, ]; }); + } - + public function creatablePatientsInvestigator() + { + return $this->state(function (array $attributes) { + return [ + 'creatable_patients_investigator' => true, + ]; + }); } } diff --git a/GaelO2/database/factories/VisitFactory.php b/GaelO2/database/factories/VisitFactory.php index 5b0d39598..3bfb136ea 100644 --- a/GaelO2/database/factories/VisitFactory.php +++ b/GaelO2/database/factories/VisitFactory.php @@ -46,7 +46,8 @@ public function definition() 'corrective_action_new_upload' => $this->faker->randomElement([true, false]), 'corrective_action_investigator_form' => $this->faker->randomElement([true, false]), 'corrective_action_comment' => $this->faker->word, - 'corrective_action_applied' => $this->faker->randomElement([true, false]) + 'corrective_action_applied' => $this->faker->randomElement([true, false]), + 'sent_files' => [] ]; } @@ -122,6 +123,15 @@ public function uploadDone() }); } + public function sentFiles(array $sentFiles) + { + return $this->state(function (array $attributes) use ($sentFiles) { + return [ + 'sent_files' => $sentFiles, + ]; + }); + } + public function configure() { return $this->afterMaking(function (Visit $visit) { diff --git a/GaelO2/database/migrations/2023_09_19_193551_add_file_visit.php b/GaelO2/database/migrations/2023_09_19_193551_add_file_visit.php new file mode 100644 index 000000000..337315e01 --- /dev/null +++ b/GaelO2/database/migrations/2023_09_19_193551_add_file_visit.php @@ -0,0 +1,35 @@ +json('sent_files')->nullable(true); + }); + + DB::table('visits')->whereNull('sent_files')->update(['sent_files'=>'{}']); + + Schema::table('visits', function (Blueprint $table) { + $table->json('sent_files')->nullable(false)->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('visits', function (Blueprint $table) { + $table->dropColumn('sent_files'); + }); + } +}; diff --git a/GaelO2/database/migrations/2023_10_16_221003_creatable_patient.php b/GaelO2/database/migrations/2023_10_16_221003_creatable_patient.php new file mode 100644 index 000000000..cf369cfde --- /dev/null +++ b/GaelO2/database/migrations/2023_10_16_221003_creatable_patient.php @@ -0,0 +1,28 @@ +boolean('creatable_patients_investigator')->default(false); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('studies', function (Blueprint $table) { + $table->dropColumn('creatable_patients_investigator'); + }); + } +}; diff --git a/GaelO2/routes/api.php b/GaelO2/routes/api.php index 1103910a1..b57ed25df 100644 --- a/GaelO2/routes/api.php +++ b/GaelO2/routes/api.php @@ -92,6 +92,7 @@ Route::get('studies/{studyName}/dicom-studies', [StudyController::class, 'getDicomStudiesFromStudy']); Route::post('studies/{studyName}/send-reminder', [StudyController::class, 'sendReminder']); Route::post('studies/{studyName}/ask-patient-creation', [StudyController::class, 'requestPatientCreation']); + Route::get('studies/{studyName}/creatable-patients', [StudyController::class, 'getCreatablePatients']); Route::post('send-mail', [StudyController::class, 'sendMail']); //Centers Routes @@ -112,6 +113,7 @@ //VisitType Routes Route::post('visit-groups/{visitGroupId}/visit-types', [VisitTypeController::class, 'createVisitType']); Route::get('visit-types/{visitTypeId}', [VisitTypeController::class, 'getVisitType']); + Route::get('visit-types/{visitTypeId}/files/metadata', [VisitTypeController::class, 'getFileMetadataFromVisitType']); Route::delete('visit-types/{visitTypeId}', [VisitTypeController::class, 'deleteVisitType']); //Patients Routes @@ -133,6 +135,9 @@ Route::post('visits/{id}/activate', [VisitController::class, 'reactivateVisit']); Route::post('visit-types/{visitTypeId}/visits', [VisitController::class, 'createVisit']); Route::get('visits/{id}', [VisitController::class, 'getVisit']); + Route::get('visits/{id}/files/{key}', [VisitController::class, 'getFileOfVisit']); + Route::post('visits/{id}/files/{key}', [VisitController::class, 'createFileOfVisit']); + Route::delete('visits/{id}/files/{key}', [VisitController::class, 'deleteFileOfVisit']); //Local Form Routes Route::get('visits/{id}/investigator-form', [ReviewController::class, 'getInvestigatorForm']); @@ -148,8 +153,8 @@ Route::get('reviews/{id}', [ReviewController::class, 'getReviewForm']); Route::delete('reviews/{id}', [ReviewController::class, 'deleteReviewForm']); Route::patch('reviews/{id}/unlock', [ReviewController::class, 'unlockReviewForm']); - Route::post('reviews/{id}/file/{key}', [ReviewController::class, 'createReviewFile']); - Route::delete('reviews/{id}/file/{key}', [ReviewController::class, 'deleteReviewFile']); + Route::post('reviews/{id}/files/{key}', [ReviewController::class, 'createReviewFile']); + Route::delete('reviews/{id}/files/{key}', [ReviewController::class, 'deleteReviewFile']); Route::get('visits/{visitId}/reviews', [ReviewController::class, 'getReviewsFromVisit']); Route::get('studies/{studyName}/visits/{visitId}/reviewer-associated-data', [ReviewController::class, 'getAssociatedDataOfVisitForReviewer']); @@ -194,7 +199,7 @@ Route::get('visits/{id}/dicoms/file', [DicomController::class, 'getVisitDicomsFile']); Route::get('studies/{studyName}/export', [StudyController::class, 'exportStudyData']); Route::post('studies/{studyName}/dicom-series/file', [DicomController::class, 'getSupervisorDicomsFile']); - Route::get('reviews/{id}/file/{key}', [ReviewController::class, 'getReviewFile']); + Route::get('reviews/{id}/files/{key}', [ReviewController::class, 'getReviewFile']); Route::get('dicom-series/{seriesInstanceUID}/nifti', [DicomController::class, 'getNiftiSeries']); }); diff --git a/GaelO2/storage/fonts/.gitkeep b/GaelO2/storage/fonts/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/GaelO2/tests/Feature/TestPatients/ImportPatientTest.php b/GaelO2/tests/Feature/TestPatients/ImportPatientTest.php index a473e8bdf..9810ef13f 100644 --- a/GaelO2/tests/Feature/TestPatients/ImportPatientTest.php +++ b/GaelO2/tests/Feature/TestPatients/ImportPatientTest.php @@ -14,199 +14,239 @@ class ImportPatientTest extends TestCase { use RefreshDatabase; - protected function setUp() : void{ + private Study $study; + private array $validPayload; + + protected function setUp(): void + { parent::setUp(); $this->artisan('db:seed'); $this->study = Study::factory()->patientCodeLength(14)->code('123')->create(); - $this->validPayload = [ ["code" => '12341231234123', - "lastname" => "test", - "firstname" => "test", - "gender" => "M", - "birthDay" => 1, - "birthMonth" => 1, - "birthYear" => 1998, - "registrationDate" => '2011-10-05', - "investigatorName" => "administrator", - "centerCode" => 0, - "inclusionStatus" => InclusionStatusEnum::INCLUDED->value + $this->validPayload = [[ + "code" => '12341231234123', + "lastname" => "test", + "firstname" => "test", + "gender" => "M", + "birthDay" => 1, + "birthMonth" => 1, + "birthYear" => 1998, + "registrationDate" => '2011-10-05', + "investigatorName" => "administrator", + "centerCode" => 0, + "inclusionStatus" => InclusionStatusEnum::INCLUDED->value ]]; - } - public function testImportMultiplePatients() { + public function testImportMultiplePatients() + { $currentUserId = AuthorizationTools::actAsAdmin(false); AuthorizationTools::addRoleToUser($currentUserId, Constants::ROLE_SUPERVISOR, $this->study->name); - $this->validPayload = [ ["code" => '12341231234123', - "lastname" => "test", - "firstname" => "test", - "gender" => "M", - "birthDay" => 1, - "birthMonth" => 1, - "birthYear" => 1998, - "registrationDate" => '2011-10-05', - "investigatorName" => "administrator", - "centerCode" => 0, - "inclusionStatus" => InclusionStatusEnum::INCLUDED->value], - ["code" => '12341231234124', - "lastname" => "test", - "firstname" => "test", - "gender" => "M", - "birthDay" => 1, - "birthMonth" => 1, - "birthYear" => 1998, - "registrationDate" => '2011-10-06', - "investigatorName" => "administrator", - "centerCode" => 0, - "inclusionStatus" => InclusionStatusEnum::INCLUDED->value], - ["code" => '12341231234125', - "lastname" => "test", - "firstname" => "test", - "gender" => "M", - "birthDay" => 1, - "birthMonth" => 1, - "birthYear" => 1998, - "registrationDate" => '2011-10-07', - "investigatorName" => "administrator", - "centerCode" => 0, - "inclusionStatus" => InclusionStatusEnum::INCLUDED->value] - ]; - $resp = $this->json('POST', '/api/studies/'.$this->study->name.'/import-patients', $this->validPayload); + $this->validPayload = [ + [ + "code" => '12341231234123', + "lastname" => "test", + "firstname" => "test", + "gender" => "M", + "birthDay" => 1, + "birthMonth" => 1, + "birthYear" => 1998, + "registrationDate" => '2011-10-05', + "investigatorName" => "administrator", + "centerCode" => 0, + "inclusionStatus" => InclusionStatusEnum::INCLUDED->value + ], + [ + "code" => '12341231234124', + "lastname" => "test", + "firstname" => "test", + "gender" => "M", + "birthDay" => 1, + "birthMonth" => 1, + "birthYear" => 1998, + "registrationDate" => '2011-10-06', + "investigatorName" => "administrator", + "centerCode" => 0, + "inclusionStatus" => InclusionStatusEnum::INCLUDED->value + ], + [ + "code" => '12341231234125', + "lastname" => "test", + "firstname" => "test", + "gender" => "M", + "birthDay" => 1, + "birthMonth" => 1, + "birthYear" => 1998, + "registrationDate" => '2011-10-07', + "investigatorName" => "administrator", + "centerCode" => 0, + "inclusionStatus" => InclusionStatusEnum::INCLUDED->value + ] + ]; + $resp = $this->json('POST', '/api/studies/' . $this->study->name . '/import-patients?role=Supervisor', $this->validPayload); $resp->assertSuccessful(); - $this->assertEquals(3,sizeof($resp['success'])); - $this->assertEquals(0,sizeof($resp['fail'])); + $this->assertEquals(3, sizeof($resp['success'])); + $this->assertEquals(0, sizeof($resp['fail'])); - $patient1 = Patient::find($this->study->code.'12341231234123')->toArray(); + $patient1 = Patient::find($this->study->code . '12341231234123')->toArray(); $this->assertEquals('2011-10-05T00:00:00.000000Z', $patient1['registration_date']); } - public function testImportPatient() { + public function testImportPatient() + { $currentUserId = AuthorizationTools::actAsAdmin(false); AuthorizationTools::addRoleToUser($currentUserId, Constants::ROLE_SUPERVISOR, $this->study->name); //Test patient creation - $reponse1 = $this->json('POST', '/api/studies/'.$this->study->name.'/import-patients', $this->validPayload)->assertSuccessful(); - $this->assertEquals(1,sizeof($reponse1['success'])); - $this->assertEquals(0,sizeof($reponse1['fail'])); + $reponse1 = $this->json('POST', '/api/studies/' . $this->study->name . '/import-patients?role=Supervisor', $this->validPayload)->assertSuccessful(); + $this->assertEquals(1, sizeof($reponse1['success'])); + $this->assertEquals(0, sizeof($reponse1['fail'])); //Test that copies of existing patients don't insert - $response2 = $this->json('POST', '/api/studies/'.$this->study->name.'/import-patients', $this->validPayload); - $this->assertEquals(0,sizeof($response2['success'])); - $this->assertEquals(1,sizeof($response2['fail'])); + $response2 = $this->json('POST', '/api/studies/' . $this->study->name . '/import-patients?role=Supervisor', $this->validPayload); + $this->assertEquals(0, sizeof($response2['success'])); + $this->assertEquals(1, sizeof($response2['fail'])); + } + + public function testImportPatientInvestigator() + { + $this->study->creatable_patients_investigator = true; + $this->study->save(); + $currentUserId = AuthorizationTools::actAsAdmin(false); + AuthorizationTools::addRoleToUser($currentUserId, Constants::ROLE_INVESTIGATOR, $this->study->name); + $reponse1 = $this->json('POST', '/api/studies/' . $this->study->name . '/import-patients?role=Investigator', $this->validPayload)->assertSuccessful(); + $this->assertEquals(1, sizeof($reponse1['success'])); + $this->assertEquals(0, sizeof($reponse1['fail'])); + } - public function testImportPatientForbiddenNoRole(){ + public function testImportPatientInvestigatorForbidden() + { + $currentUserId = AuthorizationTools::actAsAdmin(false); + AuthorizationTools::addRoleToUser($currentUserId, Constants::ROLE_INVESTIGATOR, $this->study->name); + $this->json('POST', '/api/studies/' . $this->study->name . '/import-patients?role=Investigator', $this->validPayload)->assertForbidden(); + } + + public function testImportPatientForbiddenNoRole() + { AuthorizationTools::actAsAdmin(false); - $this->json('POST', '/api/studies/'.$this->study->name.'/import-patients', $this->validPayload)->assertStatus(403); + $this->json('POST', '/api/studies/' . $this->study->name . '/import-patients?role=Supervisor', $this->validPayload)->assertStatus(403); } - public function testImportPatientForbiddenForAncillaryStudy(){ + public function testImportPatientForbiddenForAncillaryStudy() + { $currentUserId = AuthorizationTools::actAsAdmin(false); $ancillaryStudies = Study::factory()->ancillaryOf($this->study->name)->create(); AuthorizationTools::addRoleToUser($currentUserId, Constants::ROLE_SUPERVISOR, $ancillaryStudies->name); - $answer = $this->json('POST', '/api/studies/'.$ancillaryStudies->name.'/import-patients', $this->validPayload); + $answer = $this->json('POST', '/api/studies/' . $ancillaryStudies->name . '/import-patients?role=Supervisor', $this->validPayload); $answer->assertStatus(403); } - public function testCreateWrongDayOfBirth() { + public function testCreateWrongDayOfBirth() + { $currentUserId = AuthorizationTools::actAsAdmin(false); AuthorizationTools::addRoleToUser($currentUserId, Constants::ROLE_SUPERVISOR, $this->study->name); $this->validPayload[0]['birthDay'] = 0; - $resp = $this->json('POST', '/api/studies/'.$this->study->name.'/import-patients', $this->validPayload); + $resp = $this->json('POST', '/api/studies/' . $this->study->name . '/import-patients?role=Supervisor', $this->validPayload); //Check that inserting patient failed because day of birth was incorrect $this->assertEquals(0, count($resp['success'])); $this->assertNotEmpty($resp['fail']['Incorrect Birthdate day format']); $this->assertEquals(12341231234123, $resp['fail']['Incorrect Birthdate day format'][0]); $this->validPayload[0]['birthDay'] = 32; - $resp = $this->json('POST', '/api/studies/'.$this->study->name.'/import-patients', $this->validPayload); + $resp = $this->json('POST', '/api/studies/' . $this->study->name . '/import-patients?role=Supervisor', $this->validPayload); $this->assertEquals(0, count($resp['success'])); $this->assertNotEmpty($resp['fail']['Incorrect Birthdate day format']); $this->assertEquals(12341231234123, $resp['fail']['Incorrect Birthdate day format'][0]); } - public function testCreateWrongMonthOfBirth() { + public function testCreateWrongMonthOfBirth() + { $currentUserId = AuthorizationTools::actAsAdmin(false); AuthorizationTools::addRoleToUser($currentUserId, Constants::ROLE_SUPERVISOR, $this->study->name); $this->validPayload[0]['birthMonth'] = 0; - $resp = $this->json('POST', '/api/studies/'.$this->study->name.'/import-patients', $this->validPayload); + $resp = $this->json('POST', '/api/studies/' . $this->study->name . '/import-patients?role=Supervisor', $this->validPayload); //Check that inserting patient failed because day of birth was incorrect $this->assertEquals(0, count($resp['success'])); $this->assertNotEmpty($resp['fail']['Incorrect Birthdate month format']); $this->assertEquals(12341231234123, $resp['fail']['Incorrect Birthdate month format'][0]); $this->validPayload[0]['birthMonth'] = 13; - $resp = $this->json('POST', '/api/studies/'.$this->study->name.'/import-patients', $this->validPayload); + $resp = $this->json('POST', '/api/studies/' . $this->study->name . '/import-patients?role=Supervisor', $this->validPayload); $this->assertEquals(0, count($resp['success'])); $this->assertNotEmpty($resp['fail']['Incorrect Birthdate month format']); $this->assertEquals(12341231234123, $resp['fail']['Incorrect Birthdate month format'][0]); } - public function testCreateWrongYearOfBirth() { + public function testCreateWrongYearOfBirth() + { $currentUserId = AuthorizationTools::actAsAdmin(false); AuthorizationTools::addRoleToUser($currentUserId, Constants::ROLE_SUPERVISOR, $this->study->name); $this->validPayload[0]['birthYear'] = 1800; - $resp = $this->json('POST', '/api/studies/'.$this->study->name.'/import-patients', $this->validPayload); + $resp = $this->json('POST', '/api/studies/' . $this->study->name . '/import-patients?role=Supervisor', $this->validPayload); //Check that inserting patient failed because day of birth was incorrect $this->assertEquals(0, count($resp['success'])); $this->assertNotEmpty($resp['fail']['Incorrect Birthdate year format']); $this->assertEquals(12341231234123, $resp['fail']['Incorrect Birthdate year format'][0]); $this->validPayload[0]['birthYear'] = 3010; - $resp = $this->json('POST', '/api/studies/'.$this->study->name.'/import-patients', $this->validPayload); + $resp = $this->json('POST', '/api/studies/' . $this->study->name . '/import-patients?role=Supervisor', $this->validPayload); $this->assertEquals(0, count($resp['success'])); $this->assertNotEmpty($resp['fail']['Incorrect Birthdate year format']); $this->assertEquals(12341231234123, $resp['fail']['Incorrect Birthdate year format'][0]); } - public function testCreateAlreadyKnownPatient(){ + public function testCreateAlreadyKnownPatient() + { $currentUserId = AuthorizationTools::actAsAdmin(false); AuthorizationTools::addRoleToUser($currentUserId, Constants::ROLE_SUPERVISOR, $this->study->name); - $this->json('POST', '/api/studies/'.$this->study->name.'/import-patients', $this->validPayload); - $resp = $this->json('POST', '/api/studies/'.$this->study->name.'/import-patients', $this->validPayload); + $this->json('POST', '/api/studies/' . $this->study->name . '/import-patients?role=Supervisor', $this->validPayload); + $resp = $this->json('POST', '/api/studies/' . $this->study->name . '/import-patients?role=Supervisor', $this->validPayload); $this->assertEquals(0, count($resp['success'])); $this->assertNotEmpty($resp['fail']['Existing Patient Code']); $this->assertEquals(12341231234123, $resp['fail']['Existing Patient Code'][0]); } - public function testIncorrectPatientCodeLength(){ + public function testIncorrectPatientCodeLength() + { $currentUserId = AuthorizationTools::actAsAdmin(false); AuthorizationTools::addRoleToUser($currentUserId, Constants::ROLE_SUPERVISOR, $this->study->name); $this->validPayload[0]['code'] = '123'; - $resp = $this->json('POST', '/api/studies/'.$this->study->name.'/import-patients', $this->validPayload); + $resp = $this->json('POST', '/api/studies/' . $this->study->name . '/import-patients?role=Supervisor', $this->validPayload); $this->assertEquals(0, count($resp['success'])); $this->assertNotEmpty($resp['fail']['Incorrect Patient Code Length']); $this->assertEquals(123, $resp['fail']['Incorrect Patient Code Length'][0]); } - public function testMissingInclusionDateWhileIncluded(){ + public function testMissingInclusionDateWhileIncluded() + { $currentUserId = AuthorizationTools::actAsAdmin(false); AuthorizationTools::addRoleToUser($currentUserId, Constants::ROLE_SUPERVISOR, $this->study->name); $this->validPayload[0]['registrationDate'] = '123'; - $resp = $this->json('POST', '/api/studies/'.$this->study->name.'/import-patients', $this->validPayload); + $resp = $this->json('POST', '/api/studies/' . $this->study->name . '/import-patients?role=Supervisor', $this->validPayload); $this->assertEquals(0, count($resp['success'])); $this->assertNotEmpty($resp['fail']['Registration Date Missing or Invalid']); $this->assertEquals(12341231234123, $resp['fail']['Registration Date Missing or Invalid'][0]); } - public function testMissingInclusionDateAllowedIfPreIncluded(){ + public function testMissingInclusionDateAllowedIfPreIncluded() + { $currentUserId = AuthorizationTools::actAsAdmin(false); AuthorizationTools::addRoleToUser($currentUserId, Constants::ROLE_SUPERVISOR, $this->study->name); $this->validPayload[0]['registrationDate'] = null; $this->validPayload[0]['inclusionStatus'] = InclusionStatusEnum::PRE_INCLUDED->value; - $resp = $this->json('POST', '/api/studies/'.$this->study->name.'/import-patients', $this->validPayload); + $resp = $this->json('POST', '/api/studies/' . $this->study->name . '/import-patients?role=Supervisor', $this->validPayload); $this->assertEquals(1, count($resp['success'])); } - } diff --git a/GaelO2/tests/Feature/TestReviewForm/GetFileFormTest.php b/GaelO2/tests/Feature/TestReviewForm/GetFileFormTest.php index b022972f9..cae5c7e08 100644 --- a/GaelO2/tests/Feature/TestReviewForm/GetFileFormTest.php +++ b/GaelO2/tests/Feature/TestReviewForm/GetFileFormTest.php @@ -51,7 +51,7 @@ public function testGetFileOfForm(){ $review->sent_files = ['41' => $currentVisit['studyName'].'/'.'attached_review_file'.'/'.'review_1_41.csv']; $review->save(); AuthorizationTools::addRoleToUser($currentUserId, Constants::ROLE_SUPERVISOR, $currentVisit['studyName'] ); - $response = $this->get('api/reviews/' . $review->id . '/file/41?role=Supervisor'); + $response = $this->get('api/reviews/' . $review->id . '/files/41?role=Supervisor'); $response->assertSuccessful(); } @@ -63,7 +63,7 @@ public function testGetFileOfFormShouldFailNoRole(){ $review = Review::factory()->userId($currentUserId)->visitId($currentVisit['visitId'])->studyName($currentVisit['studyName'])->create(); $review->sent_files = ['41' => $currentVisit['studyName'].'/'.'attached_review_file'.'/'.'review_1_41.csv']; $review->save(); - $response = $this->get('api/reviews/' . $review->id . '/file/41?role=Supervisor'); + $response = $this->get('api/reviews/' . $review->id . '/files/41?role=Supervisor'); $response->assertStatus(403); } @@ -75,7 +75,7 @@ public function testDeleteFileOfForm(){ $review = Review::factory()->userId($currentUserId)->visitId($currentVisit['visitId'])->studyName($currentVisit['studyName'])->create(); $review->sent_files = ['41' => $currentVisit['studyName'].'/'.'attached_review_file'.'/'.'review_1_41.csv']; $review->save(); - $response = $this->delete('api/reviews/' . $review->id . '/file/41'); + $response = $this->delete('api/reviews/' . $review->id . '/files/41'); $response->assertSuccessful(); } @@ -85,7 +85,7 @@ public function testDeleteFileOfFormShouldFailNoRole(){ $review = Review::factory()->userId($currentUserId)->visitId($currentVisit['visitId'])->studyName($currentVisit['studyName'])->create(); $review->sent_files = ['41' => $currentVisit['studyName'].'/'.'attached_review_file'.'/'.'review_1_41.csv']; $review->save(); - $response = $this->delete('api/reviews/' . $review->id . '/file/41'); + $response = $this->delete('api/reviews/' . $review->id . '/files/41'); $response->assertStatus(403); } diff --git a/GaelO2/tests/Feature/TestReviewForm/UploadFileFormTest.php b/GaelO2/tests/Feature/TestReviewForm/UploadFileFormTest.php index cf2b4d670..996d049ec 100644 --- a/GaelO2/tests/Feature/TestReviewForm/UploadFileFormTest.php +++ b/GaelO2/tests/Feature/TestReviewForm/UploadFileFormTest.php @@ -1,6 +1,5 @@ userId($currentUserId)->visitId($currentVisit['visitId'])->studyName($currentVisit['studyName'])->create(); AuthorizationTools::addRoleToUser($currentUserId, Constants::ROLE_INVESTIGATOR, $currentVisit['studyName'] ); AuthorizationTools::addAffiliatedCenter($currentUserId, $currentVisit['centerCode']); - $response = $this->post('api/reviews/' . $review->id . '/file/41', [base64_encode("testFileContent")], ['CONTENT_TYPE' => 'text/csv']); + $response = $this->post('api/reviews/' . $review->id . '/files/41', [base64_encode("testFileContent")], ['CONTENT_TYPE' => 'text/csv']); $response->assertSuccessful(); } @@ -55,7 +54,7 @@ public function testUploadFileShouldFailNoRole() $currentVisit = $this->createVisit(); $currentUserId = AuthorizationTools::actAsAdmin(false); $review = Review::factory()->userId($currentUserId)->visitId($currentVisit['visitId'])->studyName($currentVisit['studyName'])->create(); - $response = $this->post('api/reviews/' . $review->id . '/file/41', [base64_encode("testFileContent")], ['CONTENT_TYPE' => 'text/csv']); + $response = $this->post('api/reviews/' . $review->id . '/files/41', [base64_encode("testFileContent")], ['CONTENT_TYPE' => 'text/csv']); $response->assertStatus(403); } @@ -67,7 +66,7 @@ public function testUploadFileShouldFailWrongMime() AuthorizationTools::addAffiliatedCenter($currentUserId, $currentVisit['centerCode']); $review = Review::factory()->userId($currentUserId)->visitId($currentVisit['visitId'])->studyName($currentVisit['studyName'])->create(); AuthorizationTools::addRoleToUser($currentUserId, Constants::ROLE_REVIEWER, $currentVisit['studyName'] ); - $response = $this->post('api/reviews/' . $review->id . '/file/41', [base64_encode("testFileContent")], ['CONTENT_TYPE' => 'image/png']); + $response = $this->post('api/reviews/' . $review->id . '/files/41', [base64_encode("testFileContent")], ['CONTENT_TYPE' => 'image/png']); $response->assertStatus(400); } diff --git a/GaelO2/tests/Feature/TestStudy/CreateStudyTest.php b/GaelO2/tests/Feature/TestStudy/CreateStudyTest.php index b8be40f67..9435cb022 100644 --- a/GaelO2/tests/Feature/TestStudy/CreateStudyTest.php +++ b/GaelO2/tests/Feature/TestStudy/CreateStudyTest.php @@ -23,7 +23,8 @@ protected function setUp(): void 'controllerShowAll' => true, 'monitorShowAll' => false, 'documentationMandatory' => false, - 'contactEmail' => 'test@gaelo.fr' + 'contactEmail' => 'test@gaelo.fr', + 'creatablePatientsInvestigator' => false ]; } diff --git a/GaelO2/tests/Feature/TestStudy/GetCreatablePatientsTest.php b/GaelO2/tests/Feature/TestStudy/GetCreatablePatientsTest.php new file mode 100644 index 000000000..d7434e7f8 --- /dev/null +++ b/GaelO2/tests/Feature/TestStudy/GetCreatablePatientsTest.php @@ -0,0 +1,48 @@ +artisan('db:seed'); + Study::factory()->name('TEST')->create(); + + $mockTestStudy = $this->partialMock(TEST::class, function (MockInterface $mock) { + $mock->shouldReceive('getExpectedPatients') + ->andReturn([new ExpectedPatient('1234', 0, 'Included')]); + }); + + $mockFramework = $this->partialMock(FrameworkAdapter::class, function (MockInterface $mock) use ($mockTestStudy) { + $mock->shouldReceive('make') + ->andReturn($mockTestStudy); + }); + + + $this->instance(FrameworkAdapter::class, $mockFramework); + } + + public function testGetCreatablePatient() + { + + $userId = AuthorizationTools::actAsAdmin(true); + AuthorizationTools::addRoleToUser($userId, Constants::ROLE_INVESTIGATOR, "TEST"); + $answer = $this->json('GET', '/api/studies/TEST/creatable-patients?role=Investigator'); + $answer->assertSuccessful(); + } +} diff --git a/GaelO2/tests/Feature/TestUser/ForgotPasswordTest.php b/GaelO2/tests/Feature/TestUser/ForgotPasswordTest.php index f3eacb1af..fb44fcb31 100644 --- a/GaelO2/tests/Feature/TestUser/ForgotPasswordTest.php +++ b/GaelO2/tests/Feature/TestUser/ForgotPasswordTest.php @@ -74,6 +74,24 @@ public function testUpdatePassword() $this->assertTrue(Hash::check($password, $user->fresh()->password)); } + public function testUpdatePasswordShouldPassEvenUppercase() + { + $user = User::factory()->create(); + + $token = Password::createToken($user); + + $password = '>=5KBhxE=wWC'; + + $response = $this->post('api/tools/reset-password', [ + 'token' => $token, + 'email' => strtoupper($user->email), + 'password' => $password, + 'password_confirmation' => $password + ]); + + $this->assertTrue(Hash::check($password, $user->fresh()->password)); + } + public function testUpdatePasswordShouldFailWrongToken() { $user = User::factory()->create(); diff --git a/GaelO2/tests/Feature/TestUser/LoginTest.php b/GaelO2/tests/Feature/TestUser/LoginTest.php index a58cfe752..bc297a784 100644 --- a/GaelO2/tests/Feature/TestUser/LoginTest.php +++ b/GaelO2/tests/Feature/TestUser/LoginTest.php @@ -33,7 +33,20 @@ public function testLogin() $this->assertEquals($content['onboarded'], true); } - public function testLoginSuccessButNotOnboaded() + public function testLoginShouldPassInsensitive() + { + $data = ['email'=> strtoupper('administrator@gaelo.fr'), + 'password'=> 'administrator']; + $adminDefaultUser = User::where('id', 1)->first(); + $adminDefaultUser->onboarding_version= Config::get('app.onboarding_version'); + $adminDefaultUser->save(); + $response = $this->json('POST', '/api/login', $data)-> assertSuccessful(); + $content= json_decode($response->content(), true); + $this->assertArrayHasKey('access_token', $content); + $this->assertEquals($content['onboarded'], true); + } + + public function testLoginSuccessButNotOnboarded() { $data = ['email'=> 'administrator@gaelo.fr', 'password'=> 'administrator']; diff --git a/GaelO2/tests/Feature/TestUser/UserTest.php b/GaelO2/tests/Feature/TestUser/UserTest.php index b3483c0f2..72793461e 100644 --- a/GaelO2/tests/Feature/TestUser/UserTest.php +++ b/GaelO2/tests/Feature/TestUser/UserTest.php @@ -259,7 +259,7 @@ public function testGetUsersFromStudyAdministrator() //Expect to have 5 users in the list $this->assertEquals(5, sizeof($responseArray)); //Each User has full details - $this->assertEquals(16, sizeof( array_keys($responseArray[0]) )); + $this->assertEquals(17, sizeof( array_keys($responseArray[0]) )); } public function testGetUsersFromStudySupervisor() diff --git a/GaelO2/tests/Feature/TestVisitType/VisitTypeTest.php b/GaelO2/tests/Feature/TestVisitType/VisitTypeTest.php index c1729df2b..6536c1002 100644 --- a/GaelO2/tests/Feature/TestVisitType/VisitTypeTest.php +++ b/GaelO2/tests/Feature/TestVisitType/VisitTypeTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature\TestVisitType; +use App\GaelO\Constants\Constants; use App\Models\Patient; use App\Models\Study; use Tests\TestCase; @@ -15,32 +16,32 @@ class VisitTypeTest extends TestCase { use RefreshDatabase; - protected function setUp() : void { + protected function setUp(): void + { parent::setUp(); $this->artisan('db:seed'); $this->visitGroup = VisitGroup::factory()->create(); $this->payload = [ - 'name'=>'Baseline', - 'order'=>0, - 'localFormNeeded'=>true, - 'qcProbability'=>100, - 'reviewProbability'=>100, - 'optional'=>true, - 'limitLowDays'=>5, - 'limitUpDays'=>50, - 'anonProfile'=>'Default', - 'dicomConstraints'=>[] + 'name' => 'Baseline', + 'order' => 0, + 'localFormNeeded' => true, + 'qcProbability' => 100, + 'reviewProbability' => 100, + 'optional' => true, + 'limitLowDays' => 5, + 'limitUpDays' => 50, + 'anonProfile' => 'Default', + 'dicomConstraints' => [] ]; - } public function testCreateVisitType() { AuthorizationTools::actAsAdmin(true); $id = $this->visitGroup->id; - $this->json('POST', 'api/visit-groups/'.$id.'/visit-types', $this->payload)->assertNoContent(201); + $this->json('POST', 'api/visit-groups/' . $id . '/visit-types', $this->payload)->assertNoContent(201); $visitType = VisitType::where('name', 'Baseline')->get()->first(); $this->assertEquals(14, sizeOf($visitType->toArray())); $this->assertEquals($this->payload['qcProbability'], $visitType->qc_probability); @@ -54,8 +55,7 @@ public function testCreateVisitTypeShouldFailForAncillaryStudy() $ancillaryStudy = Study::factory()->ancillaryOf($study->name)->create(); $visitGroup = VisitGroup::factory()->studyName($ancillaryStudy->name)->create(); $id = $visitGroup->id; - $this->json('POST', 'api/visit-groups/'.$id.'/visit-types', $this->payload)->assertStatus(403); - + $this->json('POST', 'api/visit-groups/' . $id . '/visit-types', $this->payload)->assertStatus(403); } public function testCreateVisitTypeShouldFailedBecauseAlreadyExistingName() @@ -66,7 +66,7 @@ public function testCreateVisitTypeShouldFailedBecauseAlreadyExistingName() $payload = $this->payload; $payload['name'] = $visitType['name']; - $this->json('POST', 'api/visit-groups/'.$visitType->visitGroup->id.'/visit-types', $payload)->assertStatus(409); + $this->json('POST', 'api/visit-groups/' . $visitType->visitGroup->id . '/visit-types', $payload)->assertStatus(409); } public function testCreateVisitTypeShouldFailedBecauseAlreadyExistingVisitsInStudy() @@ -79,7 +79,7 @@ public function testCreateVisitTypeShouldFailedBecauseAlreadyExistingVisitsInStu $visit = Visit::factory()->patientId($patient->id)->visitTypeId($visitType->id)->create(); $payload = $this->payload; - $this->json('POST', 'api/visit-groups/'.$visit->visitType->visitGroup->id.'/visit-types', $payload)->assertStatus(409); + $this->json('POST', 'api/visit-groups/' . $visit->visitType->visitGroup->id . '/visit-types', $payload)->assertStatus(409); } public function testCreateVisitTypeForbiddenNotAdmin() @@ -87,15 +87,16 @@ public function testCreateVisitTypeForbiddenNotAdmin() AuthorizationTools::actAsAdmin(false); $visitType = VisitType::factory()->create(); $id = $visitType->visitGroup->id; - $this->json('POST', 'api/visit-groups/'.$id.'/visit-types', $this->payload)->assertStatus(403); + $this->json('POST', 'api/visit-groups/' . $id . '/visit-types', $this->payload)->assertStatus(403); } - public function testGetVisitType(){ + public function testGetVisitType() + { AuthorizationTools::actAsAdmin(true); $visitType = VisitType::factory()->create(); - $answer = $this->json('GET', 'api/visit-types/'.$visitType->id); + $answer = $this->json('GET', 'api/visit-types/' . $visitType->id); $answer->assertStatus(200); $expectedKeys = [ @@ -114,7 +115,6 @@ public function testGetVisitType(){ ]; $answer->assertJsonStructure($expectedKeys); - } public function testGetVisitTypeForbiddenNotAdmin() @@ -122,38 +122,61 @@ public function testGetVisitTypeForbiddenNotAdmin() AuthorizationTools::actAsAdmin(false); $visitType = VisitType::factory()->create(); - $this->json('GET', 'api/visit-types/'.$visitType->id)->assertStatus(403); + $this->json('GET', 'api/visit-types/' . $visitType->id)->assertStatus(403); } - public function testDeleteVisitType(){ + public function testDeleteVisitType() + { AuthorizationTools::actAsAdmin(true); $visitGroup = VisitGroup::factory()->create(); $visitType = VisitType::factory()->visitGroupId($visitGroup->id)->create(); - $this->json('DELETE', 'api/visit-types/'.$visitType->id)->assertStatus(200); + $this->json('DELETE', 'api/visit-types/' . $visitType->id)->assertStatus(200); } - public function testDeleteVisitTypeForbiddenNotAdmin(){ + public function testDeleteVisitTypeForbiddenNotAdmin() + { AuthorizationTools::actAsAdmin(false); $visitGroup = VisitGroup::factory()->create(); $visitType = VisitType::factory()->visitGroupId($visitGroup->id)->create(); - $this->json('DELETE', 'api/visit-types/'.$visitType->id)->assertStatus(403); + $this->json('DELETE', 'api/visit-types/' . $visitType->id)->assertStatus(403); } - public function testDeleteVisitTypeForbiddenForAncillaries(){ + public function testDeleteVisitTypeForbiddenForAncillaries() + { AuthorizationTools::actAsAdmin(true); $study = Study::factory()->create(); $ancillaryStudy = Study::factory()->ancillaryOf($study->name)->create(); $visitGroup = VisitGroup::factory()->studyName($ancillaryStudy->name)->create(); $visitType = VisitType::factory()->visitGroupId($visitGroup->id)->create(); - $this->json('DELETE', 'api/visit-types/'.$visitType->id)->assertStatus(403); + $this->json('DELETE', 'api/visit-types/' . $visitType->id)->assertStatus(403); } - public function testDeleteVisitTypeShouldFailedBecauseHasChildVisit(){ + public function testDeleteVisitTypeShouldFailedBecauseHasChildVisit() + { AuthorizationTools::actAsAdmin(true); $patient = Patient::factory()->create(); $visitGroup = VisitGroup::factory()->studyName($patient->study_name)->create(); $visitType = VisitType::factory()->visitGroupId($visitGroup->id)->create(); $visits = Visit::factory()->patientId($patient->id)->visitTypeId($visitType->id)->create(); - $this->json('DELETE', 'api/visit-types/'.$visits->first()->visitType->id)->assertStatus(403); + $this->json('DELETE', 'api/visit-types/' . $visits->first()->visitType->id)->assertStatus(403); + } + + public function testGetFileMetadataOfVisitType() + { + $visitType = VisitType::factory()->create(); + $userId = AuthorizationTools::actAsAdmin(false); + $studyName = $visitType->visitGroup->study_name; + AuthorizationTools::addRoleToUser($userId, Constants::ROLE_SUPERVISOR, $visitType->visitGroup->study_name); + $answer = $this->json('GET', 'api/visit-types/' . $visitType->id . '/files/metadata?studyName=' . $studyName . '&role=' . Constants::ROLE_SUPERVISOR); + $answer->assertStatus(200); + } + + public function testGetFileMetadataOfVisitTypeShouldFailNoRole() + { + $visitType = VisitType::factory()->create(); + $userId = AuthorizationTools::actAsAdmin(false); + $studyName = $visitType->visitGroup->study_name; + $answer = $this->json('GET', 'api/visit-types/' . $visitType->id . '/files/metadata?studyName=' . $studyName . '&role=' . Constants::ROLE_SUPERVISOR); + $answer->assertStatus(403); } } diff --git a/GaelO2/tests/Feature/TestVisits/FileVisitTest.php b/GaelO2/tests/Feature/TestVisits/FileVisitTest.php new file mode 100644 index 000000000..fd12f0d08 --- /dev/null +++ b/GaelO2/tests/Feature/TestVisits/FileVisitTest.php @@ -0,0 +1,112 @@ +artisan('db:seed'); + Storage::fake(); + } + + private function createVisit() + { + $study = Study::factory()->name('TEST')->create(); + $patient = Patient::factory()->studyName($study->name)->create(); + $visitGroup = VisitGroup::factory()->studyName($study->name)->name('FDG')->create(); + $visitType = VisitType::factory()->visitGroupId($visitGroup->id)->name('PET_0')->localFormNeeded()->create(); + $path = $study->name . '/' . 'attached_visit_file' . '/' . 'visit_1_41.csv'; + $visit = Visit::factory()->patientId($patient->id)->visitTypeId($visitType->id)->sentFiles(['41' => $path])->create(); + Storage::put($path, "testcontent"); + return [ + 'studyName' => $study->name, + 'visitId' => $visit->id, + 'centerCode' => $patient->center_code + ]; + } + + public function testGetFileOfVisit() + { + $currentVisit = $this->createVisit(); + $currentUserId = AuthorizationTools::actAsAdmin(false); + AuthorizationTools::addRoleToUser($currentUserId, Constants::ROLE_INVESTIGATOR, $currentVisit['studyName']); + AuthorizationTools::addAffiliatedCenter($currentUserId, $currentVisit['centerCode']); + $response = $this->get('api/visits/' . $currentVisit['visitId'] . '/files/41?role=Investigator&studyName=TEST'); + $response->assertSuccessful(); + } + + public function testGetFileOfVisitShouldFailNoRole() + { + $currentVisit = $this->createVisit(); + $currentUserId = AuthorizationTools::actAsAdmin(false); + AuthorizationTools::addRoleToUser($currentUserId, Constants::ROLE_INVESTIGATOR, $currentVisit['studyName']); + AuthorizationTools::addAffiliatedCenter($currentUserId, $currentVisit['centerCode']); + $response = $this->get('api/visits/' . $currentVisit['visitId'] . '/files/41?role=Supervisor&studyName=TEST'); + $response->assertStatus(403); + } + + public function testDeleteFileOfVisit() + { + $currentVisit = $this->createVisit(); + $currentUserId = AuthorizationTools::actAsAdmin(false); + AuthorizationTools::addRoleToUser($currentUserId, Constants::ROLE_SUPERVISOR, $currentVisit['studyName']); + $visit = Visit::find($currentVisit['visitId']); + $visit->sent_files = ['41' => $currentVisit['studyName'] . '/' . 'attached_review_file' . '/' . 'review_1_41.csv']; + $visit->save(); + $response = $this->delete('api/visits/' . $visit->id . '/files/41?studyName=TEST&role=Supervisor'); + $response->assertSuccessful(); + } + + public function testDeleteFileOfVisitShouldFailNoRole() + { + $currentVisit = $this->createVisit(); + AuthorizationTools::actAsAdmin(false); + $visit = Visit::find($currentVisit['visitId']); + $visit->sent_files = ['41' => $currentVisit['studyName'] . '/' . 'attached_review_file' . '/' . 'review_1_41.csv']; + $visit->save(); + $response = $this->delete('api/visits/' . $visit->id . '/files/41?studyName=TEST&role=Supervisor'); + $response->assertStatus(403); + } + + public function testCreateFileOfVisit() + { + + $currentVisit = $this->createVisit(); + $currentUserId = AuthorizationTools::actAsAdmin(false); + AuthorizationTools::addRoleToUser($currentUserId, Constants::ROLE_SUPERVISOR, $currentVisit['studyName']); + $response = $this->post('api/visits/' . $currentVisit['visitId'] . '/files/prediction?studyName=TEST&role=Supervisor', ['content' => base64_encode("testFileContent"), "extension" => 'csv', 'contentType'=>'text/csv']); + $response->assertStatus(201); + } + + public function testCreateFileOfVisitShouldFailWrongKey() + { + + $currentVisit = $this->createVisit(); + $currentUserId = AuthorizationTools::actAsAdmin(false); + AuthorizationTools::addRoleToUser($currentUserId, Constants::ROLE_SUPERVISOR, $currentVisit['studyName']); + $response = $this->post('api/visits/' . $currentVisit['visitId'] . '/files/wrong?studyName=TEST&role=Supervisor', ['content' => base64_encode("testFileContent"), 'contentType'=>'text/csv']); + $response->assertStatus(403); + } + + public function testCreateFileOfVisitShouldFileNoRole() + { + + $currentVisit = $this->createVisit(); + AuthorizationTools::actAsAdmin(false); + $response = $this->post('api/visits/' . $currentVisit['visitId'] . '/files/prediction?studyName=TEST&role=Supervisor', ['content' => base64_encode("testFileContent"), 'contentType'=>'text/csv']); + $response->assertStatus(403); + } +} diff --git a/GaelO2/tests/Unit/TestJobs/JobGaelOProcessingTest.php b/GaelO2/tests/Unit/TestJobs/JobGaelOProcessingTest.php deleted file mode 100644 index eca941148..000000000 --- a/GaelO2/tests/Unit/TestJobs/JobGaelOProcessingTest.php +++ /dev/null @@ -1,49 +0,0 @@ -artisan('db:seed'); - } - - public function testBatch(){ - - //Bus::fake(); - Bus::fake(); - - $job1 = new JobGaelOProcessing( ["3a84b7f7-d0c66087-d70b292e-0c585356-56b6ccb3"],'Nimportequoi','http://gaeloprocessing:8000'); - $job2 = new JobGaelOProcessing( ["3a84b7f7-d0c66087-d70b292e-0c585356-56b6ccb3"],'Nimportequoi','http://gaeloprocessing:8000'); - - $batch = Bus::batch([ - $job1, - $job2 - ])->then(function (Batch $batch) { - // All jobs completed successfully... - })->name('processing')->allowFailures()->dispatch(); - - //dd($batch); - - Bus::assertBatched(function (PendingBatch $batch) { - // Make sure you test the batch is dispatched - return $batch->name === 'processing'; - }); - - $this->assertEquals(2, $batch->totalJobs); - - } -} - - diff --git a/GaelO2/tests/Unit/TestJobs/JobQcReportTest.php b/GaelO2/tests/Unit/TestJobs/JobQcReportTest.php index 2dc68b8fc..5bc735b8d 100644 --- a/GaelO2/tests/Unit/TestJobs/JobQcReportTest.php +++ b/GaelO2/tests/Unit/TestJobs/JobQcReportTest.php @@ -20,6 +20,8 @@ class JobQcReportTest extends TestCase { use RefreshDatabase; + private DicomSeriesRepository $orthancSeriesRepository; + private DicomStudy $orthancStudy; protected function setUp(): void { diff --git a/GaelO2/tests/Unit/TestJobs/JobRadiomicsReportTest.php b/GaelO2/tests/Unit/TestJobs/JobRadiomicsReportTest.php new file mode 100644 index 000000000..e0baacc9d --- /dev/null +++ b/GaelO2/tests/Unit/TestJobs/JobRadiomicsReportTest.php @@ -0,0 +1,47 @@ +artisan('db:seed'); + $this->study = Study::factory()->name("TEST")->create(); + $this->visit = Visit::factory()->create(); + $dicomStudy = DicomStudy::factory()->visitId($this->visit->id)->create(); + $dicomSeries = DicomSeries::factory()->studyInstanceUID($dicomStudy->study_uid)->count(5)->create(); + + + } + + public function testTmtvInference() { + $this->markTestSkipped(); + JobRadiomicsReport::dispatchSync($this->visit->id); + } + + public function testTmtvInferenceFailMethods(){ + Mail::fake(); + $radiomicsReportJob = new JobRadiomicsReport($this->visit->id, 1); + $exception = new Exception('fakeError'); + $radiomicsReportJob->failed($exception); + Mail::assertQueued(JobFailure::class); + } +} diff --git a/GaelO2/tests/Unit/TestRepositories/StudyRepositoryTest.php b/GaelO2/tests/Unit/TestRepositories/StudyRepositoryTest.php index a6866c34b..e005f8fd4 100644 --- a/GaelO2/tests/Unit/TestRepositories/StudyRepositoryTest.php +++ b/GaelO2/tests/Unit/TestRepositories/StudyRepositoryTest.php @@ -26,21 +26,24 @@ protected function setUp(): void $this->studyRepository = new StudyRepository(new Study()); } - public function testCreateStudy(){ - $this->studyRepository->addStudy('myStudy', '12345', 5, 'contact@gaelo.fr',true, false, false, null); + public function testCreateStudy() + { + $this->studyRepository->addStudy('myStudy', '12345', 5, 'contact@gaelo.fr', true, false, false, null, false, false); $studyEntity = Study::find('myStudy'); $this->assertEquals('myStudy', $studyEntity->name); $this->assertEquals('12345', $studyEntity->code); - $this->assertEquals( 5 , $studyEntity->patient_code_length); - $this->assertEquals( 'contact@gaelo.fr' , $studyEntity->contact_email); - $this->assertTrue( (bool) $studyEntity->controller_show_all); - $this->assertFalse( (bool) $studyEntity->monitor_show_all); - $this->assertFalse( (bool) $studyEntity->documentation_mandatory); - $this->assertEquals( null , $studyEntity->ancillary_of); + $this->assertEquals(5, $studyEntity->patient_code_length); + $this->assertEquals('contact@gaelo.fr', $studyEntity->contact_email); + $this->assertTrue((bool) $studyEntity->controller_show_all); + $this->assertFalse((bool) $studyEntity->monitor_show_all); + $this->assertFalse((bool) $studyEntity->documentation_mandatory); + $this->assertEquals(null, $studyEntity->ancillary_of); + $this->assertFalse((bool) $studyEntity->creatable_patients_investigator); } - public function testIsExistingStudy(){ + public function testIsExistingStudy() + { $studyEntity = Study::factory()->create(); $answer = $this->studyRepository->isExistingStudyName($studyEntity->name); $answer2 = $this->studyRepository->isExistingStudyName('NotExistingStudyName'); @@ -48,7 +51,8 @@ public function testIsExistingStudy(){ $this->assertFalse($answer2); } - public function testGetStudies(){ + public function testGetStudies() + { Study::factory()->create(); Study::factory()->create()->delete(); @@ -56,12 +60,12 @@ public function testGetStudies(){ $answer = $this->studyRepository->getStudies(); $answer2 = $this->studyRepository->getStudies(true); - $this->assertEquals(1, sizeof($answer) ); - $this->assertEquals(2, sizeof($answer2) ); - + $this->assertEquals(1, sizeof($answer)); + $this->assertEquals(2, sizeof($answer2)); } - public function testGetStudyWithDetails(){ + public function testGetStudyWithDetails() + { $visitType = VisitType::factory()->create(); @@ -71,10 +75,10 @@ public function testGetStudyWithDetails(){ $this->assertArrayHasKey('visit_groups', $answer); $this->assertArrayHasKey('visit_types', $answer['visit_groups'][0]); - } - public function testGetAllStudiesWithDetails(){ + public function testGetAllStudiesWithDetails() + { VisitType::factory()->count(5)->create(); VisitType::factory()->create()->delete(); @@ -84,10 +88,10 @@ public function testGetAllStudiesWithDetails(){ $this->assertEquals(6, sizeof($answer)); $this->assertArrayHasKey('visit_groups', $answer[0]); $this->assertArrayHasKey('visit_types', $answer[0]['visit_groups'][0]); - } - public function testReactivateStudy(){ + public function testReactivateStudy() + { $study = Study::factory()->create(); $study->delete(); @@ -98,7 +102,8 @@ public function testReactivateStudy(){ $this->assertNull($updatedStudy['deleted_at']); } - public function testGetAncilariesStudies(){ + public function testGetAncilariesStudies() + { $study = Study::factory()->create(); Study::factory()->ancillaryOf($study->name)->count(5)->create(); @@ -106,7 +111,8 @@ public function testGetAncilariesStudies(){ $this->assertEquals(5, sizeof($ancilarriesStudies)); } - public function testGetStatistics(){ + public function testGetStatistics() + { $study = Study::factory()->create(); $patients = Patient::factory()->count(30)->studyName($study->name)->create(); $visit = Visit::factory()->patientId($patients->first()->id)->create(); @@ -121,11 +127,7 @@ public function testGetStatistics(){ $this->assertEquals($statistics['patients_count'], 30); $this->assertEquals($statistics['dicom_studies_count'], 1); $this->assertEquals($statistics['dicom_series_count'], 5); - $this->assertGreaterThan(0 , $statistics['dicom_instances_count']); - $this->assertGreaterThan(0 , $statistics['dicom_disk_size']); - - + $this->assertGreaterThan(0, $statistics['dicom_instances_count']); + $this->assertGreaterThan(0, $statistics['dicom_disk_size']); } - - } diff --git a/GaelO2/tests/Unit/TestRepositories/UserRepositoryTest.php b/GaelO2/tests/Unit/TestRepositories/UserRepositoryTest.php index 3333261e5..4c4e5a0d1 100644 --- a/GaelO2/tests/Unit/TestRepositories/UserRepositoryTest.php +++ b/GaelO2/tests/Unit/TestRepositories/UserRepositoryTest.php @@ -161,6 +161,13 @@ public function testGetUserByUsername() $this->assertNotNull($userEntity['deleted_at']); } + public function testGetUserByEmailInsensitive() + { + $user = User::factory()->job(JobEnum::SUPERVISION->value)->create(); + $userEntity = $this->userRepository->getUserByEmail(strtoupper($user->email), false); + $this->assertIsArray($userEntity); + } + public function testIsExistingEmail() { @@ -175,6 +182,18 @@ public function testIsExistingEmail() $this->assertFalse($testNotExisting); } + public function testIsExistingEmailInsensitive() + { + + $user = User::factory()->create(); + //Username test even if soft deleted user + $user->delete(); + + $testExisting = $this->userRepository->isExistingEmail(strtoupper($user->email)); + + $this->assertTrue($testExisting); + } + public function testGetAdministrators() { diff --git a/GaelO2/tests/Unit/TestRepositories/VisitRepositoryTest.php b/GaelO2/tests/Unit/TestRepositories/VisitRepositoryTest.php index 0bdf5a2ac..fffaf50f6 100644 --- a/GaelO2/tests/Unit/TestRepositories/VisitRepositoryTest.php +++ b/GaelO2/tests/Unit/TestRepositories/VisitRepositoryTest.php @@ -459,4 +459,11 @@ public function testGetVisitOfPatientByVisitTypeName() $this->expectException(ModelNotFoundException::class); $this->visitRepository->getVisitOfPatientByVisitTypeName($patientId, $visitGroupName, $visitTypeName.'wrong', true, $studyName); } + + public function testUpdateVisitFiles(){ + $visit = Visit::factory()->create(); + $this->visitRepository->updateVisitFile($visit->id, ['myKey' => 'myFile.pdf'] ); + $updatedVisit = Visit::find($visit->id); + $this->assertArrayHasKey('myKey', $updatedVisit['sent_files']); + } } diff --git a/GaelO2/tests/Unit/TestServices/GaelOProcessingServiceTest.php b/GaelO2/tests/Unit/TestServices/GaelOProcessingServiceTest.php index 5443a6221..1c7264cd6 100644 --- a/GaelO2/tests/Unit/TestServices/GaelOProcessingServiceTest.php +++ b/GaelO2/tests/Unit/TestServices/GaelOProcessingServiceTest.php @@ -19,6 +19,7 @@ protected function setUp():void{ public function testSendDicom() { - $resultat=$this->gaeloProcessingService->sendDicom(["a97f5e66-bbff00d4-1639c63f-a3e1e53a-d4b5e553"]); + $path = getcwd() . "/tests/data/MR.zip"; + $resultat=$this->gaeloProcessingService->createDicom($path); } } diff --git a/GaelO2/tests/Unit/TestServices/PdfServiceTest.php b/GaelO2/tests/Unit/TestServices/PdfServiceTest.php new file mode 100644 index 000000000..6b484a8c0 --- /dev/null +++ b/GaelO2/tests/Unit/TestServices/PdfServiceTest.php @@ -0,0 +1,25 @@ +pdfService = App::make(PdfServices::class); + } + + public function testMakeRadiomicsPdf() + { + $filename = $this->pdfService->saveRadiomicsPdf('TEST', '12345', 'PET0', '07141995', ['tmtv41' => 55]); + $this->assertIsString($filename); + } + +} diff --git a/GaelO2/tests/Unit/TestServices/VisitTreeTest.php b/GaelO2/tests/Unit/TestServices/VisitTreeTest.php index a03338c2c..49da9148d 100644 --- a/GaelO2/tests/Unit/TestServices/VisitTreeTest.php +++ b/GaelO2/tests/Unit/TestServices/VisitTreeTest.php @@ -147,7 +147,9 @@ protected function setUp(): void 'monitor_show_all' => false, 'documentation_mandatory' => false, 'ancillary_of' => null, - 'deleted_at' => null + 'deleted_at' => null, + 'creatable_patients_investigator' => false + ])); diff --git a/README.md b/README.md index 2696edec5..b9959d5c8 100755 --- a/README.md +++ b/README.md @@ -29,5 +29,6 @@ node_modules/mjml/bin/mjml ./app/GaelO/views/mails/mjml/qc_report_buttons.mjml - node_modules/mjml/bin/mjml ./app/GaelO/views/mails/mjml/qc_report_series.mjml -o ./app/GaelO/views/mails/mail_qc_report_series.blade.php node_modules/mjml/bin/mjml ./app/GaelO/views/mails/mjml/qc_report_study.mjml -o ./app/GaelO/views/mails/mail_qc_report_study.blade.php node_modules/mjml/bin/mjml ./app/GaelO/views/mails/mjml/qc_report_investigator_form.mjml -o ./app/GaelO/views/mails/mail_qc_report_investigator_form.blade.php +node_modules/mjml/bin/mjml ./app/GaelO/views/mails/mjml/radiomics_report.mjml -o ./app/GaelO/views/mails/mail_radiomics_report.blade.php ``` In blade generated files, edit file to keep only body content (remove header...)