From 977728a22ddc692ea2854d66ee76dd068f6d311e Mon Sep 17 00:00:00 2001 From: dbdbd9 Date: Fri, 20 Sep 2024 16:02:16 +0700 Subject: [PATCH] Evaluation: filter date sprint-review, filter technical name, export report file --- .../Http/Controllers/EvaluationController.php | 253 ++++++++++++++++++ .../Modules/Admin/app/Models/Technical.php | 10 + .../Admin/app/Models/TechnicalUser.php | 21 ++ BACKEND/Modules/Admin/routes/api.php | 9 + BACKEND/app/Exports/TechnicalsExport.php | 18 ++ BACKEND/composer.json | 1 + BACKEND/composer.lock | 163 ++++++++++- 7 files changed, 474 insertions(+), 1 deletion(-) create mode 100644 BACKEND/Modules/Admin/app/Http/Controllers/EvaluationController.php create mode 100644 BACKEND/Modules/Admin/app/Models/Technical.php create mode 100644 BACKEND/Modules/Admin/app/Models/TechnicalUser.php create mode 100644 BACKEND/app/Exports/TechnicalsExport.php diff --git a/BACKEND/Modules/Admin/app/Http/Controllers/EvaluationController.php b/BACKEND/Modules/Admin/app/Http/Controllers/EvaluationController.php new file mode 100644 index 0000000..788236c --- /dev/null +++ b/BACKEND/Modules/Admin/app/Http/Controllers/EvaluationController.php @@ -0,0 +1,253 @@ +jiraService = $jiraService; + } + + public function sprintReview(Request $request) + { + $request->validate([ + 'userID' => 'required|exists:users,id', + 'fromDate' => 'nullable|date', + 'toDate' => 'nullable|date', + ]); + + $userEmail = User::findOrFail($request->input('userID'))->email; + + $projects = $this->jiraService->getAllProjects(); + + $query = UserCriteria::where('user_email', $userEmail); + + // Date filter + if ($request->filled('fromDate')) { + $fromDate = Carbon::parse($request->input('fromDate')); + $query->where('created_at', '>=', $fromDate); + } + + if ($request->filled('toDate')) { + $toDate = Carbon::parse($request->input('toDate')); + $query->where('created_at', '<=', $toDate); + } + + // End query + $userCriterias = $query->with(['sprint', 'criteria'])->get(); + + $projectsData = $userCriterias->groupBy('sprint.project_id')->map(function ($userCriteriasByProject, $projectId) use ($projects) { + $result = self::getProjectById($projects, $projectId); + return [ + 'name' => $result['name'], + 'sprints' => $userCriteriasByProject->groupBy('sprint.id')->map(function ($userCriteriasBySprint) { + $sprint = $userCriteriasBySprint->first()->sprint; + return [ + 'name' => $sprint->name, + 'criterias' => $userCriteriasBySprint->map(function ($userCriteria) { + $criteria = $userCriteria->criteria; + return [ + 'criteria' => $criteria->name, + 'note' => $userCriteria->note ?? '', + 'createdBy' => $userCriteria->created_by ?? '', + 'point' => $userCriteria->point ?? '', + ]; + }) + ]; + })->values() + ]; + })->values(); + + return AbstractController::ResultSuccess($projectsData); + } + + public function getProjectById($projects, $inputId) + { + $filteredProjects = array_filter($projects, function ($project) use ($inputId) { + return $project['id'] == $inputId; + }); + return array_values($filteredProjects) ? array_values($filteredProjects)[0] : array_values($filteredProjects); + } + + public function technical(Request $request) + { + $request->validate([ + 'user_id' => 'required|exists:users,id', + 'keyword' => 'nullable|string' + ]); + + $user = User::findOrFail($request->input('user_id')); + + $query = TechnicalUser::query(); + $query = TechnicalUser::where('user_id', $user->id); + + if ($request->filled('keyword')) { + $keyword = '%' . $request->input('keyword') . '%'; + $query->whereHas('technical', fn($q) => $q->where('name', 'like', $keyword)) + ->with('technical'); + } else { + $query->with('technical'); + } + + $technical_users = $query->where('point', '>', 0)->get(); + + return AbstractController::ResultSuccess( + $technical_users + ->map(function ($user) { + return [ + 'id' => $user->id, + 'point' => $user->point, + 'updated_at' => $user->updated_at, + 'technical' => [ + 'id' => $user->technical->id, + 'name' => $user->technical->name, + 'level' => $user->technical->level, + ] + ]; + }) + ); + } + + public function report(Request $request) + { + $request->validate([ + 'userID' => 'required|exists:users,id', + 'fromDate' => 'nullable|date', + 'toDate' => 'nullable|date', + ]); + + // Query User + $user = User::findOrFail($request->input('userID')); + $userEmail = $user->email; + + // Query User Criteria + $query = UserCriteria::where('user_email', $userEmail); + + if ($request->filled('fromDate')) { + $fromDate = Carbon::parse($request->input('fromDate')); + $query->where('created_at', '>=', $fromDate); + } + + if ($request->filled('toDate')) { + $toDate = Carbon::parse($request->input('toDate')); + $query->where('created_at', '<=', $toDate); + } + + $userCriterias = $query->with(['sprint', 'criteria'])->get(); + + + // Structure projects data + $projects = $this->jiraService->getAllProjects(); + + $projectsData = $userCriterias->groupBy('sprint.project_id')->map(function ($userCriteriasByProject, $projectId) use ($projects) { + $result = self::getProjectById($projects, $projectId); + + return [ + 'name' => $result['name'], + 'sprints' => $userCriteriasByProject->groupBy('sprint.id')->map(function ($userCriteriasBySprint) { + $sprint = $userCriteriasBySprint->first()->sprint; + return [ + 'name' => $sprint->name, + 'criterias' => $userCriteriasBySprint->map(function ($userCriteria) { + $criteria = $userCriteria->criteria; + return [ + 'criteria' => $criteria->name, + 'note' => $userCriteria->note ?? '', + 'createdBy' => $userCriteria->created_by ?? '', + 'point' => $userCriteria->point ?? '', + ]; + }) + ]; + })->values() + ]; + })->values(); + + // Generate Word document + $phpWord = new PhpWord(); + $phpWord->setDefaultFontName('Times New Roman'); + $phpWord->setDefaultFontSize(12); + $section = $phpWord->addSection(); + + $section->addText('Staff Evaluation', [ + 'bold' => true, + 'size' => 20, + 'color' => '000000', + ], [ + 'align' => 'center', + 'spaceAfter' => 600, + ]); + + if ($request->filled('fromDate')) { + $fromDate = Carbon::parse($request->input('fromDate')); + $section->addText("From: " . $fromDate->format('d-m-Y'), ['name' => 'Times New Roman', 'size' => 12], ['align' => 'end']); + } + + if ($request->filled('toDate')) { + $toDate = Carbon::parse($request->input('toDate')); + $section->addText("To: " . $toDate->format('d-m-Y'), ['name' => 'Times New Roman', 'size' => 12], ['align' => 'end']); + } + + $section->addText("{$user->name}", [ + 'bold' => true, + 'size' => 14, + 'color' => '000000', + ], [ + 'spaceAfter' => 400, + ]); + + foreach ($projectsData as $project) { + $section->addText("Project: {$project['name']}", ['bold' => true, 'size' => 14, 'color' => '000080']); + + foreach ($project['sprints'] as $sprint) { + $section->addText("Sprint: {$sprint['name']}", ['bold' => true, 'italic' => true, 'size' => 12, 'color' => '000000']); + + $table = $section->addTable([ + 'borderColor' => '000000', + 'borderSize' => 6, + 'cellMargin' => 80, + ]); + + $table->addRow(); + $table->addCell(2000)->addText('Criteria', ['bold' => true]); + $table->addCell(2000)->addText('Note', ['bold' => true]); + $table->addCell(2000)->addText('Created By', ['bold' => true]); + $table->addCell(2000)->addText('Points', ['bold' => true]); + + foreach ($sprint['criterias'] as $criteria) { + $table->addRow(); + $table->addCell(2000)->addText($criteria['criteria']); + $table->addCell(2000)->addText($criteria['note']); + $table->addCell(2000)->addText($criteria['createdBy']); + $table->addCell(2000)->addText($criteria['point']); + } + + $section->addText(' ', [], [ + 'spaceAfter' => 600, + ]); + } + + $section->addText(' ', [], [ + 'spaceAfter' => 600, + ]); + } + + $tempFile = tempnam(sys_get_temp_dir(), 'word'); + $objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007'); + $objWriter->save($tempFile); + + return response()->download($tempFile, "$user->name.docx")->deleteFileAfterSend(true); + } +} diff --git a/BACKEND/Modules/Admin/app/Models/Technical.php b/BACKEND/Modules/Admin/app/Models/Technical.php new file mode 100644 index 0000000..6776347 --- /dev/null +++ b/BACKEND/Modules/Admin/app/Models/Technical.php @@ -0,0 +1,10 @@ +belongsTo(User::class, 'user_id', 'id'); + } + + public function technical() + { + return $this->belongsTo(Technical::class, 'technical_id', 'id'); + } +} diff --git a/BACKEND/Modules/Admin/routes/api.php b/BACKEND/Modules/Admin/routes/api.php index b6382a7..6089496 100755 --- a/BACKEND/Modules/Admin/routes/api.php +++ b/BACKEND/Modules/Admin/routes/api.php @@ -16,6 +16,7 @@ use Modules\Admin\app\Http\Controllers\TicketController; use Modules\Admin\app\Http\Controllers\TimekeepingController; use Modules\Admin\app\Http\Controllers\TrackingController; use Modules\Admin\app\Http\Controllers\CriteriasController; +use Modules\Admin\app\Http\Controllers\EvaluationController; use Modules\Admin\app\Http\Controllers\ProfileController; use Modules\Admin\app\Http\Controllers\TestCaseForSprintController; use Modules\Admin\app\Http\Middleware\AdminMiddleware; @@ -173,6 +174,14 @@ Route::middleware('api') Route::get('/profiles-data', [ProfileController::class, 'getProfilesData'])->middleware('check.permission:admin.hr.staff.tester'); Route::post('/profiles-data/update', [ProfileController::class, 'updateProfilesData'])->middleware('check.permission:admin.hr.staff.tester'); }); + + Route::group([ + 'prefix' => 'evaluation', + ], function () { + Route::get('/sprint-review', [EvaluationController::class, 'sprintReview'])->middleware('check.permission:admin'); + Route::get('/technical', [EvaluationController::class, 'technical'])->middleware('check.permission:admin'); + Route::get('/report', [EvaluationController::class, 'report'])->middleware('check.permission:admin'); + }); }); }); diff --git a/BACKEND/app/Exports/TechnicalsExport.php b/BACKEND/app/Exports/TechnicalsExport.php new file mode 100644 index 0000000..301b316 --- /dev/null +++ b/BACKEND/app/Exports/TechnicalsExport.php @@ -0,0 +1,18 @@ +=7.0", + "symfony/process": "^4.4 || ^5.0", + "tecnickcom/tcpdf": "^6.5" + }, + "suggest": { + "dompdf/dompdf": "Allows writing PDF", + "ext-gd2": "Allows adding images", + "ext-xmlwriter": "Allows writing OOXML and ODF", + "ext-xsl": "Allows applying XSL style sheet to headers, to main document part, and to footers of an OOXML template", + "ext-zip": "Allows writing OOXML and ODF" + }, + "type": "library", + "autoload": { + "psr-4": { + "PhpOffice\\PhpWord\\": "src/PhpWord" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0" + ], + "authors": [ + { + "name": "Mark Baker" + }, + { + "name": "Gabriel Bull", + "email": "me@gabrielbull.com", + "homepage": "http://gabrielbull.com/" + }, + { + "name": "Franck Lefevre", + "homepage": "https://rootslabs.net/blog/" + }, + { + "name": "Ivan Lanin", + "homepage": "http://ivan.lanin.org" + }, + { + "name": "Roman Syroeshko", + "homepage": "http://ru.linkedin.com/pub/roman-syroeshko/34/a53/994/" + }, + { + "name": "Antoine de Troostembergh" + } + ], + "description": "PHPWord - A pure PHP library for reading and writing word processing documents (OOXML, ODF, RTF, HTML, PDF)", + "homepage": "https://phpoffice.github.io/PHPWord/", + "keywords": [ + "ISO IEC 29500", + "OOXML", + "Office Open XML", + "OpenDocument", + "OpenXML", + "PhpOffice", + "PhpWord", + "Rich Text Format", + "WordprocessingML", + "doc", + "docx", + "html", + "odf", + "odt", + "office", + "pdf", + "php", + "reader", + "rtf", + "template", + "template processor", + "word", + "writer" + ], + "support": { + "issues": "https://github.com/PHPOffice/PHPWord/issues", + "source": "https://github.com/PHPOffice/PHPWord/tree/1.3.0" + }, + "time": "2024-08-30T18:03:42+00:00" + }, { "name": "phpoption/phpoption", "version": "1.9.3",