chore: cleanup old files and Serena config
This commit is contained in:
parent
3ab2258b1b
commit
643413c8d3
1
.serena/.gitignore
vendored
1
.serena/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/cache
|
|
||||||
@ -1,103 +0,0 @@
|
|||||||
# Coding Conventions & Standards
|
|
||||||
|
|
||||||
## PHP Standards
|
|
||||||
- PHP 8.1+ features (typed properties, match expressions)
|
|
||||||
- Always declare return types for public methods
|
|
||||||
- No comments unless explaining complex logic
|
|
||||||
- Use `esc()` when outputting user data in views
|
|
||||||
|
|
||||||
## Naming Conventions
|
|
||||||
|
|
||||||
| Type | Convention | Example |
|
|
||||||
|------|------------|---------|
|
|
||||||
| Classes | PascalCase | `Admin`, `UserController` |
|
|
||||||
| Methods/Variables | camelCase | `getUsers()`, `$userId` |
|
|
||||||
| Constants | UPPER_SNAKE_CASE | `DB_HOST` |
|
|
||||||
| Database tables | UPPER_SNAKE_CASE | `GDC_CMOD.dbo.USERS` |
|
|
||||||
| Views | lowercase_underscores | `admin/index.php` |
|
|
||||||
| Routes | lowercase | `/admin/users` |
|
|
||||||
|
|
||||||
## Controller Patterns
|
|
||||||
|
|
||||||
```php
|
|
||||||
namespace App\Controllers;
|
|
||||||
|
|
||||||
class Admin extends BaseController {
|
|
||||||
public function index() { }
|
|
||||||
}
|
|
||||||
|
|
||||||
// API Controllers use ResponseTrait
|
|
||||||
use App\Controllers\BaseController;
|
|
||||||
use CodeIgniter\API\ResponseTrait;
|
|
||||||
|
|
||||||
class Users extends BaseController {
|
|
||||||
use ResponseTrait;
|
|
||||||
protected $db;
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
$this->db = \Config\Database::connect();
|
|
||||||
helper(['url', 'form', 'text']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Database Operations
|
|
||||||
|
|
||||||
```php
|
|
||||||
$this->db = \Config\Database::connect();
|
|
||||||
|
|
||||||
// Parameterized queries only
|
|
||||||
$query = $this->db->query("SELECT * FROM table WHERE id = ?", [$id]);
|
|
||||||
$row = $query->getRowArray();
|
|
||||||
$results = $query->getResultArray();
|
|
||||||
|
|
||||||
// Transactions
|
|
||||||
$this->db->transBegin();
|
|
||||||
try {
|
|
||||||
$this->db->query("INSERT INTO ...", [$data]);
|
|
||||||
$this->db->transCommit();
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
$this->db->transRollback();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Request/Response Patterns
|
|
||||||
|
|
||||||
```php
|
|
||||||
// GET input
|
|
||||||
$date1 = $this->request->getVar('date1') ?? date('Y-m-d');
|
|
||||||
|
|
||||||
// POST JSON
|
|
||||||
$input = $this->request->getJSON(true);
|
|
||||||
|
|
||||||
// JSON response
|
|
||||||
return $this->respond(['data' => $results]);
|
|
||||||
return $this->response->setJSON(['message' => 'Success']);
|
|
||||||
|
|
||||||
// View response
|
|
||||||
return view('admin/index', $data);
|
|
||||||
|
|
||||||
// Redirect with errors
|
|
||||||
return redirect()->back()->with('errors', ['key' => 'message']);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Session Structure
|
|
||||||
|
|
||||||
```php
|
|
||||||
session()->set([
|
|
||||||
'isLoggedIn' => true,
|
|
||||||
'userid' => (string) $user['USERID'],
|
|
||||||
'userroleid' => (int) $user['USERROLEID'],
|
|
||||||
'userrole' => (string) $role,
|
|
||||||
]);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Security Guidelines
|
|
||||||
- Use parameterized queries (never interpolate directly)
|
|
||||||
- Hash passwords with `password_hash()` / `password_verify()`
|
|
||||||
- Validate and sanitize all input before use
|
|
||||||
- Use `esc()` when outputting user data in views
|
|
||||||
|
|
||||||
## Validation Endpoints
|
|
||||||
- `POST /api/{resource}/validate/{id}` - validate a record
|
|
||||||
- `DELETE /api/{resource}/validate/{id}` - unvalidate a record
|
|
||||||
@ -1,134 +0,0 @@
|
|||||||
# Development Commands
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
```bash
|
|
||||||
# Run all tests
|
|
||||||
composer test
|
|
||||||
./vendor/bin/phpunit
|
|
||||||
|
|
||||||
# Run single test file
|
|
||||||
./vendor/bin/phpunit tests/unit/HealthTest.php
|
|
||||||
|
|
||||||
# Run single test method
|
|
||||||
./vendor/bin/phpunit tests/unit/HealthTest.php --filter testIsDefinedAppPath
|
|
||||||
|
|
||||||
# Generate code coverage report
|
|
||||||
./vendor/bin/phpunit --colors --coverage-text=tests/coverage.txt --coverage-html=tests/coverage/ -d memory_limit=1024m
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development Server
|
|
||||||
```bash
|
|
||||||
# Start development server
|
|
||||||
php spark serve
|
|
||||||
```
|
|
||||||
|
|
||||||
## Code Generation (CLI)
|
|
||||||
```bash
|
|
||||||
# Create controller
|
|
||||||
php spark make:controller Admin
|
|
||||||
|
|
||||||
# Create model
|
|
||||||
php spark make:model User
|
|
||||||
```
|
|
||||||
|
|
||||||
## Routing
|
|
||||||
```bash
|
|
||||||
# List all routes
|
|
||||||
php spark list
|
|
||||||
```
|
|
||||||
|
|
||||||
## Git Commands (Windows)
|
|
||||||
```bash
|
|
||||||
# Check git status
|
|
||||||
git status
|
|
||||||
|
|
||||||
# Check git diff
|
|
||||||
git diff
|
|
||||||
|
|
||||||
# View recent commits
|
|
||||||
git log
|
|
||||||
|
|
||||||
# Add files to staging
|
|
||||||
git add .
|
|
||||||
|
|
||||||
# Create commit
|
|
||||||
git commit -m "message"
|
|
||||||
|
|
||||||
# Push to remote
|
|
||||||
git push
|
|
||||||
|
|
||||||
# Pull from remote
|
|
||||||
git pull
|
|
||||||
```
|
|
||||||
|
|
||||||
## File System Commands (Windows)
|
|
||||||
```bash
|
|
||||||
# List directory contents
|
|
||||||
ls
|
|
||||||
dir
|
|
||||||
|
|
||||||
# Change directory
|
|
||||||
cd directory_name
|
|
||||||
|
|
||||||
# Create directory
|
|
||||||
mkdir directory_name
|
|
||||||
|
|
||||||
# Copy files
|
|
||||||
copy source destination
|
|
||||||
|
|
||||||
# Move files
|
|
||||||
move source destination
|
|
||||||
|
|
||||||
# Remove files
|
|
||||||
del filename
|
|
||||||
rm filename
|
|
||||||
|
|
||||||
# Find files by name
|
|
||||||
dir /s /b filename
|
|
||||||
|
|
||||||
# Search content in files
|
|
||||||
findstr /s /i "search_term" *.php
|
|
||||||
```
|
|
||||||
|
|
||||||
## CodeIgniter-Specific
|
|
||||||
```bash
|
|
||||||
# Clear cache
|
|
||||||
php spark cache:clear
|
|
||||||
|
|
||||||
# Clear view cache
|
|
||||||
php spark cache:clear
|
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
composer install
|
|
||||||
composer update
|
|
||||||
|
|
||||||
# Dump autoload
|
|
||||||
composer dump-autoload
|
|
||||||
```
|
|
||||||
|
|
||||||
## What to do after completing a task
|
|
||||||
|
|
||||||
1. **Run tests**
|
|
||||||
```bash
|
|
||||||
composer test
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Check for errors**
|
|
||||||
- Review any output from tests
|
|
||||||
- Check for lint errors or warnings
|
|
||||||
|
|
||||||
3. **Verify the changes**
|
|
||||||
- Test the functionality manually
|
|
||||||
- Check if any routes or filters were affected
|
|
||||||
|
|
||||||
4. **Update documentation** (if needed)
|
|
||||||
- Add new routes to documentation
|
|
||||||
- Update CHECKLIST.md if it's a new feature
|
|
||||||
|
|
||||||
5. **Commit changes** (only when explicitly asked)
|
|
||||||
```bash
|
|
||||||
git add .
|
|
||||||
git commit -m "describe changes"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Important**: Never commit changes unless explicitly asked by the user.
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
# GDC CMOD - Laboratory Management System
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
CodeIgniter 4 PHP application for laboratory management (GDC CMOD - Laboratory Request Management System). Handles specimen collection tracking, request validation, and result management with role-based access control.
|
|
||||||
|
|
||||||
## Tech Stack
|
|
||||||
- **Framework**: CodeIgniter 4 (PHP 8.1+)
|
|
||||||
- **Database**: SQL Server (primary) + Firebird/InterBase (legacy patient data via ODBC)
|
|
||||||
- **Frontend**: TailwindCSS + DaisyUI 5 + Alpine.js + Font Awesome 7
|
|
||||||
- **Testing**: PHPUnit 10.x
|
|
||||||
- **Server**: Windows (XAMPP/IIS)
|
|
||||||
|
|
||||||
## Database Architecture
|
|
||||||
- **Primary DB**: SQL Server (`GDC_CMOD.dbo`) via Microsoft ODBC Driver (MSOLEDBSQL)
|
|
||||||
- **Legacy DB**: Firebird/InterBase (`GLENEAGLES` via ODBC)
|
|
||||||
- **Connection**: `\Config\Database::connect()` returns MySQLi connection (configured as SQLSRV)
|
|
||||||
- **No CI4 Models**: Uses raw SQL queries via `Database::connect()->query()`
|
|
||||||
|
|
||||||
## Key Database Tables
|
|
||||||
- `GDC_CMOD.dbo.USERS` - Users with `USERID`, `USERROLEID`, `PASSWORD`
|
|
||||||
- `GDC_CMOD.dbo.CM_REQUESTS` - Validation tracking (`ISVAL1`, `ISVAL2`, validation users/dates)
|
|
||||||
- `GDC_CMOD.dbo.TUBES` - Sample collection status
|
|
||||||
- `GDC_CMOD.dbo.V_DASHBOARD_DEV` - Dashboard data view
|
|
||||||
- `glendb.dbo.*` - Legacy Firebird patient data
|
|
||||||
|
|
||||||
## Architecture Overview
|
|
||||||
- **Session-based authentication** with role-based access control (RBAC)
|
|
||||||
- **Dual-level validation** system requiring 2 different users to validate the same request
|
|
||||||
- **Role groups** filter middleware for routing control
|
|
||||||
- **API endpoints** for JSON responses
|
|
||||||
- **Page controllers** for HTML views
|
|
||||||
|
|
||||||
## Current Pending Features (CHECKLIST.md)
|
|
||||||
- Restrict Print/Save-to-PDF to CS Role only
|
|
||||||
- Add Dedicated Print Button
|
|
||||||
- Update PDF Report Metadata (Replace 'Printed By' with validating user's name)
|
|
||||||
- Reprint Label functionality
|
|
||||||
- Print Result Audit (Track when result reports are printed/exported)
|
|
||||||
@ -1,107 +0,0 @@
|
|||||||
# Project Structure
|
|
||||||
|
|
||||||
## Directory Layout
|
|
||||||
```
|
|
||||||
gdc_cmod/
|
|
||||||
├── app/ # Application code
|
|
||||||
│ ├── Controllers/ # Controllers (API & Pages)
|
|
||||||
│ │ ├── Pages/ # Page controllers by role
|
|
||||||
│ │ │ ├── AdminController.php
|
|
||||||
│ │ │ ├── CsController.php
|
|
||||||
│ │ │ ├── LabController.php
|
|
||||||
│ │ │ ├── PhlebotomistController.php
|
|
||||||
│ │ │ └── SuperuserController.php
|
|
||||||
│ │ ├── ApiDashboard.php
|
|
||||||
│ │ ├── ApiRequestsAuditController.php
|
|
||||||
│ │ ├── ApiValidateController.php
|
|
||||||
│ │ ├── AuthController.php
|
|
||||||
│ │ ├── BaseController.php
|
|
||||||
│ │ ├── ErrorPage.php
|
|
||||||
│ │ ├── Home.php
|
|
||||||
│ │ ├── LabelController.php
|
|
||||||
│ │ ├── RequestsController.php
|
|
||||||
│ │ ├── SamplesController.php
|
|
||||||
│ │ └── UsersController.php
|
|
||||||
│ ├── Config/ # Configuration
|
|
||||||
│ │ ├── App.php
|
|
||||||
│ │ ├── Database.php
|
|
||||||
│ │ ├── Filters.php # Filter definitions
|
|
||||||
│ │ ├── Routes.php # Route definitions
|
|
||||||
│ │ └── ...
|
|
||||||
│ ├── Database/ # Database configurations
|
|
||||||
│ ├── Filters/ # Custom filters
|
|
||||||
│ │ ├── GuestFilter.php
|
|
||||||
│ │ ├── RoleFilter.php
|
|
||||||
│ │ └── .gitkeep
|
|
||||||
│ ├── Helpers/ # Helper functions
|
|
||||||
│ ├── Language/ # Language files
|
|
||||||
│ ├── Libraries/ # Custom libraries
|
|
||||||
│ ├── Models/ # Models (none used in this project)
|
|
||||||
│ ├── ThirdParty/ # Third-party code
|
|
||||||
│ └── Views/ # Views
|
|
||||||
│ ├── admin/ # Admin views
|
|
||||||
│ ├── cs/ # CS views
|
|
||||||
│ ├── errors/ # Error pages
|
|
||||||
│ ├── lab/ # Lab views
|
|
||||||
│ ├── phlebo/ # Phlebo views
|
|
||||||
│ ├── shared/ # Shared components
|
|
||||||
│ ├── superuser/ # Superuser views
|
|
||||||
│ └── ...
|
|
||||||
├── public/ # Web root
|
|
||||||
│ ├── index.php # Front controller
|
|
||||||
│ ├── .htaccess
|
|
||||||
│ ├── web.config
|
|
||||||
│ ├── css/ # Local CSS
|
|
||||||
│ └── js/ # Local JavaScript
|
|
||||||
├── tests/ # PHPUnit tests
|
|
||||||
│ ├── _support/ # Test support files
|
|
||||||
│ ├── database/ # Database tests
|
|
||||||
│ ├── session/ # Session tests
|
|
||||||
│ ├── unit/ # Unit tests
|
|
||||||
│ └── README.md
|
|
||||||
├── writable/ # Writeable directories
|
|
||||||
├── vendor/ # Composer dependencies
|
|
||||||
├── .env # Environment variables (git ignored)
|
|
||||||
├── env # Example environment file
|
|
||||||
├── .gitignore # Git ignore rules
|
|
||||||
├── AGENTS.md # Agent guidelines (this file)
|
|
||||||
├── CHECKLIST.md # Feature checklist
|
|
||||||
├── CLAUDE.md # Claude Code guidelines
|
|
||||||
├── composer.json # Composer dependencies
|
|
||||||
├── composer.lock # Locked dependencies
|
|
||||||
├── phpunit.xml.dist # PHPUnit configuration
|
|
||||||
├── preload.php # PHP OpCache preload
|
|
||||||
├── README.md # Project README
|
|
||||||
├── spark # CodeIgniter CLI tool
|
|
||||||
└── LICENSE # MIT License
|
|
||||||
```
|
|
||||||
|
|
||||||
## Key Files & Their Purposes
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
- **app/Config/Database.php** - Database connections (SQL Server + Firebird ODBC)
|
|
||||||
- **app/Config/Filters.php** - Filter definitions (RoleFilter, GuestFilter)
|
|
||||||
- **app/Config/Routes.php** - Route definitions (API and page routes)
|
|
||||||
- **app/Config/App.php** - Application settings (baseURL, etc.)
|
|
||||||
|
|
||||||
### Controllers
|
|
||||||
- **Pages/[Role]Controller.php** - Page controllers for each role
|
|
||||||
- **Api[Resource]Controller.php** - API controllers for JSON endpoints
|
|
||||||
- **AuthController.php** - Authentication (login/logout)
|
|
||||||
- **UsersController.php** - User management API
|
|
||||||
- **RequestsController.php** - Request validation API
|
|
||||||
- **SamplesController.php** - Sample collection API
|
|
||||||
|
|
||||||
### Filters
|
|
||||||
- **app/Filters/RoleFilter.php** - Check user role on protected routes
|
|
||||||
- **app/Filters/GuestFilter.php** - Redirect logged-in users from public pages
|
|
||||||
|
|
||||||
### Views
|
|
||||||
- **views/shared/** - Shared components used across roles
|
|
||||||
- **views/{role}/** - Role-specific views
|
|
||||||
- **views/login.php** - Login page
|
|
||||||
|
|
||||||
### Tests
|
|
||||||
- **tests/unit/** - Unit tests (HealthTest, etc.)
|
|
||||||
- **tests/database/** - Database tests
|
|
||||||
- **tests/session/** - Session tests
|
|
||||||
@ -1,104 +0,0 @@
|
|||||||
# Role-Based Access Control (RBAC)
|
|
||||||
|
|
||||||
## Role Definitions
|
|
||||||
|
|
||||||
| Role ID | Name | Route Prefix | Permissions |
|
|
||||||
|---------|------|--------------|-------------|
|
|
||||||
| 0 | Superuser | `/superuser` | Full access + Users CRUD |
|
|
||||||
| 1 | Admin | `/admin` | Full access + Users CRUD |
|
|
||||||
| 2 | Lab | `/lab` | Request validation, Sample collection |
|
|
||||||
| 3 | Phlebo | `/phlebo` | Specimen collection, Dashboard |
|
|
||||||
| 4 | CS | `/cs` | Dashboard, Status Monitoring, Patient Inquiry |
|
|
||||||
|
|
||||||
## Route Filtering
|
|
||||||
|
|
||||||
### Role Filter
|
|
||||||
```php
|
|
||||||
// Single role
|
|
||||||
['filter' => 'role:1']
|
|
||||||
|
|
||||||
// Multiple roles
|
|
||||||
['filter' => 'role:1,2']
|
|
||||||
```
|
|
||||||
|
|
||||||
### Filter Usage
|
|
||||||
|
|
||||||
**app/Filters/RoleFilter.php**
|
|
||||||
- Checks `session()->get('isLoggedIn')` - redirects to `/login` if not logged in
|
|
||||||
- Checks role ID against allowed roles from route arguments
|
|
||||||
- Redirects to `/unauthorized` if role not authorized
|
|
||||||
|
|
||||||
**app/Filters/GuestFilter.php**
|
|
||||||
- Redirects logged-in users to role-based dashboard
|
|
||||||
- Use for public-only routes (e.g., `/login`)
|
|
||||||
|
|
||||||
## Route Prefixes & Controllers
|
|
||||||
|
|
||||||
### Superuser (Role 0)
|
|
||||||
- `/superuser` - Pages\SuperuserController::index
|
|
||||||
- `/superuser/users` - Pages\SuperuserController::users
|
|
||||||
- `/superuser/validate` - Pages\SuperuserController::validatePage
|
|
||||||
|
|
||||||
### Admin (Role 1)
|
|
||||||
- `/admin` - Pages\AdminController::index
|
|
||||||
- `/admin/users` - Pages\AdminController::users
|
|
||||||
- `/admin/validate` - Pages\AdminController::validationPage
|
|
||||||
|
|
||||||
### Lab (Role 2)
|
|
||||||
- `/lab` - Pages\LabController::index
|
|
||||||
- `/lab/validate` - Pages\LabController::validationPage
|
|
||||||
|
|
||||||
### Phlebo (Role 3)
|
|
||||||
- `/phlebo` - Pages\PhlebotomistController::index
|
|
||||||
|
|
||||||
### CS (Role 4)
|
|
||||||
- `/cs` - Pages\CsController::index
|
|
||||||
|
|
||||||
## Validation System (Dual-Level)
|
|
||||||
|
|
||||||
Validation requires 2 different users to validate the same request:
|
|
||||||
|
|
||||||
**First Validation:**
|
|
||||||
- Sets `ISVAL1=1`
|
|
||||||
- Records `VAL1USER` (username)
|
|
||||||
- Records `VAL1DATE` (datetime)
|
|
||||||
|
|
||||||
**Second Validation (different user):**
|
|
||||||
- Sets `ISVAL2=1`
|
|
||||||
- Records `VAL2USER` (username)
|
|
||||||
- Records `VAL2DATE` (datetime)
|
|
||||||
|
|
||||||
**Validation Permission:**
|
|
||||||
- Available to Role 0, 1, 2 (Superuser, Admin, Lab)
|
|
||||||
|
|
||||||
## Unvalidation
|
|
||||||
- Available to Role 0, 1 (Superuser, Admin)
|
|
||||||
- Sets `ISVAL1=0` and `ISVAL2=0`, clears validation user/date fields
|
|
||||||
|
|
||||||
## Authentication Flow
|
|
||||||
|
|
||||||
1. **AuthController::login()** - Verifies credentials against `GDC_CMOD.dbo.USERS`, sets session
|
|
||||||
2. **RoleFilter** - Runs on protected routes, checks `session()->get('isLoggedIn')` and role ID
|
|
||||||
3. **GuestFilter** - Runs on public routes, redirects logged-in users to dashboard
|
|
||||||
|
|
||||||
## API Endpoint Permissions
|
|
||||||
|
|
||||||
### Users Management
|
|
||||||
- **Access**: Role 0, 1 (Superuser, Admin)
|
|
||||||
- **Endpoints**: GET, POST, PATCH, DELETE on `/api/users`
|
|
||||||
|
|
||||||
### Requests
|
|
||||||
- **Access**: Role 0, 1, 2, 3, 4 (All Roles)
|
|
||||||
- **Endpoints**:
|
|
||||||
- `GET /api/requests` - Dashboard data
|
|
||||||
- `POST /api/requests/validate/:id` - Validate request
|
|
||||||
- `DELETE /api/requests/validate/:id` - Unvalidate request
|
|
||||||
- `GET /api/requests/:id/audit` - Audit trail
|
|
||||||
|
|
||||||
### Samples
|
|
||||||
- **Access**: All Roles for collect/show
|
|
||||||
- **Unreceive**: Role 0, 1 only
|
|
||||||
- **Endpoints**:
|
|
||||||
- `POST /api/samples/collect/:accessnumber` - Mark sample collected
|
|
||||||
- `GET /api/samples/:accessnumber` - Show sample info
|
|
||||||
- `DELETE /api/samples/receive/:accessnumber` - Unreceive sample
|
|
||||||
@ -1,136 +0,0 @@
|
|||||||
# Route Patterns
|
|
||||||
|
|
||||||
## Route File Structure
|
|
||||||
File: **app/Config/Routes.php**
|
|
||||||
|
|
||||||
## Public Routes
|
|
||||||
```php
|
|
||||||
$routes->get('/', 'Home::index');
|
|
||||||
$routes->get('/login', 'AuthController::loginPage', ['filter' => 'guest']);
|
|
||||||
$routes->post('/login', 'AuthController::login', ['filter' => 'guest']);
|
|
||||||
$routes->get('/logout', 'AuthController::logout');
|
|
||||||
$routes->patch('/setPassword', 'AuthController::setPassword');
|
|
||||||
```
|
|
||||||
|
|
||||||
## Label Routes (Public)
|
|
||||||
```php
|
|
||||||
$routes->get('label/coll/(:any)', 'LabelController::coll/$1');
|
|
||||||
$routes->get('label/dispatch/(:any)/(:any)', 'LabelController::dispatch/$1/$2');
|
|
||||||
$routes->get('label/all/(:any)', 'LabelController::print_all/$1');
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Routes (Protected by Role Filter)
|
|
||||||
File: **app/Config/Routes.php** - `$routes->group('api', ...)` block
|
|
||||||
|
|
||||||
### Users Management
|
|
||||||
```php
|
|
||||||
$routes->group('users', ['filter' => 'role:0,1'], function ($routes) {
|
|
||||||
$routes->get('', 'UsersController::index'); // Get all users
|
|
||||||
$routes->post('', 'UsersController::create'); // Create user
|
|
||||||
$routes->patch('(:any)', 'UsersController::update/$1'); // Update user
|
|
||||||
$routes->delete('(:any)', 'UsersController::delete/$1'); // Delete user
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Requests
|
|
||||||
```php
|
|
||||||
$routes->group('requests', ['filter' => 'role:0,1,2,3,4'], function ($routes) {
|
|
||||||
$routes->get('', 'RequestsController::index'); // Dashboard data
|
|
||||||
$routes->get('(:any)/audit', 'ApiRequestsAuditController::show/$1'); // Audit trail
|
|
||||||
$routes->post('validate/(:any)', 'RequestsController::val/$1'); // Validate request
|
|
||||||
$routes->delete('validate/(:any)', 'RequestsController::unval/$1'); // Unvalidate request
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Validate API
|
|
||||||
```php
|
|
||||||
$routes->group('validate', ['filter' => 'role:0,1,2'], function ($routes) {
|
|
||||||
$routes->get('unvalidated', 'ApiValidateController::unvalidated'); // Get unvalidated requests
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Samples
|
|
||||||
```php
|
|
||||||
$routes->group('samples', function ($routes) {
|
|
||||||
// Collect & Show - All Roles
|
|
||||||
$routes->group('', ['filter' => 'role:0,1,2,3,4'], function ($routes) {
|
|
||||||
$routes->post('collect/(:any)', 'SamplesController::collect/$1');
|
|
||||||
$routes->get('(:any)', 'SamplesController::show/$1');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Unreceive - Only Superuser (0) and Admin (1)
|
|
||||||
$routes->group('', ['filter' => 'role:0,1'], function ($routes) {
|
|
||||||
$routes->delete('receive/(:any)', 'SamplesController::unreceive/$1');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## Page Routes (Protected by Role Filter)
|
|
||||||
|
|
||||||
### Superuser (Role 0)
|
|
||||||
```php
|
|
||||||
$routes->group('superuser', ['filter' => 'role:0'], function ($routes) {
|
|
||||||
$routes->get('', 'Pages\SuperuserController::index');
|
|
||||||
$routes->get('users', 'Pages\SuperuserController::users');
|
|
||||||
$routes->get('validate', 'Pages\SuperuserController::validatePage');
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Admin (Role 1)
|
|
||||||
```php
|
|
||||||
$routes->group('admin', ['filter' => 'role:1'], function ($routes) {
|
|
||||||
$routes->get('', 'Pages\AdminController::index');
|
|
||||||
$routes->get('users', 'Pages\AdminController::users');
|
|
||||||
$routes->get('validate', 'Pages\AdminController::validationPage');
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Lab (Role 2)
|
|
||||||
```php
|
|
||||||
$routes->group('lab', ['filter' => 'role:2'], function ($routes) {
|
|
||||||
$routes->get('', 'Pages\LabController::index');
|
|
||||||
$routes->get('validate', 'Pages\LabController::validationPage');
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Phlebo (Role 3)
|
|
||||||
```php
|
|
||||||
$routes->group('phlebo', ['filter' => 'role:3'], function ($routes) {
|
|
||||||
$routes->get('', 'Pages\PhlebotomistController::index');
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### CS (Role 4)
|
|
||||||
```php
|
|
||||||
$routes->group('cs', ['filter' => 'role:4'], function ($routes) {
|
|
||||||
$routes->get('', 'Pages\CsController::index');
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
```php
|
|
||||||
$routes->set404Override(function () {
|
|
||||||
$response = service('response');
|
|
||||||
$response->setStatusCode(404);
|
|
||||||
echo view('errors/notfound');
|
|
||||||
});
|
|
||||||
$routes->get('/unauthorized', 'ErrorPage::unauthorized');
|
|
||||||
```
|
|
||||||
|
|
||||||
## Route Filter Order
|
|
||||||
1. **Before filters** (run before request):
|
|
||||||
- `forcehttps` - Force HTTPS
|
|
||||||
- `pagecache` - Web page caching
|
|
||||||
- `role` - Role-based access (on protected routes)
|
|
||||||
- `guest` - Guest-only (on public routes)
|
|
||||||
|
|
||||||
2. **After filters** (run after response):
|
|
||||||
- `pagecache` - Web page caching
|
|
||||||
- `performance` - Performance metrics
|
|
||||||
- `toolbar` - Debug toolbar
|
|
||||||
|
|
||||||
## Route Naming Conventions
|
|
||||||
- API endpoints: kebab-case, under `/api/` prefix
|
|
||||||
- Page routes: kebab-case, grouped by role
|
|
||||||
- Resource endpoints: standard REST pattern (`/resource/{id}`)
|
|
||||||
- Action endpoints: kebab-case, snake_case for parameters
|
|
||||||
@ -1,132 +0,0 @@
|
|||||||
# the name by which the project can be referenced within Serena
|
|
||||||
project_name: "gdc_cmod"
|
|
||||||
|
|
||||||
|
|
||||||
# list of languages for which language servers are started; choose from:
|
|
||||||
# al bash clojure cpp csharp
|
|
||||||
# csharp_omnisharp dart elixir elm erlang
|
|
||||||
# fortran fsharp go groovy haskell
|
|
||||||
# java julia kotlin lua markdown
|
|
||||||
# matlab nix pascal perl php
|
|
||||||
# powershell python python_jedi r rego
|
|
||||||
# ruby ruby_solargraph rust scala swift
|
|
||||||
# terraform toml typescript typescript_vts vue
|
|
||||||
# yaml zig
|
|
||||||
# (This list may be outdated. For the current list, see values of Language enum here:
|
|
||||||
# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py
|
|
||||||
# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.)
|
|
||||||
# Note:
|
|
||||||
# - For C, use cpp
|
|
||||||
# - For JavaScript, use typescript
|
|
||||||
# - For Free Pascal/Lazarus, use pascal
|
|
||||||
# Special requirements:
|
|
||||||
# Some languages require additional setup/installations.
|
|
||||||
# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers
|
|
||||||
# When using multiple languages, the first language server that supports a given file will be used for that file.
|
|
||||||
# The first language is the default language and the respective language server will be used as a fallback.
|
|
||||||
# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored.
|
|
||||||
languages:
|
|
||||||
- php
|
|
||||||
|
|
||||||
# the encoding used by text files in the project
|
|
||||||
# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings
|
|
||||||
encoding: "utf-8"
|
|
||||||
|
|
||||||
# whether to use project's .gitignore files to ignore files
|
|
||||||
ignore_all_files_in_gitignore: true
|
|
||||||
|
|
||||||
# list of additional paths to ignore in all projects
|
|
||||||
# same syntax as gitignore, so you can use * and **
|
|
||||||
ignored_paths: []
|
|
||||||
|
|
||||||
# whether the project is in read-only mode
|
|
||||||
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
|
|
||||||
# Added on 2025-04-18
|
|
||||||
read_only: false
|
|
||||||
|
|
||||||
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
|
|
||||||
# Below is the complete list of tools for convenience.
|
|
||||||
# To make sure you have the latest list of tools, and to view their descriptions,
|
|
||||||
# execute `uv run scripts/print_tool_overview.py`.
|
|
||||||
#
|
|
||||||
# * `activate_project`: Activates a project by name.
|
|
||||||
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
|
|
||||||
# * `create_text_file`: Creates/overwrites a file in the project directory.
|
|
||||||
# * `delete_lines`: Deletes a range of lines within a file.
|
|
||||||
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
|
|
||||||
# * `execute_shell_command`: Executes a shell command.
|
|
||||||
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
|
|
||||||
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
|
|
||||||
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
|
|
||||||
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
|
|
||||||
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
|
|
||||||
# * `initial_instructions`: Gets the initial instructions for the current project.
|
|
||||||
# Should only be used in settings where the system prompt cannot be set,
|
|
||||||
# e.g. in clients you have no control over, like Claude Desktop.
|
|
||||||
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
|
|
||||||
# * `insert_at_line`: Inserts content at a given line in a file.
|
|
||||||
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
|
|
||||||
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
|
|
||||||
# * `list_memories`: Lists memories in Serena's project-specific memory store.
|
|
||||||
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
|
|
||||||
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
|
|
||||||
# * `read_file`: Reads a file within the project directory.
|
|
||||||
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
|
|
||||||
# * `remove_project`: Removes a project from the Serena configuration.
|
|
||||||
# * `replace_lines`: Replaces a range of lines within a file with new content.
|
|
||||||
# * `replace_symbol_body`: Replaces the full definition of a symbol.
|
|
||||||
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
|
|
||||||
# * `search_for_pattern`: Performs a search for a pattern in the project.
|
|
||||||
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
|
|
||||||
# * `switch_modes`: Activates modes by providing a list of their names
|
|
||||||
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
|
|
||||||
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
|
|
||||||
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
|
|
||||||
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
|
|
||||||
excluded_tools: []
|
|
||||||
|
|
||||||
# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default)
|
|
||||||
included_optional_tools: []
|
|
||||||
|
|
||||||
# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools.
|
|
||||||
# This cannot be combined with non-empty excluded_tools or included_optional_tools.
|
|
||||||
fixed_tools: []
|
|
||||||
|
|
||||||
# list of mode names to that are always to be included in the set of active modes
|
|
||||||
# The full set of modes to be activated is base_modes + default_modes.
|
|
||||||
# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply.
|
|
||||||
# Otherwise, this setting overrides the global configuration.
|
|
||||||
# Set this to [] to disable base modes for this project.
|
|
||||||
# Set this to a list of mode names to always include the respective modes for this project.
|
|
||||||
base_modes:
|
|
||||||
|
|
||||||
# list of mode names that are to be activated by default.
|
|
||||||
# The full set of modes to be activated is base_modes + default_modes.
|
|
||||||
# If the setting is undefined, the default_modes from the global configuration (serena_config.yml) apply.
|
|
||||||
# Otherwise, this overrides the setting from the global configuration (serena_config.yml).
|
|
||||||
# This setting can, in turn, be overridden by CLI parameters (--mode).
|
|
||||||
default_modes:
|
|
||||||
|
|
||||||
# initial prompt for the project. It will always be given to the LLM upon activating the project
|
|
||||||
# (contrary to the memories, which are loaded on demand).
|
|
||||||
initial_prompt: ""
|
|
||||||
|
|
||||||
# override of the corresponding setting in serena_config.yml, see the documentation there.
|
|
||||||
# If null or missing, the value from the global config is used.
|
|
||||||
symbol_info_budget:
|
|
||||||
|
|
||||||
# The language backend to use for this project.
|
|
||||||
# If not set, the global setting from serena_config.yml is used.
|
|
||||||
# Valid values: LSP, JetBrains
|
|
||||||
# Note: the backend is fixed at startup. If a project with a different backend
|
|
||||||
# is activated post-init, an error will be returned.
|
|
||||||
language_backend:
|
|
||||||
|
|
||||||
# list of regex patterns which, when matched, mark a memory entry as read‑only.
|
|
||||||
# Extends the list from the global configuration, merging the two lists.
|
|
||||||
read_only_memory_patterns: []
|
|
||||||
|
|
||||||
# line ending convention to use when writing source files.
|
|
||||||
# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default)
|
|
||||||
# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings.
|
|
||||||
line_ending:
|
|
||||||
44
TODO.md
44
TODO.md
@ -1,44 +0,0 @@
|
|||||||
# Project Checklist: Glen RME & Lab Management System
|
|
||||||
|
|
||||||
**Last Updated:** 20260212
|
|
||||||
|
|
||||||
Pending:
|
|
||||||
- sample other for MCU is annoying
|
|
||||||
- report2 go to cmod
|
|
||||||
|
|
||||||
Completed:
|
|
||||||
- Update User Role levels (Standardize roles: Superuser, Admin, Lab, Phlebo, CS)
|
|
||||||
- Role-Based Dashboard Filtering (Filter by patient_status or service_type)
|
|
||||||
- Fix Table Sorting (Enable sorting by "No Register" and "Patient Name")
|
|
||||||
- Fix Language Toggle (Toggle lab result preview between Indonesian and English)
|
|
||||||
- Apply Row Color-Coding (Color-code "No Register" column)
|
|
||||||
- Initialize RME Sidebar Menu (Create menu items)
|
|
||||||
- Dashboard Performance (When getting data more than 100 rows, it load too slow)
|
|
||||||
- Dashboard for Lab -> no test with only number, remove request with empty test
|
|
||||||
- Dashboard for Others -> complete
|
|
||||||
- Refactor same views/*role* to views/shared
|
|
||||||
- Move all CDN to local
|
|
||||||
- Remove 'status' field on dashboard
|
|
||||||
- Restrict 'Validate' to Lab, Admin, Superuser
|
|
||||||
- Hide/Disable 'Validation' button after 2nd validation (Prevent redundant validation actions)
|
|
||||||
- Restrict 'UnValidate' to Admin, Superuser
|
|
||||||
- Remove 'UnCollect'
|
|
||||||
- Audit Trail (Track all actions: validation, unvalidation, collection, uncollection)
|
|
||||||
- Create Validate Page
|
|
||||||
- Sync color with old gdc_cmod
|
|
||||||
- Add Val1 Val2 on the result
|
|
||||||
- Show Print / PDF button when val1 val2 done
|
|
||||||
- Restrict Print/Save-to-PDF to CS Role only (Admin, Lab, CS can print/save)
|
|
||||||
- Add Receive to Audit
|
|
||||||
- Put all action to dropdown on request / dashboard
|
|
||||||
- Auto generate PDF on second val
|
|
||||||
- Validate delay when loading result
|
|
||||||
- Reprint Label (Add functionality to reprint labels)
|
|
||||||
- Create Eng Result UI UX on request dashboard
|
|
||||||
- Test and fix PDF Generation
|
|
||||||
- Print Result Audit (Track when result reports are printed/exported, log user and timestamp)
|
|
||||||
- Test and fix Reprint label
|
|
||||||
- fasten the load of val page
|
|
||||||
- preview result for validate for su adm lab
|
|
||||||
- auto generate pdf after 2 val from preview
|
|
||||||
- add datetime val1 val2
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
USE [GDC_CMOD]
|
|
||||||
GO
|
|
||||||
|
|
||||||
/****** Object: Table [dbo].[AUDIT_EVENTS] Script Date: 1/23/2026 4:38:31 PM ******/
|
|
||||||
SET ANSI_NULLS ON
|
|
||||||
GO
|
|
||||||
|
|
||||||
SET QUOTED_IDENTIFIER ON
|
|
||||||
GO
|
|
||||||
|
|
||||||
CREATE TABLE [dbo].[AUDIT_EVENTS](
|
|
||||||
[ACCESSNUMBER] [varchar](20) NOT NULL,
|
|
||||||
[EVENT_TYPE] [varchar](20) NOT NULL,
|
|
||||||
[USERID] [varchar](50) NOT NULL,
|
|
||||||
[EVENT_AT] [datetime] NOT NULL,
|
|
||||||
[REASON] [varchar](500) NULL,
|
|
||||||
CONSTRAINT [PK_AUDIT_EVENTS] PRIMARY KEY CLUSTERED
|
|
||||||
(
|
|
||||||
[ACCESSNUMBER] ASC,
|
|
||||||
[EVENT_TYPE] ASC,
|
|
||||||
[EVENT_AT] ASC
|
|
||||||
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
|
|
||||||
) ON [PRIMARY]
|
|
||||||
GO
|
|
||||||
|
|
||||||
ALTER TABLE [dbo].[AUDIT_EVENTS] ADD DEFAULT (getdate()) FOR [EVENT_AT]
|
|
||||||
GO
|
|
||||||
|
|
||||||
|
|
||||||
172
script.php
172
script.php
@ -1,172 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Batch PDF Generator CLI Script
|
|
||||||
* Uses curl to CI4 web endpoint (avoids slow CLI database queries)
|
|
||||||
*
|
|
||||||
* Usage: php script.php accessnumbers.txt
|
|
||||||
*
|
|
||||||
* Input file format: One accessnumber per line
|
|
||||||
* Example:
|
|
||||||
* 202403110001
|
|
||||||
* 202403110002
|
|
||||||
* 202403110003
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Check command line arguments
|
|
||||||
if ($argc < 2) {
|
|
||||||
echo "Usage: php script.php <accessnumbers_file>\n";
|
|
||||||
echo "Example: php script.php batch.txt\n\n";
|
|
||||||
echo "File format: One accessnumber per line\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$inputFile = $argv[1];
|
|
||||||
|
|
||||||
if (!file_exists($inputFile)) {
|
|
||||||
echo "Error: File not found: $inputFile\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base URL for CI4 endpoint
|
|
||||||
// Using batch/pdf endpoint (no auth required)
|
|
||||||
// Dev: localhost/gdc_cmod/ | Prod: glenlis/cmod/
|
|
||||||
$baseUrl = 'http://localhost/gdc_cmod/batch/pdf';
|
|
||||||
|
|
||||||
// Read accessnumbers from file
|
|
||||||
$lines = file($inputFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
|
||||||
if ($lines === false) {
|
|
||||||
echo "Error: Could not read file: $inputFile\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$accessnumbers = array_filter(array_map('trim', $lines));
|
|
||||||
$total = count($accessnumbers);
|
|
||||||
|
|
||||||
if ($total === 0) {
|
|
||||||
echo "Error: No accessnumbers found in file\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "Processing $inputFile\n";
|
|
||||||
echo "Found $total access number(s) via $baseUrl\n";
|
|
||||||
echo str_repeat("-", 60) . "\n";
|
|
||||||
|
|
||||||
// Statistics
|
|
||||||
$stats = [
|
|
||||||
'success' => 0,
|
|
||||||
'failed' => 0,
|
|
||||||
'details' => []
|
|
||||||
];
|
|
||||||
|
|
||||||
// Process each accessnumber
|
|
||||||
foreach ($accessnumbers as $index => $accessnumber) {
|
|
||||||
$current = $index + 1;
|
|
||||||
$startTime = microtime(true);
|
|
||||||
echo "[$current/$total] $accessnumber... ";
|
|
||||||
|
|
||||||
try {
|
|
||||||
$url = "$baseUrl/$accessnumber/pdf";
|
|
||||||
|
|
||||||
$ch = curl_init();
|
|
||||||
curl_setopt($ch, CURLOPT_URL, $url);
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
|
|
||||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
|
|
||||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
|
||||||
curl_setopt($ch, CURLOPT_HEADER, true); // Include headers in output
|
|
||||||
|
|
||||||
$response = curl_exec($ch);
|
|
||||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
$redirectUrl = curl_getinfo($ch, CURLINFO_REDIRECT_URL);
|
|
||||||
$finalUrl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
|
|
||||||
$curlError = curl_error($ch);
|
|
||||||
curl_close($ch);
|
|
||||||
|
|
||||||
if ($curlError) {
|
|
||||||
throw new \Exception("cURL error: $curlError");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug: Show redirect info
|
|
||||||
if ($httpCode !== 200) {
|
|
||||||
$debug = "HTTP $httpCode";
|
|
||||||
if ($redirectUrl) {
|
|
||||||
$debug .= " -> $redirectUrl";
|
|
||||||
}
|
|
||||||
if ($finalUrl !== $url) {
|
|
||||||
$debug .= " (final: $finalUrl)";
|
|
||||||
}
|
|
||||||
throw new \Exception($debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove headers from response body
|
|
||||||
$headerSize = strpos($response, "\r\n\r\n");
|
|
||||||
if ($headerSize !== false) {
|
|
||||||
$response = substr($response, $headerSize + 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = json_decode($response, true);
|
|
||||||
|
|
||||||
if (!isset($data['success']) || !$data['success']) {
|
|
||||||
$error = $data['error'] ?? 'Unknown error';
|
|
||||||
throw new \Exception($error);
|
|
||||||
}
|
|
||||||
|
|
||||||
$jobId = $data['jobId'] ?? 'unknown';
|
|
||||||
$lang = $data['lang'] ?? 'Unknown';
|
|
||||||
$isRegen = $data['isRegen'] ?? false;
|
|
||||||
$regenText = $isRegen ? ' (regen)' : '';
|
|
||||||
|
|
||||||
$elapsed = round((microtime(true) - $startTime) * 1000);
|
|
||||||
echo "QUEUED (job: $jobId, lang: $lang$regenText, ${elapsed}ms)\n";
|
|
||||||
|
|
||||||
$stats['success']++;
|
|
||||||
$stats['details'][] = [
|
|
||||||
'accessnumber' => $accessnumber,
|
|
||||||
'status' => 'success',
|
|
||||||
'jobId' => $jobId,
|
|
||||||
'language' => $lang,
|
|
||||||
'time_ms' => $elapsed
|
|
||||||
];
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$elapsed = round((microtime(true) - $startTime) * 1000);
|
|
||||||
echo "FAILED (" . $e->getMessage() . ", ${elapsed}ms)\n";
|
|
||||||
|
|
||||||
$stats['failed']++;
|
|
||||||
$stats['details'][] = [
|
|
||||||
'accessnumber' => $accessnumber,
|
|
||||||
'status' => 'failed',
|
|
||||||
'error' => $e->getMessage(),
|
|
||||||
'time_ms' => $elapsed
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
echo str_repeat("-", 60) . "\n";
|
|
||||||
echo "Complete: " . $stats['success'] . " queued, " . $stats['failed'] . " failed\n";
|
|
||||||
|
|
||||||
// Save detailed log
|
|
||||||
$logFile = 'batch_pdf_' . date('Ymd_His') . '.log';
|
|
||||||
$logContent = "Batch PDF Generation Log\n";
|
|
||||||
$logContent .= "Generated: " . date('Y-m-d H:i:s') . "\n";
|
|
||||||
$logContent .= "Input file: $inputFile\n";
|
|
||||||
$logContent .= "Endpoint: $baseUrl\n";
|
|
||||||
$logContent .= "Total: $total, Success: {$stats['success']}, Failed: {$stats['failed']}\n";
|
|
||||||
$logContent .= str_repeat("-", 60) . "\n";
|
|
||||||
|
|
||||||
foreach ($stats['details'] as $detail) {
|
|
||||||
$logContent .= $detail['accessnumber'] . " | " . $detail['status'];
|
|
||||||
if (isset($detail['jobId'])) {
|
|
||||||
$logContent .= " | job: " . $detail['jobId'];
|
|
||||||
$logContent .= " | lang: " . $detail['language'];
|
|
||||||
}
|
|
||||||
if (isset($detail['error'])) {
|
|
||||||
$logContent .= " | error: " . $detail['error'];
|
|
||||||
}
|
|
||||||
$logContent .= " | " . $detail['time_ms'] . "ms\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
file_put_contents($logFile, $logContent);
|
|
||||||
echo "Log saved to: $logFile\n";
|
|
||||||
|
|
||||||
exit($stats['failed'] > 0 ? 1 : 0);
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
202403110001
|
|
||||||
202403110002
|
|
||||||
Loading…
x
Reference in New Issue
Block a user