diff --git a/AGENTS.md b/AGENTS.md index 33402be..aeeb2a2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -138,12 +138,4 @@ eleventyConfig.addFilter("dateFormat", (date, format = "full") => { - Files use `.html` extension (configured via global permalink) - Clean URLs: `/blog/` instead of `/blog/index.html` -- Nested folders auto-generate index pages - -## Communication Style - -When interacting with the user: -- Address them professionally as "commander" -- Use space/sci-fi themed language when appropriate -- Start with basmalah, end with hamdalah -- Be concise and await orders +- Nested folders auto-generate index pages \ No newline at end of file diff --git a/src/projects/clqms01/007-calculator-operators.md b/src/projects/clqms01/007-test-calc-engine.md similarity index 50% rename from src/projects/clqms01/007-calculator-operators.md rename to src/projects/clqms01/007-test-calc-engine.md index bb1ef5c..29cd3b0 100644 --- a/src/projects/clqms01/007-calculator-operators.md +++ b/src/projects/clqms01/007-test-calc-engine.md @@ -2,8 +2,8 @@ layout: clqms-post.njk tags: clqms title: "Calculator Service Operators Reference" -description: "Complete reference for mathematical operators, functions, and constants available in the CalculatorService." -date: 2026-03-16 +description: "Operators, functions, constants, and API usage for the calc engine" +date: 2026-03-17 order: 7 --- @@ -11,7 +11,68 @@ order: 7 ## Overview -The `CalculatorService` (`app/Services/CalculatorService.php`) uses the [mossadal/math-parser](https://github.com/mossadal/math-parser) library to safely evaluate mathematical expressions. This document lists all available operators, functions, and constants. +The `CalculatorService` (`app/Services/CalculatorService.php`) evaluates formulas with Symfony's `ExpressionLanguage`. This document lists the operators, functions, and constants that are available in the current implementation. + +--- + +## API Endpoints + +All endpoints live under `/api` and accept JSON. Responses use the standard `{ status, message, data }` envelope unless stated otherwise. + +### Calculate By Test Site + +Uses the `testdefcal` definition for a test site. The incoming body supplies the variables required by the formula. + +```http +POST /api/calc/testsite/123 +Content-Type: application/json + +{ + "result": 85, + "gender": "female", + "age": 30 +} +``` + +Response: + +```json +{ + "status": "success", + "data": { + "result": 92.4, + "testSiteID": 123, + "formula": "{result} * {factor} + {age}", + "variables": { + "result": 85, + "gender": "female", + "age": 30 + } + } +} +``` + +### Calculate By Code Or Name + +Evaluates a configured calculation by `TestSiteCode` or `TestSiteName`. Returns a compact map with a single key/value or `{}` on failure. + +```http +POST /api/calc/testcode/GLU +Content-Type: application/json + +{ + "result": 110, + "factor": 1.1 +} +``` + +Response: + +```json +{ + "GLU": 121 +} +``` --- @@ -25,9 +86,34 @@ The `CalculatorService` (`app/Services/CalculatorService.php`) uses the [mossada | `-` | Subtraction | `10 - 4` | `6` | | `*` | Multiplication | `6 * 7` | `42` | | `/` | Division | `20 / 4` | `5` | -| `^` | Exponentiation (power) | `2 ^ 3` | `8` | -| `!` | Factorial | `5!` | `120` | -| `!!` | Semi-factorial (double factorial) | `5!!` | `15` | +| `%` | Modulo | `20 % 6` | `2` | +| `**` | Exponentiation (power) | `2 ** 3` | `8` | + +### Comparison Operators + +| Operator | Description | Example | +|----------|-------------|---------| +| `==` | Equal | `{result} == 10` | +| `!=` | Not equal | `{result} != 10` | +| `<` | Less than | `{result} < 10` | +| `<=` | Less than or equal | `{result} <= 10` | +| `>` | Greater than | `{result} > 10` | +| `>=` | Greater than or equal | `{result} >= 10` | + +### Logical Operators + +| Operator | Description | Example | +|----------|-------------|---------| +| `and` / `&&` | Logical AND | `{result} > 0 and {factor} > 0` | +| `or` / `||` | Logical OR | `{gender} == 1 or {gender} == 2` | +| `!` / `not` | Logical NOT | `not ({result} > 0)` | + +### Conditional Operators + +| Operator | Description | Example | +|----------|-------------|---------| +| `?:` | Ternary | `{result} > 10 ? {result} : 10` | +| `??` | Null coalescing | `{result} ?? 0` | ### Parentheses @@ -38,84 +124,35 @@ Use parentheses to control operation precedence: 2 + 3 * 4 // Result: 14 ``` +### Notes + +- `^` is bitwise XOR (not exponentiation). Use `**` for powers. +- Variables must be numeric after normalization (gender is mapped to 0/1/2). + --- -## Mathematical Functions +## Functions -### Rounding Functions +Only the default ExpressionLanguage functions are available: | Function | Description | Example | |----------|-------------|---------| -| `sqrt(x)` | Square root | `sqrt(16)` → `4` | -| `round(x)` | Round to nearest integer | `round(3.7)` → `4` | -| `ceil(x)` | Round up to integer | `ceil(3.2)` → `4` | -| `floor(x)` | Round down to integer | `floor(3.9)` → `3` | -| `abs(x)` | Absolute value | `abs(-5)` → `5` | -| `sgn(x)` | Sign function | `sgn(-10)` → `-1` | - -### Trigonometric Functions (Radians) - -| Function | Description | Example | -|----------|-------------|---------| -| `sin(x)` | Sine | `sin(pi/2)` → `1` | -| `cos(x)` | Cosine | `cos(0)` → `1` | -| `tan(x)` | Tangent | `tan(pi/4)` → `1` | -| `cot(x)` | Cotangent | `cot(pi/4)` → `1` | - -### Trigonometric Functions (Degrees) - -| Function | Description | Example | -|----------|-------------|---------| -| `sind(x)` | Sine (degrees) | `sind(90)` → `1` | -| `cosd(x)` | Cosine (degrees) | `cosd(0)` → `1` | -| `tand(x)` | Tangent (degrees) | `tand(45)` → `1` | -| `cotd(x)` | Cotangent (degrees) | `cotd(45)` → `1` | - -### Hyperbolic Functions - -| Function | Description | Example | -|----------|-------------|---------| -| `sinh(x)` | Hyperbolic sine | `sinh(1)` → `1.175...` | -| `cosh(x)` | Hyperbolic cosine | `cosh(1)` → `1.543...` | -| `tanh(x)` | Hyperbolic tangent | `tanh(1)` → `0.761...` | -| `coth(x)` | Hyperbolic cotangent | `coth(2)` → `1.037...` | - -### Inverse Trigonometric Functions - -| Function | Aliases | Description | Example | -|----------|---------|-------------|---------| -| `arcsin(x)` | `asin(x)` | Inverse sine | `arcsin(0.5)` → `0.523...` | -| `arccos(x)` | `acos(x)` | Inverse cosine | `arccos(0.5)` → `1.047...` | -| `arctan(x)` | `atan(x)` | Inverse tangent | `arctan(1)` → `0.785...` | -| `arccot(x)` | `acot(x)` | Inverse cotangent | `arccot(1)` → `0.785...` | - -### Inverse Hyperbolic Functions - -| Function | Aliases | Description | Example | -|----------|---------|-------------|---------| -| `arsinh(x)` | `asinh(x)`, `arcsinh(x)` | Inverse hyperbolic sine | `arsinh(1)` → `0.881...` | -| `arcosh(x)` | `acosh(x)`, `arccosh(x)` | Inverse hyperbolic cosine | `arcosh(2)` → `1.316...` | -| `artanh(x)` | `atanh(x)`, `arctanh(x)` | Inverse hyperbolic tangent | `artanh(0.5)` → `0.549...` | -| `arcoth(x)` | `acoth(x)`, `arccoth(x)` | Inverse hyperbolic cotangent | `arcoth(2)` → `0.549...` | - -### Logarithmic & Exponential Functions - -| Function | Aliases | Description | Example | -|----------|---------|-------------|---------| -| `exp(x)` | - | Exponential (e^x) | `exp(2)` → `7.389...` | -| `log(x)` | `ln(x)` | Natural logarithm (base e) | `log(e)` → `1` | -| `log10(x)` | `lg(x)` | Logarithm base 10 | `log10(100)` → `2` | +| `min(a, b, ...)` | Minimum value | `min({result}, 10)` | +| `max(a, b, ...)` | Maximum value | `max({result}, 10)` | +| `constant(name)` | PHP constant by name | `constant("PHP_INT_MAX")` | +| `enum(name)` | PHP enum case by name | `enum("App\\Enum\\Status::Active")` | --- ## Constants -| Constant | Value | Description | Example | -|----------|-------|-------------|---------| -| `pi` | 3.14159265... | Ratio of circle circumference to diameter | `pi * r ^ 2` | -| `e` | 2.71828182... | Euler's number | `e ^ x` | -| `NAN` | Not a Number | Invalid mathematical result | - | -| `INF` | Infinity | Positive infinity | - | +ExpressionLanguage recognizes boolean and null literals: + +| Constant | Value | Description | +|----------|-------|-------------| +| `true` | `true` | Boolean true | +| `false` | `false` | Boolean false | +| `null` | `null` | Null value | --- @@ -148,16 +185,12 @@ Or use string values: `'unknown'`, `'female'`, `'male'` ## Implicit Multiplication -The parser supports implicit multiplication (no explicit `*` operator needed): +Implicit multiplication is not supported. Always use `*` between values: -| Expression | Parsed As | Result (x=2, y=3) | -|------------|-----------|-------------------| -| `2x` | `2 * x` | `4` | -| `x sin(x)` | `x * sin(x)` | `1.818...` | -| `2xy` | `2 * x * y` | `12` | -| `x^2y` | `x^2 * y` | `12` | - -**Note:** Implicit multiplication has the same precedence as explicit multiplication. `xy^2z` is parsed as `x*y^2*z`, NOT as `x*y^(2*z)`. +| Expression | Use Instead | +|------------|-------------| +| `2x` | `2 * x` | +| `{result}{factor}` | `{result} * {factor}` | --- @@ -174,13 +207,9 @@ $calculator = new CalculatorService(); $result = $calculator->calculate("5 + 3 * 2"); // Result: 11 -// Using functions -$result = $calculator->calculate("sqrt(16) + abs(-5)"); -// Result: 9 - -// Using constants -$result = $calculator->calculate("2 * pi * r", ['r' => 5]); -// Result: 31.415... +// Using min/max +$result = $calculator->calculate("max({result}, 10)", ['result' => 7]); +// Result: 10 ``` ### With Variables @@ -199,7 +228,7 @@ $result = $calculator->calculate($formula, $variables); ### BMI Calculation ```php -$formula = "{weight} / ({height} ^ 2)"; +$formula = "{weight} / ({height} ** 2)"; $variables = [ 'weight' => 70, // kg 'height' => 1.75 // meters @@ -226,8 +255,8 @@ $result = $calculator->calculate($formula, $variables); ### Complex Formula ```php -// Pythagorean theorem with rounding -$formula = "round(sqrt({a} ^ 2 + {b} ^ 2))"; +// Pythagorean theorem +$formula = "(({a} ** 2 + {b} ** 2) ** 0.5)"; $variables = [ 'a' => 3, 'b' => 4 @@ -313,6 +342,5 @@ Common errors: ## References -- [math-parser GitHub](https://github.com/mossadal/math-parser) -- [math-parser Documentation](http://mossadal.github.io/math-parser/) +- [Symfony ExpressionLanguage](https://symfony.com/doc/current/components/expression_language.html) - `app/Services/CalculatorService.php` diff --git a/src/projects/clqms01/009-audit-logging.md b/src/projects/clqms01/009-audit-logging.md new file mode 100644 index 0000000..f621b7b --- /dev/null +++ b/src/projects/clqms01/009-audit-logging.md @@ -0,0 +1,202 @@ +--- +layout: clqms-post.njk +tags: clqms +title: "Audit Logging" +description: "Unified audit logging model, event catalog, and capture rules for CLQMS." +date: 2026-03-17 +order: 9 +--- +# Audit Logging Strategy + +## Overview + +This document defines how CLQMS should capture audit and operational logs across four tables: + +- `logpatient` — patient, visit, and ADT activity +- `logorder` — orders, tests, specimens, results, and QC +- `logmaster` — master data and configuration changes +- `logsystem` — sessions, security, import/export, and system operations + +The intent is to audit all domains, including master data changes, and to standardize event capture so reporting and compliance are consistent. + +## Table Ownership + +| Event | Table | +| --- | --- | +| Patient registered/updated/merged | `logpatient` | +| Insurance/consent changed | `logpatient` | +| Patient visit (admit/transfer/discharge) | `logpatient` | +| Order created/cancelled | `logorder` | +| Sample received/rejected | `logorder` | +| Result entered/verified/amended | `logorder` | +| Result released/retracted/corrected | `logorder` | +| QC result recorded | `logorder` | +| Test panel added/removed | `logmaster` | +| Reference range changed | `logmaster` | +| Analyzer config updated | `logmaster` | +| User role changed | `logmaster` | +| User login/logout | `logsystem` | +| Import/export job start/end | `logsystem` | + +## Standard Log Schema (Shared Columns) + +Use a shared schema for all four tables to keep instrumentation and reporting consistent. The legacy names below match existing patterns and can be reused. + +| Column | Description | +| --- | --- | +| `LogID` (PK) | Auto increment primary key per table (e.g., `LogPatientID`) | +| `TblName` | Source table name | +| `RecID` | Record ID of the entity | +| `FldName` | Field name that changed (nullable for bulk events) | +| `FldValuePrev` | Previous value (string or JSON) | +| `FldValueNew` | New value (string or JSON) | +| `UserID` | Acting user ID (nullable for system actions) | +| `SiteID` | Site context | +| `DIDType` | Device identifier type | +| `DID` | Device identifier | +| `MachineID` | Workstation or host identifier | +| `SessionID` | Session identifier | +| `AppID` | Client application ID | +| `ProcessID` | Process/workflow identifier | +| `WebPageID` | UI page/context (nullable) | +| `EventID` | Event code (see catalog) | +| `ActivityID` | Action code (create/update/delete/read/etc.) | +| `Reason` | User/system reason | +| `LogDate` | Timestamp of event | +| `Context` | JSON metadata (optional but recommended) | +| `IpAddress` | Remote IP (optional but recommended) | + +Recommended: keep a JSON string in `Context` for extra details (e.g., route, request id, batch id, error message). Use size limits to avoid oversized rows. + +## Event Catalog + +### logpatient + +**Patient core** + +- Register patient +- Update demographics +- Merge/unmerge/split +- Identity changes (MRN, external identifiers) +- Consent grant/revoke/update +- Insurance add/update/remove +- Patient record view (if required by compliance) + +**Visit/ADT** + +- Admit, transfer, discharge +- Bed/ward/unit changes +- Visit status updates + +**Other** + +- Patient notes/attachments added/removed +- Patient alerts/flags changes + +### logorder + +**Orders/tests** + +- Create/cancel/reopen order +- Add/remove tests +- Priority changes +- Order comments added/removed + +**Specimen lifecycle** + +- Collected, labeled, received, rejected +- Centrifuged, aliquoted, stored +- Disposed/expired + +**Results** + +- Result entered/updated +- Verified/amended +- Released/retracted/corrected +- Result comments/interpretation changes +- Auto-verification override + +**QC** + +- QC result recorded +- QC failure/override + +### logmaster + +**Value sets** + +- Create/update/retire value set items + +**Test definitions** + +- Test definition updates (units, methods, ranges) +- Reference range changes +- Formula/delta check changes +- Test panel membership add/remove + +**Infrastructure** + +- Analyzer/instrument config changes +- Host app integration config +- Coding system changes + +**Users/roles** + +- User create/disable/reset +- Role changes +- Permission changes + +**Sites/workstations** + +- Site/location/workstation CRUD + +### logsystem + +**Sessions & security** + +- Login/logout +- Failed login attempts +- Lockouts/password resets +- Token issue/refresh/revoke +- Authorization failures + +**Import/export** + +- Import/export job start/end +- Batch ID, source, record counts, status + +**System operations** + +- Background jobs start/end +- Integration sync runs +- System config changes +- Service errors that affect data integrity + +## Activity & Event Codes + +Use consistent `ActivityID` and `EventID` values. Recommended defaults: + +- `ActivityID`: `CREATE`, `UPDATE`, `DELETE`, `READ`, `MERGE`, `SPLIT`, `CANCEL`, `REOPEN`, `VERIFY`, `AMEND`, `RETRACT`, `RELEASE`, `IMPORT`, `EXPORT`, `LOGIN`, `LOGOUT` +- `EventID`: domain-specific codes (e.g., `PATIENT_REGISTERED`, `ORDER_CREATED`, `RESULT_VERIFIED`, `QC_RECORDED`) + +## Capture Guidelines + +- Always capture `UserID`, `SessionID`, `SiteID`, and `LogDate` when available. +- If the action is system-driven, set `UserID` to `SYSTEM` (or null) and add context in `Context`. +- Store payload diffs in `FldValuePrev` and `FldValueNew` for single-field changes; for multi-field changes, put a JSON diff in `Context` and leave `FldName` null. +- For bulk operations, store batch metadata in `Context` (`batch_id`, `record_count`, `source`). +- Do not log secrets, tokens, or full PHI when not required. Mask or omit sensitive fields. + +## Retention & Governance + +- Define retention policy per table (e.g., 7 years for patient/order, 2 years for system). +- Archive before purge; record purge activity in `logsystem`. +- Restrict write/delete permissions to service accounts only. + +## Implementation Checklist + +1. Create the four tables with shared schema (or migrate existing log tables to match). +2. Add a single audit service with helpers to build a normalized payload. +3. Instrument controllers/services for each event category above. +4. Add automated tests for representative audit writes. +5. Document `EventID` codes used by each endpoint/service. diff --git a/src/projects/clqms01/index.njk b/src/projects/clqms01/index.njk index 66862aa..1c2576c 100644 --- a/src/projects/clqms01/index.njk +++ b/src/projects/clqms01/index.njk @@ -92,7 +92,7 @@ order: 0 # Quick Access -
+
🏗️ @@ -100,6 +100,13 @@ order: 0

Architecture, auth, and technical docs

+ +
+ 🧾 +

Audit Logging

+
+

Event catalog, schema, and capture rules

+
💡 @@ -124,6 +131,17 @@ order: 0 Core Documentation
+ +
+
+

Calculator Service Operators Reference

+

+ Operators, functions, constants, and API usage for the calc engine. +

+
+ +
+
{% for post in collections.clqms %} {% set is_suggestion = "/suggestion/" in post.url %} {% set is_review = "/review/" in post.url %}