Document Type: Domain Design (Tier 2)
Domain: ANALYTICS, ANALYZER, PROGRESS
Domain Character: Stateful
SRS References: analytics.md, analyzer.md, progress.md
Status: Draft
Last Updated: 2026-01-25
1. Overview
1.1 Purpose
The Analytics domain encompasses the core analysis engine that processes PCR run data by executing configured rules against wells. This domain manages:
- Rule execution pipeline - Executes rules in precedence order, respecting error well handling and LIMS preventability
- Accession validation - Configurable validation of patient well data during import
- Progress tracking - Real-time progress feedback for long-running analysis operations with multi-user visibility
This is a stateful domain because analysis jobs progress through discrete lifecycle states (queued, running, completed, failed) and progress state is persisted across user sessions via cache.
1.2 Requirements Covered
| REQ ID | Title | Priority |
|---|
| REQ-ANALYTICS-001 | Execute Rules on Error Wells | Must |
| REQ-ANALYTICS-002 | Control Rule Execution Based on LIMS Preventability | Must |
| REQ-ANALYZER-001 | Configure Accession Validation Enforcement | Must |
| REQ-PROGRESS-001 | Display Progress for Long-Running Processes | Must |
| REQ-PROGRESS-002 | Persist Progress Tracking Across Navigation | Must |
| REQ-PROGRESS-003 | Support Multiple Concurrent Progress Indicators | Must |
| REQ-PROGRESS-004 | Display Progress to All Users Viewing Affected Resources | Must |
1.3 Constraints
Tier 2 Constraint: This document describes ownership, patterns, and design rationale. It links to reference docs for full schemas and API specifications. Rule-specific logic is documented in the rules/ subdirectory.
1.4 Dependencies
| Direction | Domain/Component | Purpose |
|---|
| Consumes | Run Model | Run context for analysis |
| Well Model | Wells to analyze |
| Observation Model | Per-target well observations |
| Rule Configuration | Configured rules with precedence |
| LimsStatus Model | LIMS outcome configuration |
| ClientConfiguration | Accession validation settings |
| Provides to | RUNRPT | Analysis results for display |
| NOTIF | Well status updates for notifications |
| REANALYZE | Re-analysis triggers for combined outcomes |
2. Component Architecture
2.1 Component Diagram
2.2 Component Responsibilities
| Component | Layer | Responsibility | REQ Trace |
|---|
RunAnalyseJob | Queue | Queues analysis for async processing; prevents overlapping | REQ-PROGRESS-001 |
UpdateRunAction | Action | Orchestrates full analysis pipeline: normalize, analyze, summarize, persist | All |
Analyzer | Core | Executes rules in precedence order against wells | REQ-ANALYTICS-001, 002 |
Well | Core | Abstract well state; tracks error codes, LIMS status, resolutions | REQ-ANALYTICS-001, 002 |
Observation | Core | Per-target observation data; checks LIMS preventability | REQ-ANALYTICS-002 |
PatientValidator | Validation | Validates patient wells during normalization | REQ-ANALYZER-001 |
HasValidAccession | Validation | Checks accession format against allowed characters | REQ-ANALYZER-001 |
RunAnalyseProgress | Progress | Cache-based progress tracking per run | REQ-PROGRESS-001, 002, 003 |
RunAnalyseProgressUpdated | Event | Broadcasts progress to all connected clients | REQ-PROGRESS-004 |
3. Data Design
3.1 Entities
This domain does not own persistent entities. It consumes and transforms data from RUNRPT, ERRORCODES, and CLIENTCFG domains.
| Entity | Owner | Usage in ANALYTICS |
|---|
runs | RUNRPT | Read run context, update analysis timestamp |
wells | RUNRPT | Read/update well state (error_code, lims_status, resolution_codes) |
observations | RUNRPT | Read/update observation classifications and CT values |
rules | RULES | Read rule configuration (precedence, allow_error_wells) |
lims_statuses | LIMS | Read LIMS outcome configuration (does_prevent_analyse) |
client_configurations | CLIENTCFG | Read bypass_accession_validation setting |
See Database Reference for full schema.
3.2 Data Structures
Analysis Pipeline Data Flow
interface AnalysisInput {
run: Run;
wells: Well[];
observations: Observation[];
runTargets: RunTarget[];
}
interface NormalizedData {
wells: WellCollection;
run_mixes: RunMix[];
run_targets: RunTarget[];
}
interface AnalyzedData {
wells: WellCollection;
all_wells: WellCollection;
run_mixes: RunMix[];
run_targets: RunTarget[];
}
Progress State
interface ProgressCache {
[runId: string]: number;
}
3.3 State Transitions
Analysis Job Lifecycle
State Descriptions:
| State | Entry Action | Progress % |
|---|
| Queued | Job added to queue | 0 |
| Running | RunAnalyzeStarted event fired | 1 |
| Normalizing | Loading wells, observations, run targets | 1-25 |
| Analyzing | Executing rules in precedence order | 25-75 |
| Summarizing | Aggregating results per mix/target | 75-90 |
| Persisting | Batch updating database | 90-100 |
| Completed | RunUpdated event, progress deleted | - |
| Failed | RunUpdateFailed event, progress deleted | - |
4. Interface Design
4.1 APIs Consumed
| Endpoint | Method | Purpose | Response Fields Used |
|---|
| Internal | - | Configuration repository | Rules, LIMS statuses, client config |
4.2 APIs Provided
This domain does not expose external APIs. Analysis is triggered via job dispatch.
4.3 Events
| Event | Direction | Payload | Purpose |
|---|
RunAnalyzeStarted | Backend → Frontend | { runId } | Notify clients analysis started |
RunAnalyseProgressUpdated | Backend → Frontend (Pusher) | { runId, percentage } | Real-time progress updates |
RunUpdated | Backend → Frontend | { runId, username } | Analysis completed successfully |
RunUpdateFailed | Backend → Frontend | { runId, message } | Analysis failed with error |
Broadcasting Channel: Run.Analyse
5. Behavioral Design
5.1 Rule Execution Pipeline
Algorithm: Execute Rules
Inputs:
- run: Run - The run being analyzed
- wells: WellCollection - Wells to analyze
- runMixes: Collection - Mix configurations
- runTargets: Collection - Target configurations
Outputs:
- analyzedWells: WellCollection - Wells with updated state
Assumptions:
- Rules are loaded from configuration repository sorted by precedence
- Wells have been normalized and validated
Steps:
1. Get rules sorted by precedence for well role/target/specimen combinations
2. Count total rules for progress calculation
3. For each rule in precedence order:
a. Update progress: (ruleIndex / rulesCount * 50) + 25
b. For each well:
i. If well.hasExportDate(): skip (already exported)
ii. If well.limsStatusIsExcluded(): skip (EXCLUDE status)
iii. Check error well handling (§5.2)
iv. Check LIMS preventability (§5.3)
v. If allowed: rule.execute(well, context)
4. Return wells with updated state
Notes:
- Progress range during rule execution is 25-75%
- Rules are idempotent; re-execution produces same result
5.2 Error Well Handling
Algorithm: Check Error Well Processing
Inputs:
- well: Well - The well to check
- rule: Rule - The rule being executed
Outputs:
- shouldExecute: boolean - Whether to execute rule on this well
Steps:
1. If well.hasErrorWhichPreventAnalyse():
a. If rule.is_allow_error_wells == true:
Return true (execute rule)
b. Else:
Return false (skip rule)
2. Else:
Return true (no error, execute rule)
Notes:
- Rules with allow_error_well=true can "recover" error wells
- Error recovery enables downstream rules to resolve upstream errors
5.3 LIMS Preventability Check
Algorithm: Check LIMS Preventability
Inputs:
- well: Well - The well to check
- rule: Rule - The rule being executed
Outputs:
- shouldExecute: boolean - Whether to execute rule on this well
Steps:
1. If well.hasAnalysePreventableLimsStatus():
a. Get LIMS status configuration
b. If limsStatus.does_prevent_analyse == true:
Return false (skip subsequent rules)
2. Return true (continue rule chain)
Notes:
- Preventable LIMS outcomes represent "final" statuses
- Non-preventable outcomes allow subsequent rules to override
- Example: DETECTED (non-preventable) can become INHERITED_CONTROL_FAILURE
5.4 Progress Tracking
5.5 Accession Validation
Algorithm: Validate Accession
Inputs:
- well: PatientWell - The patient well being validated
- config: ClientConfiguration - Client settings
Outputs:
- errors: MessageBag - Validation errors (empty if valid or bypassed)
Steps:
1. If config.bypass_accession_validation == true:
Return empty errors (bypass validation)
2. Get accession value from well
3. Check against allowed character pattern
4. If invalid characters found:
Add validation error
5. Return errors
Notes:
- Bypass is client-level configuration
- Only affects patient wells, not control wells
- Invalid wells are imported but flagged for review
6. Error Handling
| Condition | Detection | Response | User Impact |
|---|
| Rule execution error | Exception in rule.execute() | Log error, set run to failed state | Analysis aborted, error notification |
| Database timeout | PDOException | Retry with backoff, then fail | May see stale data briefly |
| Overlapping analysis | WithoutOverlapping middleware | Release job after 3 seconds | Slight delay in processing |
| Missing configuration | Config lookup returns null | Use defaults, log warning | Analysis proceeds with defaults |
| Pusher disconnect | Connection lost | UI reconnects, refetches state | Progress may be stale briefly |
7. Configuration
| Setting | Location | Default | Effect |
|---|
allow_error_well | rules table | false | Whether rule processes error wells |
does_prevent_analyse | lims_statuses table | true | Whether LIMS blocks subsequent rules |
bypass_accession_validation | client_configurations | false | Skip accession validation on import |
long_running_threshold_seconds | client_configurations | 10 | Threshold to show progress indicator |
progress_update_interval_seconds | client_configurations | 2 | Frequency of progress updates |
analyzer.max_execution_time | config/analyzer.php | 90 | PHP max execution time for analysis |
See Configuration Reference for details.
8. Implementation Mapping
8.1 Code Locations
| Component | Type | Path |
|---|
| Analyzer | Core Class | code/app/Analyzer/Analyzer.php |
| Well | Abstract Class | code/app/Analyzer/Well.php |
| Observation | Class | code/app/Analyzer/Observation.php |
| PatientWell | Class | code/app/Analyzer/PatientWell.php |
| ControlWell | Class | code/app/Analyzer/ControlWell.php |
| PatientValidator | Validator | code/app/Analyzer/Validators/PatientValidator.php |
| HasValidAccession | Rule | code/app/Analyzer/Validators/Rules/HasValidAccession.php |
| RunAnalyseJob | Job | code/app/Jobs/RunAnalyseJob.php |
| UpdateRunAction | Action | code/app/Actions/Runs/UpdateRunAction.php |
| RunAnalyseProgress | Support | code/app/Support/RunAnalyseProgress.php |
| RunAnalyseProgressUpdated | Event | code/app/Events/RunAnalyseProgressUpdated.php |
| RunAnalyzeStarted | Event | code/app/Events/RunAnalyzeStarted.php |
| RunUpdated | Event | code/app/Events/RunUpdated.php |
| ConfigurationRepository | Contract | code/app/Analyzer/Contracts/ConfigurationRepository.php |
| EloquentConfigurationRepository | Repository | code/app/Analyzer/EloquentConfigurationRepository.php |
8.2 Rules Engine Code
| Category | Count | Location |
|---|
| Classification rules | ~15 | code/app/Analyzer/Rules/Wdcls*.php, Wfinalcls*.php |
| Combined outcome rules | 4 | code/app/Analyzer/Rules/*CombinedOutcome*.php |
| Westgard QC rules | 7 | code/app/Analyzer/Rules/Wg*.php |
| Quantification rules | 8 | code/app/Analyzer/Rules/Quant*.php, Rquant*.php |
| Inhibition rules | 3 | code/app/Analyzer/Rules/Inh*.php, SystemicInhibition*.php |
| Other specialized rules | ~30 | Various |
See SDS: Rules Engine for detailed rule documentation.
8.3 Requirement Traceability
| REQ ID | Design Section | Code Location |
|---|
| REQ-ANALYTICS-001 | §5.1, §5.2 | Analyzer.php, Well.php:hasErrorWhichPreventAnalyse() |
| REQ-ANALYTICS-002 | §5.1, §5.3 | Observation.php:shouldSkipRule(), LimsStatus.php |
| REQ-ANALYZER-001 | §5.5 | PatientValidator.php, HasValidAccession.php |
| REQ-PROGRESS-001 | §5.4 | UpdateRunAction.php, RunAnalyseProgress.php |
| REQ-PROGRESS-002 | §3.3, §5.4 | RunAnalyseProgress.php (cache-based) |
| REQ-PROGRESS-003 | §3.2 | RunAnalyseProgress.php (per-run keys) |
| REQ-PROGRESS-004 | §4.3, §5.4 | RunAnalyseProgressUpdated.php (ShouldBroadcastNow) |
9. Design Decisions
| Decision | Rationale | Alternatives Considered |
|---|
| Cache-based progress tracking | Enables persistence across navigation without DB writes | Database table (rejected: too many writes), Session (rejected: no multi-user visibility) |
| Pusher for real-time updates | Existing infrastructure, low latency, multi-user broadcast | Polling (rejected: higher load), SSE (rejected: less browser support) |
| Job-based async analysis | Prevents UI blocking on long operations; supports retries | Synchronous execution (rejected: poor UX for large runs) |
| Rules as separate classes | Each rule is testable; easy to add new rules | Single rule engine (rejected: monolithic, hard to maintain) |
| Precedence-based rule ordering | Deterministic outcomes; rules can depend on prior rule results | Priority flags (rejected: harder to reason about) |
| WithoutOverlapping middleware | Prevents race conditions on same run | Database locking (rejected: more complex) |