Skip to main content
Version: 3.0.0

Reports Domain Design

Document Type: Domain Design (Tier 2) Domain: REPORTS Domain Character: Thin orchestration SRS Reference: reports.md, print.md Status: Draft Last Updated: 2026-01-25


1. Overview

1.1 Purpose

The Reports domain provides analytical and statistical reporting capabilities for PCR run data. It encompasses three report types:

  1. Levey Jennings (LJ) Reports - Quality control charts showing Westgard-tracked control performance over time
  2. Trends Reports - Outcome trend analysis with configurable aggregation and alerting
  3. Outcomes Reports - Detailed outcome data with drill-down capabilities

This is a thin orchestration domain because it primarily coordinates data retrieval from other domains (RUNRPT, KITCFG, ERRORCODES) and delegates visualization to the frontend. Backend logic focuses on data aggregation, filtering, and export generation.

1.2 Requirements Covered

REQ IDTitlePriority
REQ-REPORTS-001Generate Levey Jennings ReportMust
REQ-REPORTS-002Create Levey Jennings DatasetMust
REQ-REPORTS-003Navigate from Levey Jennings ReportMust
REQ-REPORTS-004Export Levey Jennings ReportMust
REQ-REPORTS-005Manage Westgard RangesMust
REQ-REPORTS-006Bulk Edit Westgard LimitsShould
REQ-REPORTS-007Configure Westgard Display OptionsMust
REQ-REPORTS-008Generate Trends ReportMust
REQ-REPORTS-009Create Trends DatasetMust
REQ-REPORTS-010Navigate from Trends ReportMust
REQ-REPORTS-011Export and Print Trends ReportMust
REQ-REPORTS-012Aggregate Trends DataMust
REQ-REPORTS-013Manage Trends AlertsMust
REQ-REPORTS-014Trigger and Deliver Alert NotificationsMust
REQ-REPORTS-015Handle Special Trends DataShould
REQ-REPORTS-016Generate Outcomes ReportMust
REQ-REPORTS-017Create Outcomes DatasetMust
REQ-REPORTS-018Navigate from Outcomes ReportMust
REQ-REPORTS-019Export and Print Outcomes ReportMust
REQ-REPORTS-020Configure Combined OutcomesShould
REQ-REPORTS-021Multi-Site Report AccessMust
REQ-REPORTS-022Exclude Archived Runs from ReportsMust
REQ-REPORTS-023Display Crossover Wells in ReportsMust
REQ-REPORTS-024Role-Based Report AccessMust
REQ-REPORTS-025Report PerformanceMust
REQ-PRINT-001Generate Standardized PDF File NamesMust
REQ-PRINT-002Display Standardized Report HeaderMust
REQ-PRINT-003Include Complete Report DataMust
REQ-PRINT-004Display Standardized Report FooterMust
REQ-PRINT-005Include Report Metadata SectionMust

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.

1.4 Dependencies

DirectionDomain/ComponentPurpose
ConsumesWell ModelLJ chart data points, outcomes data
WestgardLimit ModelQC range definitions, mean/SD values
DailyOutcomes TablePre-aggregated trends data
Run ModelRun metadata, filtering
Mix/Target ModelsReport filtering criteria
Thermocycler ModelInstrument-based filtering/grouping
TrendsReportAlert ModelAlert configuration and evaluation
Provides toFrontendReport data for visualization
Export ConsumersCSV/Excel/PDF exports
NOTIF DomainAlert notifications

2. Component Architecture

2.1 Component Diagram

2.2 Component Responsibilities

ComponentLayerResponsibilityREQ Trace
LJReportControllerControllerGenerate LJ report data with wells and Westgard limitsREQ-REPORTS-001, 002, 003, 005, 007, 023
LJReportDownloadControllerControllerExport LJ report to ExcelREQ-REPORTS-004
LJReportLotsControllerControllerManage Westgard range (lot) dataREQ-REPORTS-002, 005
LjReportDataServiceAggregate well and Westgard data for LJ chartsREQ-REPORTS-001
LjReportExportExportFormat LJ data for Excel exportREQ-REPORTS-004
WestgardLimitsControllerControllerCRUD for Westgard statistical rangesREQ-REPORTS-005, 006
TrendsReportControllerControllerGenerate trends report from daily outcomesREQ-REPORTS-008, 009, 012, 015
TrendsReportExportControllerControllerExport trends reportREQ-REPORTS-011
TrendsReportPrintControllerControllerGenerate printable trends reportREQ-REPORTS-011, REQ-PRINT-*
TrendsReportAlertsControllerControllerManage trend alert configurationsREQ-REPORTS-013
TrendsReportAlertModelAlert configuration with thresholds and schedulesREQ-REPORTS-013, 014
OutcomesReportControllerControllerGenerate outcomes report with paginationREQ-REPORTS-016, 017, 018
OutcomesReportExportControllerControllerExport outcomes reportREQ-REPORTS-019
OutcomesReportPrintControllerControllerGenerate printable outcomes reportREQ-REPORTS-019, REQ-PRINT-*
OutcomesReportFiltersDTOEncapsulate outcomes filter criteriaREQ-REPORTS-017

3. Data Design

3.1 Entities

This domain does not own persistent entities. It consumes data from other domains and one pre-aggregated table.

EntityOwnerUsage in REPORTS
wellsRUNRPTSource data for LJ and Outcomes reports
westgard_limitsKITCFGQC range definitions with mean/SD/CV
daily_outcomesREPORTS (aggregated)Pre-computed outcome counts for Trends
trends_report_alertsREPORTSAlert configurations
runsRUNRPTRun metadata, archive status
mixesKITCFGFilter criteria
thermocyclersKITCFGFilter/grouping criteria

3.2 Data Structures

LJ Report Response

interface LJReportResponse {
wells: LJReportWell[]; // Non-crossover wells
crossover_wells: LJReportWell[]; // Crossover wells (separate display)
westgard_limits: WestgardLimit[]; // Statistical ranges
}

interface LJReportWell {
id: string;
extraction_date: Date;
ct_value: number;
quant_value: number;
run_name: string;
mix_name: string;
target_name: string;
control_type: string;
is_crossover: boolean;
error_code?: string;
}

interface WestgardLimit {
id: string;
lot_name: string;
mean: number;
sd: number;
cv: number;
quant_or_ct: 'CT' | 'Quantity';
created_at: Date;
event: 'NEWWG' | 'UPDATE' | 'RESOLUTION';
}

Trends Report Response

interface TrendsReportRow {
site_name: string;
date: string; // Aggregated by interval
mix_name: string; // Or "All selected mixes"
thermocycler_serial_number: string; // Or "All selected thermocyclers"
outcome: string; // Or "All selected outcomes"
well_count: number;
percentage: number;
}

Trends Alert Configuration

interface TrendsReportAlert {
id: string;
is_patient: boolean;
threshold_type: 'COUNT' | 'PERCENTAGE';
trigger: 'ABOVE' | 'BELOW';
threshold_value: number;
interval: 'DAY' | 'WEEK' | 'MONTH';
notifiers: 'EMAIL' | 'IN_APP' | 'BOTH';
thermocycler_ids: string[];
mix_ids: string[];
outcomes: OutcomeFilter[];
enabled: boolean;
site_id: string;
schedule?: AlertSchedule;
}

3.3 State Transitions

This domain is stateless for report generation. Alert state is managed through enable/disable operations.


4. Interface Design

4.1 APIs Provided

EndpointMethodPurposeController
/api/lj-reportGETGenerate LJ report dataLJReportController
/api/lj-report/downloadGETExport LJ report to ExcelLJReportDownloadController
/api/lj-report/lotsGETGet available Westgard lotsLJReportLotsController
/api/westgard-limitsGET/POST/PUT/DELETEManage Westgard rangesWestgardLimitsController
/api/trends-reportGETGenerate trends report dataTrendsReportController
/api/trends-report/exportGETExport trends reportTrendsReportExportController
/api/trends-report/print/{id}GETGet cached print dataTrendsReportPrintController
/api/trends-report-alertsGET/POST/PUT/DELETEManage alertsTrendsReportAlertsController
/api/outcomes-reportGETGenerate outcomes reportOutcomesReportController
/api/outcomes-report/exportGETExport outcomes reportOutcomesReportExportController
/api/outcomes-report/print/{id}GETGet cached print dataOutcomesReportPrintController

4.2 APIs Consumed

Endpoint/SourcePurpose
Well Model queriesSource data for LJ and Outcomes
DailyOutcomes tablePre-aggregated data for Trends
Site/Mix/Thermocycler ModelsFilter option population

5. Behavioral Design

5.1 LJ Report Data Aggregation

Algorithm: Generate Levey Jennings Report Data

Inputs:
- well_filters: WellFilters - Date range, error status filters
- run_filters: RunFilters - Run-level filters
- target_ids: array - Selected targets
- role_to_mix_mapping_ids: array - Control role mappings
- extraction_instrument_ids: array - Selected instruments
- lot_ids: array - Selected Westgard lots (optional)
- run_id: string - Focus on specific run (optional)

Outputs:
- wells: Collection - Control wells with observations
- crossover_wells: Collection - Crossover wells (separate)
- westgard_limits: Collection - Applicable Westgard ranges

Steps:
1. If run_id provided:
a. Load wells from specified run
b. Load 10 previous wells by extraction_date
c. Load 10 next wells by extraction_date
d. Merge into result set
2. Else:
a. Load all matching wells (limit 999)
3. Apply archive exclusion (whereNotBelongsToArchivedRun)
4. Load Westgard limits matching filters
5. Separate wells into crossover and non-crossover
6. Return combined dataset

Notes:
- Archived runs always excluded (REQ-REPORTS-022)
- Wells split by is_crossover flag (REQ-REPORTS-023)
- Westgard limits include soft-deleted for historical accuracy
Algorithm: Generate Trends Report

Inputs:
- is_patient: boolean - Patient vs control samples
- sample_names: array - Control role aliases with crossover flag
- thermocycler_ids: array - Instrument filter
- mix_ids: array - Mix filter
- site_ids: array - Site filter
- outcome_ids: array - Outcome filter
- date_range: [start, end] - Date filter
- interval: 'Day' | 'Week' | 'Month' - Aggregation interval
- aggregated_filters: { mixes, outcomes, thermocyclers } - Aggregation flags

Outputs:
- rows: Collection - Aggregated outcome data

Steps:
1. Query daily_outcomes table
2. Apply sample type filter:
a. If is_patient: filter role_alias = 'Patient'
b. Else: filter by sample_names with is_crossover matching
3. Apply entity filters (thermocycler, site, outcome, mix)
4. Apply date range filter
5. Group by:
a. site_id (always)
b. interval (Day/Week/Month with SQL date functions)
c. thermocycler_id (unless aggregated)
d. mix_id (unless aggregated)
e. outcome_id + is_crossover (unless aggregated)
6. Calculate:
a. well_count: SUM(count)
b. percentage: well_count / partition total * 100
7. Return ordered by date

Notes:
- Uses pre-computed daily_outcomes table for performance
- Interval grouping uses SQL date functions (DAYOFWEEK, LAST_DAY)
- Percentage calculated using window function (OVER PARTITION BY)

5.2.1 Timezone Handling

The daily_outcomes cache normalizes dates to lab timezone:

Well TypeDate SourceHandling
Extraction wellsWell extraction_dateConvert to lab timezone, store date-only
Non-extraction controlsRun created_atInherit run date

This ensures all wells in a run are grouped by the same date regardless of original timestamp precision.

5.2.2 Cache Update Strategy

OperationComponentTrigger
Initial PopulationPopulateDailyOutcomesTableActionMigration or manual rebuild
Incremental UpdateUpdateDailyOutcomesTableActionRun file processed
Background JobUpdateDailyOutcomeTableJobDispatched after each run upload
Full RegenerationMigrationData correction required

5.2.3 Performance Characteristics

MetricBefore CacheWith Cache
Query TimeSeconds to minutesMilliseconds
StorageN/AAdditional pre-computed rows
SynchronizationN/AAutomatic via job dispatch

Special Handling:

  • is_crossover flag preserved in aggregation for QC filtering
  • Soft-deleted records preserved for historical accuracy

5.3 Print Report Generation

Algorithm: Generate Print Report Data

Inputs:
- uniq_identifier: string - Cache key for request data
- report_type: 'trends' | 'outcomes' - Report type

Outputs:
- data: array - Complete report data for printing

Steps:
1. Retrieve cached request data by identifier
2. If processed data exists in cache:
a. Return cached data directly
3. Else:
a. Rebuild query using cached filter parameters
b. Execute full query (no pagination)
c. Return complete dataset

Notes:
- Print controllers return ALL data regardless of pagination (REQ-PRINT-003)
- Frontend handles header/footer formatting (REQ-PRINT-002, 004)
- File naming handled by frontend (REQ-PRINT-001)

6. Error Handling

ConditionDetectionResponseUser Impact
Empty datasetZero resultsReturn empty array"No data matches filters" message
Invalid Westgard SDSD <= 0Validation errorCannot save range
Invalid Westgard meanMean < 0Validation errorCannot save range
Duplicate range nameUniqueness check422 response"Range name must be unique"
Alert threshold invalidValue validationValidation errorCannot save alert
Cache miss (print)No cached dataRebuild querySlightly slower response

7. Configuration

SettingLocationDefaultEffect
default_lj_unitClientConfigurationCTDefault Y-axis unit
lj_show_meanClientConfigurationtrueMean line visibility
lj_show_sd_linesClientConfigurationtrueSD line visibility
lj_show_bandsClientConfigurationtrueColor band visibility
trends_default_intervalClientConfigurationWeekDefault aggregation interval
alert_email_enabledEnvironmenttrueEmail notification toggle
progressive_loading_thresholdEnvironment1000Pagination threshold

See Configuration Reference for details.


8. Implementation Mapping

8.1 Code Locations

ComponentTypePath
LJReportControllerControllercode/app/Http/Controllers/LJReportController.php
LJReportDownloadControllerControllercode/app/Http/Controllers/LJReportDownloadController.php
LJReportLotsControllerControllercode/app/Http/Controllers/LJReportLotsController.php
LjReportDataServicecode/app/LjReportData.php
LjReportExportExportcode/app/Exports/LjReportExport.php
WestgardLimitModelcode/app/WestgardLimit.php
WestgardLimitsControllerControllercode/app/Http/Controllers/WestgardLimitsController.php
TrendsReportControllerControllercode/app/Http/Controllers/TrendsReport/TrendsReportController.php
TrendsReportExportControllerControllercode/app/Http/Controllers/TrendsReportExportController.php
TrendsReportPrintControllerControllercode/app/Http/Controllers/TrendsReportPrintController.php
TrendsReportAlertsControllerControllercode/app/Http/Controllers/TrendsReportAlertsController.php
TrendsReportAlertModelcode/app/TrendsReportAlert.php
TrendsReportExportExportcode/app/Exports/TrendsReportExport.php
OutcomesReportControllerControllercode/app/Http/Controllers/OutcomesReportController.php
OutcomesReportExportControllerControllercode/app/Http/Controllers/OutcomesReportExportController.php
OutcomesReportPrintControllerControllercode/app/Http/Controllers/OutcomesReportPrintController.php
OutcomesReportFiltersDTOcode/app/Actions/OutcomesReport/OutcomesReportFilters.php

8.2 Requirement Traceability

REQ IDDesign SectionCode Location
REQ-REPORTS-001§5.1 LJ AggregationLJReportController.php, LjReportData.php
REQ-REPORTS-002§5.1 LJ AggregationLJReportLotsController.php, LjReportData.php
REQ-REPORTS-003§4.1 APIsLJReportController.php
REQ-REPORTS-004§2.2 ComponentsLJReportDownloadController.php, LjReportExport.php
REQ-REPORTS-005§2.2 ComponentsWestgardLimitsController.php, WestgardLimit.php
REQ-REPORTS-006§2.2 ComponentsWestgardLimitsController.php
REQ-REPORTS-007§7 ConfigurationLJReportController.php, ClientConfiguration.php
REQ-REPORTS-008§5.2 Trends AggregationTrendsReportController.php
REQ-REPORTS-009§5.2 Trends AggregationTrendsReportController.php
REQ-REPORTS-010§4.1 APIsTrendsReportController.php
REQ-REPORTS-011§5.3 Print GenerationTrendsReportExportController.php, TrendsReportPrintController.php
REQ-REPORTS-012§5.2 Trends AggregationTrendsReportController.php (aggregated_filters)
REQ-REPORTS-013§3.2 Alert ConfigTrendsReportAlertsController.php, TrendsReportAlert.php
REQ-REPORTS-014§3.3 Alert StateNotifyTrendAlertsAction.php, TrendsReportAlertsScheduleCheck.php
REQ-REPORTS-015§5.2 Trends AggregationTrendsReportController.php (outcomeMessage)
REQ-REPORTS-016§2.2 ComponentsOutcomesReportController.php
REQ-REPORTS-017§2.2 ComponentsOutcomesReportFilters.php
REQ-REPORTS-018§4.1 APIsOutcomesReportController.php
REQ-REPORTS-019§5.3 Print GenerationOutcomesReportExportController.php, OutcomesReportPrintController.php
REQ-REPORTS-020§2.2 ComponentsCombined outcome configuration (KITCFG)
REQ-REPORTS-021§5.1, §5.2 AggregationSite filtering in all controllers
REQ-REPORTS-022§5.1 LJ AggregationwhereNotBelongsToArchivedRun()
REQ-REPORTS-023§5.1 LJ AggregationLjReportData.php (is_crossover split)
REQ-REPORTS-024§2.2 ComponentsMiddleware role checks
REQ-REPORTS-025§7 ConfigurationPagination in controllers
REQ-PRINT-001§5.3 Print GenerationFrontend (file naming)
REQ-PRINT-002§5.3 Print GenerationFrontend (header formatting)
REQ-PRINT-003§5.3 Print GenerationPrint controllers (no pagination)
REQ-PRINT-004§5.3 Print GenerationFrontend (footer formatting)
REQ-PRINT-005§5.3 Print GenerationFrontend (metadata section)

9. Design Decisions

DecisionRationaleAlternatives Considered
Pre-aggregated daily_outcomes tablePerformance for trends over large datasetsReal-time aggregation (rejected: too slow)
Separate crossover wells in LJ responseBusiness requirement for distinct displaySingle wells array (rejected: loses visibility)
Frontend-driven print formattingFlexibility, separation of concernsServer-side PDF generation (rejected: less flexible)
Cache-based print dataSupport large reports without timeoutDirect query (rejected: timeouts on large data)
SQL window functions for percentageDatabase-efficient calculationPHP post-processing (rejected: slower)
Soft-delete preservation for WestgardHistorical accuracy in reportsHard delete (rejected: loses history)

DocumentRelevant Sections
SRS: reports.mdRequirements source
SRS: print.mdPrint requirements source
SDS: ArchitectureSystem architecture context
SDS: RUNRPT DomainWell/Run data source
SDS: KITCFG DomainWestgard configuration
SDS: Database ReferenceSchema details