1import type { ChartDataset, ChartType } from "chart.js";
2import type { Data, Series } from "../types/chart.js";
3import type { Metric, Metrics, Range } from "../types/data.js";
4
5export interface Mapper<T = number> {
6  rangeLabel: (metric: Metric<unknown>) => string;
7  sampledRanges: (metrics: Metrics<T>) => Record<string, Range>;
8  standard: (value: Metric<T>) => Series[];
9  sampled: (value: Metric<T[]>, range: Range | null) => Series[];
10}
11
12/**
13 * Converts `Metrics` to the corresponding chart data structures.
14 */
15export class ChartDataTransforms {
16
17  static mapToSeries(metrics: Metrics<number>, mapper: Mapper<number>, normalize: boolean = false): Series[] {
18    const series: Series[] = [];
19    const standard = metrics.standard;
20    const sampled = metrics.sampled;
21    // Builds ranges for distribution.
22    let ranges: Record<string, Range> = {};
23    if (normalize) {
24      ranges = mapper.sampledRanges(metrics);
25    }
26    // Builds series.
27    if (standard) {
28      for (let i = 0; i < standard.length; i += 1) {
29        const metric = standard[i];
30        const mapped = mapper.standard(metric);
31        series.push(...mapped);
32      }
33    }
34    if (sampled) {
35      for (let i = 0; i < sampled.length; i += 1) {
36        const metric = sampled[i];
37        const mapped = mapper.sampled(metric, ranges[mapper.rangeLabel(metric)]);
38        series.push(...mapped);
39      }
40    }
41    return series;
42  }
43
44  static mapToDataset(series: Series[]): Data {
45    let xmax = 0;
46    let datasets: ChartDataset[] = [];
47    for (let i = 0; i < series.length; i += 1) {
48      xmax = Math.max(xmax, series[i].data.length);
49      datasets.push(ChartDataTransforms.chartDataset(series[i]));
50    }
51    const chartData: Data = {
52      labels: ChartDataTransforms.xrange(xmax),
53      datasets: datasets
54    };
55    return chartData;
56  }
57
58  private static chartDataset<T extends ChartType>(series: Series): ChartDataset {
59    return {
60      label: series.descriptiveLabel,
61      type: series.type,
62      data: series.data,
63      ...series.options
64    };
65  }
66
67  private static xrange(xmax: number): number[] {
68    let range = [];
69    for (let i = 1; i <= xmax; i += 1) {
70      range.push(i);
71    }
72    return range;
73  }
74}
75