2025.02.16 metrics updates - SergeiGolos/wod-wiki GitHub Wiki
This PRD outlines a plan to refactor the metrics system in wod.wiki to create a clean separation between statement/precompile nodes and runtime blocks. The current implementation has tight coupling between these components, which makes maintenance difficult and limits extensibility.
- StatementNodes contain fragments (effort, repetitions, resistance, distance)
- PrecompiledNodes wrap StatementNodes and pass fragments through
- RuntimeBlocks are created from statements via strategy pattern
-
Metrics generation happens in AbstractBlockLifecycle.metrics() which:
- Extracts metrics from sources (PrecompiledNodes)
- Includes child metrics
- Inherits parent metrics
- Poor separation of concerns between statement structure and runtime representation
- Metrics inheritance is complex and follows block hierarchy which may not match logical workout grouping
- When blocks represent multiple statements, metrics relationships are unclear
- Parent metrics impact on children is implemented in a generic way without specific workout logic
- No clear mechanism for runtime blocks to update their own metrics during execution
- Create a clean separation between statement/precompile nodes and runtime blocks
- Establish a mechanism for parent metrics to properly impact child metrics
- Support proper metrics for runtime blocks representing multiple statement nodes
- Enable runtime blocks to update their own metrics during execution
A new component responsible for creating metrics from statements:
interface IMetricsFactory {
// Create metrics from a statement node
createFromStatement(node: StatementNode): RuntimeMetric[];
// Create metrics from multiple statement nodes
createFromStatements(nodes: StatementNode[]): RuntimeMetric[];
// Apply parent metrics to child metrics based on workout logic
applyParentContext(parentMetrics: RuntimeMetric[], childMetrics: RuntimeMetric[]): RuntimeMetric[];
}
A new container class for metrics operations:
class MetricsContext {
private metrics: RuntimeMetric[] = [];
// Add metrics to the context
addMetrics(metrics: RuntimeMetric[]): void;
// Update existing metrics by sourceId
updateMetrics(sourceId: string, updater: (metric: RuntimeMetric) => void): void;
// Get metrics by source ID
getMetricsBySourceId(sourceId: string): RuntimeMetric[];
// Get all metrics
getAllMetrics(): RuntimeMetric[];
// Create a new context with these metrics as parents for child context
createChildContext(): MetricsContext;
}
interface IMetricsProvider {
// Get metrics from this provider
getMetrics(): RuntimeMetric[];
// Update metrics for this provider
updateMetrics(updater: (metrics: RuntimeMetric[]) => RuntimeMetric[]): void;
}
-
RuntimeBlock implementation:
- Blocks implement IMetricsProvider
- Each block maintains its own MetricsContext
- Parents pass context to children during initialization
-
Block Lifecycle:
- During compilation, MetricsFactory creates initial metrics from statements
- During runtime, blocks can update their metrics to reflect changes
- When blocks complete, they push metrics to parent context
-
Results Reporting:
- ResultSpan objects collect metrics from blocks when events occur
- No change to the existing metrics display components
- Create the MetricsFactory and MetricsContext classes
- Implement IMetricsProvider interface
- Update AbstractBlockLifecycle to use the new metrics components
- Unit tests for metrics generation and inheritance
- Update RuntimeBlock implementations to use MetricsContext
- Implement context passing between parent and child blocks
- Refactor the metrics() method to use the new architecture
- Integration tests for block metrics inheritance
- Implement mechanisms for blocks to update metrics during execution
- Create specialized metrics updates for different block types
- Update ResultSpan to use the new metrics system
- End-to-end tests for metrics updates during workout execution
- Implement the new metrics system alongside the existing one
- Gradually update block implementations to use the new system
- Switch over completely once all blocks have been updated
- Remove legacy metrics handling code
- Clean separation between statically derived metrics (from statements) and dynamically updated metrics (from runtime)
- Parent metrics properly impact child metrics based on workout structure
- Blocks representing multiple statements have correct composite metrics
- Blocks can update their metrics during execution
- All existing unit tests pass with the new implementation
- No changes required to UI components that display metrics
- Integration testing
- Create tests for metrics generation and inheritance
- Define IMetricsProvider interface @completed(2025-05-16T14:55:00)
- Create MetricsContext implementation @completed(2025-05-16T14:55:05)
- Create MetricsFactory implementation @completed(2025-05-16T14:55:10)
- Update AbstractBlockLifecycle to use new components @completed(2025-05-16T15:05:00)
- Implement parent-child metrics relationships @completed(2025-05-16T15:10:00)
- Update specific block implementations for metrics @completed(2025-05-16T15:15:00)
- Update ResultSpan to use new metrics system @completed(2025-05-16T15:20:00)
- Enable runtime metric updates @completed(2025-05-16T15:25:00)