- Implement Monthly Entry interface with full data entry grid
- Add batch save with validation and statistics for monthly results
- Support daily comments per day per test
- Add result status indicators and validation summaries
- Consolidate Entry API controller
- Refactor EntryApiController to handle both daily/monthly operations
- Add batch save endpoints with comprehensive validation
- Implement statistics calculation for result entries
- Add Control Test master data management
- Create MasterControlsController for CRUD operations
- Add dialog forms for control test configuration
- Implement control-test associations with QC parameters
- Refactor Report API and views
- Implement new report index with Levey-Jennings charts placeholder
- Add monthly report functionality with result statistics
- Include QC summary with mean, SD, and CV calculations
- UI improvements
- Overhaul dashboard with improved layout
- Update daily entry interface with inline editing
- Enhance master data management with DaisyUI components
- Add proper modal dialogs and form validation
- Database and seeding
- Update migration for control_tests table schema
- Remove redundant migration and seed files
- Update seeders with comprehensive test data
- Documentation
- Update CLAUDE.md with comprehensive project documentation
- Add architecture overview and conventions
BREAKING CHANGES:
- Refactored Entry API endpoints structure
- Removed ReportApiController::view() - consolidated into new report index
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['controlId'], $test);
|
|
$results = $this->resultModel->getByControlAndMonth($control['controlId'], $test, $dates);
|
|
$comment = $this->commentModel->getByControlTestMonth($control['controlId'], $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());
|
|
}
|
|
}
|
|
}
|