add valueset getter to valueset : , valuesetKey :
This commit is contained in:
parent
e96ffa1ca9
commit
823694e4a1
126
CLAUDE.md
Normal file
126
CLAUDE.md
Normal file
@ -0,0 +1,126 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
CLQMS is a Clinical Laboratory Quality Management System built with CodeIgniter 4 (PHP 8.1+) providing a REST API backend for laboratory operations from patient registration through test resulting. The frontend uses Alpine.js views in `app/Views/v2/`.
|
||||
|
||||
## Development Commands
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
composer install
|
||||
|
||||
# Run all tests
|
||||
composer test
|
||||
php vendor/bin/phpunit
|
||||
|
||||
# Run single test file
|
||||
php vendor/bin/phpunit tests/feature/Patients/PatientIndexTest.php
|
||||
|
||||
# Run single test method
|
||||
php vendor/bin/phpunit tests/feature/Patients/PatientIndexTest.php --filter=testIndexWithoutParams
|
||||
|
||||
# Run tests with coverage
|
||||
php vendor/bin/phpunit --coverage-html build/logs/html
|
||||
|
||||
# Run CLI commands
|
||||
php spark help
|
||||
php spark db:seed DBSeeder
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Directory Structure
|
||||
- `app/Controllers/{Domain}/` - API controllers organized by domain (Patient, Organization, Specimen, Result, Test)
|
||||
- `app/Models/{Domain}/` - Domain models
|
||||
- `app/Libraries/Data/*.json` - Static lookup data (gender, order_priority, specimen_type, etc.)
|
||||
- `app/Views/v2/` - Alpine.js frontend views
|
||||
- `app/Database/Migrations/` - 10 consolidated migrations (2026-01-01-*)
|
||||
- `tests/feature/` - API endpoint tests using `FeatureTestTrait`
|
||||
|
||||
### Key Patterns
|
||||
|
||||
**Controller Pattern:**
|
||||
```php
|
||||
class PatientController extends Controller {
|
||||
use ResponseTrait;
|
||||
protected $db;
|
||||
protected $model;
|
||||
|
||||
public function __construct() {
|
||||
$this->db = \Config\Database::connect();
|
||||
$this->model = new PatientModel();
|
||||
}
|
||||
|
||||
public function index() {
|
||||
try {
|
||||
$data = $this->model->findAll();
|
||||
return $this->respond(['status' => 'success', 'data' => $data], 200);
|
||||
} catch (\Exception $e) {
|
||||
return $this->failServerError($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Model Pattern:** Models extend `BaseModel` which auto-normalizes dates to/from UTC via `beforeInsert`/`beforeUpdate` callbacks. Use `allowedFields` for mass assignment and `useSoftDeletes = true` with `deletedField = 'DelDate'` for soft deletes.
|
||||
|
||||
**API Response Format:**
|
||||
```json
|
||||
// Success
|
||||
{"status": "success", "message": "...", "data": [...]}
|
||||
// Error
|
||||
{"status": "failed", "message": "..."}
|
||||
```
|
||||
|
||||
### Lookups Library
|
||||
|
||||
Use `App\Libraries\Lookups` for static dropdown values (loaded from JSON files, cached):
|
||||
```php
|
||||
use App\Libraries\Lookups;
|
||||
|
||||
// Get dropdown-formatted data [{value: '1', label: 'Female'}, ...]
|
||||
$gender = Lookups::get('gender');
|
||||
|
||||
// Get label by key
|
||||
$label = Lookups::getLabel('gender', '1'); // 'Female'
|
||||
|
||||
// Clear cache after modifying lookup data
|
||||
Lookups::clearCache();
|
||||
```
|
||||
|
||||
For dynamic values managed at runtime, use the `/api/valueset*` endpoints instead.
|
||||
|
||||
### JWT Authentication
|
||||
|
||||
Most API endpoints require JWT auth via `AuthFilter`. Public endpoints include `/v2/login`, `/api/demo/*`, `/api/auth/*`.
|
||||
|
||||
## Database Conventions
|
||||
|
||||
| Element | Convention |
|
||||
|---------|------------|
|
||||
| Tables | snake_case (`patient`, `patvisit`) |
|
||||
| Columns | PascalCase (`InternalPID`, `PatientID`) |
|
||||
| Classes | PascalCase (`PatientController`, `BaseModel`) |
|
||||
| Methods/Variables | camelCase (`getPatient()`, `$internalPID`) |
|
||||
|
||||
Soft deletes use `DelDate` field - never hard delete records.
|
||||
|
||||
## Key Routes
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| POST | `/api/auth/login` | JWT authentication |
|
||||
| GET/POST | `/api/patient` | Patient CRUD |
|
||||
| GET/POST | `/api/patvisit` | Patient visits |
|
||||
| POST | `/api/ordertest` | Create orders |
|
||||
| POST | `/api/edge/results` | Instrument integration (tiny-edge middleware) |
|
||||
|
||||
## Important Notes
|
||||
|
||||
- All dates normalized to UTC automatically via `BaseModel`
|
||||
- No comments in code unless explicitly requested
|
||||
- Use transactions for multi-table operations
|
||||
- Validate input before DB operations using CodeIgniter validation rules
|
||||
@ -158,12 +158,13 @@ class TestsController extends BaseController
|
||||
$row['refnum'] = array_map(function ($r) {
|
||||
return [
|
||||
'RefNumID' => $r['RefNumID'],
|
||||
'NumRefType' => $r['NumRefType'],
|
||||
'NumRefTypeVValue' => ValueSet::getLabel('numeric_ref_type', $r['NumRefType']),
|
||||
'RangeTypeVValue' => ValueSet::getLabel('range_type', $r['RangeType']),
|
||||
'SexVValue' => ValueSet::getLabel('gender', $r['Sex']),
|
||||
'LowSignVValue' => ValueSet::getLabel('math_sign', $r['LowSign']),
|
||||
'HighSignVValue' => ValueSet::getLabel('math_sign', $r['HighSign']),
|
||||
'NumRefTypeKey' => $r['NumRefType'],
|
||||
'NumRefType' => ValueSet::getLabel('numeric_ref_type', $r['NumRefType']),
|
||||
'RangeType' => ValueSet::getLabel('range_type', $r['RangeType']),
|
||||
'SexKey' => $r['Sex'],
|
||||
'Sex' => ValueSet::getLabel('gender', $r['Sex']),
|
||||
'LowSign' => ValueSet::getLabel('math_sign', $r['LowSign']),
|
||||
'HighSign' => ValueSet::getLabel('math_sign', $r['HighSign']),
|
||||
'High' => $r['High'] !== null ? (int) $r['High'] : null,
|
||||
'Flag' => $r['Flag']
|
||||
];
|
||||
@ -183,10 +184,10 @@ class TestsController extends BaseController
|
||||
$row['reftxt'] = array_map(function ($r) {
|
||||
return [
|
||||
'RefTxtID' => $r['RefTxtID'],
|
||||
'TxtRefType' => $r['TxtRefType'],
|
||||
'TxtRefTypeVValue' => ValueSet::getLabel('text_ref_type', $r['TxtRefType']),
|
||||
'Sex' => $r['Sex'],
|
||||
'SexVValue' => ValueSet::getLabel('gender', $r['Sex']),
|
||||
'TxtRefTypeKey' => $r['TxtRefType'],
|
||||
'TxtRefType' => ValueSet::getLabel('text_ref_type', $r['TxtRefType']),
|
||||
'SexKey' => $r['Sex'],
|
||||
'Sex' => ValueSet::getLabel('gender', $r['Sex']),
|
||||
'AgeStart' => (int) $r['AgeStart'],
|
||||
'AgeEnd' => (int) $r['AgeEnd'],
|
||||
'RefTxt' => $r['RefTxt'],
|
||||
|
||||
@ -68,7 +68,9 @@ class ValueSet {
|
||||
foreach ($data as &$row) {
|
||||
foreach ($fieldMappings as $field => $lookupName) {
|
||||
if (isset($row[$field]) && $row[$field] !== null) {
|
||||
$row[$field . 'Text'] = self::getLabel($lookupName, $row[$field]) ?? '';
|
||||
$keyValue = $row[$field];
|
||||
$row[$field . 'Key'] = $keyValue;
|
||||
$row[$field] = self::getLabel($lookupName, $keyValue) ?? '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ class PatientModel extends BaseModel {
|
||||
|
||||
$rows = $this->findAll();
|
||||
$rows = ValueSet::transformLabels($rows, [
|
||||
'Sex' => 'gender',
|
||||
'Sex' => 'sex',
|
||||
]);
|
||||
return $rows;
|
||||
}
|
||||
@ -81,7 +81,7 @@ class PatientModel extends BaseModel {
|
||||
unset($patient['Comment']);
|
||||
|
||||
$patient = ValueSet::transformLabels([$patient], [
|
||||
'Sex' => 'gender',
|
||||
'Sex' => 'sex',
|
||||
'Country' => 'country',
|
||||
'Race' => 'race',
|
||||
'Religion' => 'religion',
|
||||
|
||||
@ -358,14 +358,16 @@ class ValueSetTest extends CIUnitTestCase
|
||||
['Gender' => '1', 'Country' => 'ID'],
|
||||
['Gender' => '2', 'Country' => 'US']
|
||||
];
|
||||
|
||||
|
||||
$result = ValueSet::transformLabels($data, [
|
||||
'Gender' => 'sex',
|
||||
'Country' => 'country'
|
||||
]);
|
||||
|
||||
$this->assertEquals('Female', $result[0]['GenderText']);
|
||||
$this->assertEquals('Male', $result[1]['GenderText']);
|
||||
|
||||
$this->assertEquals('Female', $result[0]['Gender']);
|
||||
$this->assertEquals('1', $result[0]['GenderKey']);
|
||||
$this->assertEquals('Male', $result[1]['Gender']);
|
||||
$this->assertEquals('2', $result[1]['GenderKey']);
|
||||
}
|
||||
|
||||
public function testGetOptions()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user