This commit introduces a complete documentation suite, refactors the API layer for
better consistency, and updates the database schema and seeding logic.
Key Changes:
- Documentation:
- Added `CLAUDE.md` with development guidelines and architecture overview.
- Created `docs/` directory with detailed guides for architecture, development,
and source tree analysis.
- Database & Migrations:
- Implemented `RenameMasterColumns` migration to standardize column naming
(e.g., `name` -> `dept_name`, `name` -> `control_name`).
- Added `CmodQcSeeder` to populate the system with realistic sample data
for depts, controls, tests, and results.
- Backend API:
- Created `DashboardApiController` with `getRecent()` for dashboard stats.
- Created `ReportApiController` for managed reporting access.
- Updated `app/Config/Routes.php` with new API groupings and documentation routes.
- Frontend & Views:
- Refactored master data views (`dept`, `test`, `control`) to use Alpine.js
and the updated API structure.
- Modernized `dashboard.php` and `main_layout.php` with improved UI/UX.
- Infrastructure:
- Updated `.gitignore` to exclude development-specific artifacts (`_bmad/`, `.claude/`).
120 lines
4.5 KiB
PHP
120 lines
4.5 KiB
PHP
<?php
|
|
|
|
namespace App\Controllers\Api;
|
|
|
|
use App\Controllers\BaseController;
|
|
use CodeIgniter\API\ResponseTrait;
|
|
use App\Models\Master\MasterControlsModel;
|
|
use App\Models\Master\MasterTestsModel;
|
|
use App\Models\Qc\ControlTestsModel;
|
|
use App\Models\Qc\ResultsModel;
|
|
use App\Models\Qc\ResultCommentsModel;
|
|
|
|
class ReportApiController extends BaseController
|
|
{
|
|
use ResponseTrait;
|
|
|
|
protected $dictControlModel;
|
|
protected $dictTestModel;
|
|
protected $controlTestModel;
|
|
protected $resultModel;
|
|
protected $commentModel;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->dictControlModel = new MasterControlsModel();
|
|
$this->dictTestModel = new MasterTestsModel();
|
|
$this->controlTestModel = new ControlTestsModel();
|
|
$this->resultModel = new ResultsModel();
|
|
$this->commentModel = new ResultCommentsModel();
|
|
}
|
|
|
|
public function getReport()
|
|
{
|
|
try {
|
|
$control1 = $this->request->getGet('control1') ?? 0;
|
|
$control2 = $this->request->getGet('control2') ?? 0;
|
|
$control3 = $this->request->getGet('control3') ?? 0;
|
|
$dates = $this->request->getGet('dates') ?? date('Y-m');
|
|
$test = $this->request->getGet('test') ?? 0;
|
|
|
|
$controlIds = array_filter([$control1, $control2, $control3]);
|
|
|
|
$reportData = [];
|
|
foreach ($controlIds as $controlId) {
|
|
$control = $this->dictControlModel->find($controlId);
|
|
if (!$control) continue;
|
|
|
|
$controlTest = $this->controlTestModel->getByControlAndTest($control['control_id'], $test);
|
|
$results = $this->resultModel->getByMonth($control['control_id'], $test, $dates);
|
|
$comment = $this->commentModel->getByControlTestMonth($control['control_id'], $test, $dates);
|
|
$testInfo = $this->dictTestModel->find($test);
|
|
|
|
$outOfRangeCount = 0;
|
|
$processedResults = [];
|
|
if ($controlTest && $controlTest['sd'] > 0) {
|
|
foreach ($results as $res) {
|
|
$zScore = ($res['resvalue'] - $controlTest['mean']) / $controlTest['sd'];
|
|
$outOfRange = abs($zScore) > 2;
|
|
if ($outOfRange) $outOfRangeCount++;
|
|
|
|
$processedResults[] = [
|
|
'resdate' => $res['resdate'],
|
|
'resvalue' => $res['resvalue'],
|
|
'zScore' => round($zScore, 2),
|
|
'outOfRange' => $outOfRange,
|
|
'status' => $zScore === null ? '-' : (abs($zScore) > 2 ? 'Out' : (abs($zScore) > 1 ? 'Warn' : 'OK'))
|
|
];
|
|
}
|
|
} else {
|
|
foreach ($results as $res) {
|
|
$processedResults[] = [
|
|
'resdate' => $res['resdate'],
|
|
'resvalue' => $res['resvalue'],
|
|
'zScore' => null,
|
|
'outOfRange' => false,
|
|
'status' => '-'
|
|
];
|
|
}
|
|
}
|
|
|
|
$daysInMonth = date('t', strtotime($dates . '-01'));
|
|
$values = [];
|
|
for ($day = 1; $day <= $daysInMonth; $day++) {
|
|
$value = null;
|
|
foreach ($processedResults as $res) {
|
|
if (date('j', strtotime($res['resdate'])) == $day) {
|
|
$value = $res['resvalue'];
|
|
break;
|
|
}
|
|
}
|
|
$values[] = $value;
|
|
}
|
|
|
|
$reportData[] = [
|
|
'control' => $control,
|
|
'controlTest' => $controlTest,
|
|
'results' => $processedResults,
|
|
'values' => $values,
|
|
'test' => $testInfo,
|
|
'comment' => $comment,
|
|
'outOfRange' => $outOfRangeCount
|
|
];
|
|
}
|
|
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'message' => 'fetch success',
|
|
'data' => [
|
|
'reportData' => $reportData,
|
|
'dates' => $dates,
|
|
'test' => $test,
|
|
'daysInMonth' => $daysInMonth
|
|
]
|
|
], 200);
|
|
} catch (\Exception $e) {
|
|
return $this->failServerError('Something went wrong: ' . $e->getMessage());
|
|
}
|
|
}
|
|
}
|