Graph: move time region calculation to a utility function (#51413)

pull/52253/head
Ryan McKinley 3 years ago committed by GitHub
parent 7bb644d910
commit ededf1dd6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      .betterer.results
  2. 43
      public/app/core/utils/timeRegions.test.ts
  3. 168
      public/app/core/utils/timeRegions.ts
  4. 211
      public/app/plugins/panel/graph/time_region_manager.ts

@ -9414,13 +9414,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
[0, 0, 0, "Unexpected any. Specify a different type.", "8"],
[0, 0, 0, "Unexpected any. Specify a different type.", "9"],
[0, 0, 0, "Unexpected any. Specify a different type.", "10"],
[0, 0, 0, "Unexpected any. Specify a different type.", "11"],
[0, 0, 0, "Unexpected any. Specify a different type.", "12"]
[0, 0, 0, "Unexpected any. Specify a different type.", "6"]
],
"public/app/plugins/panel/graph/time_regions_form.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],

@ -0,0 +1,43 @@
import { dateTime, TimeRange } from '@grafana/data';
import { calculateTimesWithin, TimeRegionConfig } from './timeRegions';
describe('timeRegions', () => {
describe('day of week', () => {
it('4 sundays in january 2021', () => {
const cfg: TimeRegionConfig = {
fromDayOfWeek: 1,
from: '12:00',
};
const tr: TimeRange = {
from: dateTime('2021-01-00', 'YYYY-MM-dd'),
to: dateTime('2021-02-00', 'YYYY-MM-dd'),
raw: {
to: '',
from: '',
},
};
const regions = calculateTimesWithin(cfg, tr);
expect(regions).toMatchInlineSnapshot(`
Array [
Object {
"from": 1609779600000,
"to": 1609779600000,
},
Object {
"from": 1610384400000,
"to": 1610384400000,
},
Object {
"from": 1610989200000,
"to": 1610989200000,
},
Object {
"from": 1611594000000,
"to": 1611594000000,
},
]
`);
});
});
});

@ -0,0 +1,168 @@
import { AbsoluteTimeRange, dateTime, TimeRange } from '@grafana/data';
export interface TimeRegionConfig {
from?: string;
fromDayOfWeek?: number; // 1-7
to?: string;
toDayOfWeek?: number; // 1-7
}
interface ParsedTime {
dayOfWeek?: number; // 1-7
h?: number; // 0-23
m?: number; // 0-59
s?: number; // 0-59
}
export function calculateTimesWithin(cfg: TimeRegionConfig, tRange: TimeRange): AbsoluteTimeRange[] {
if (!(cfg.fromDayOfWeek || cfg.from) && !(cfg.toDayOfWeek || cfg.to)) {
return [];
}
// So we can mutate
const timeRegion = { ...cfg };
if (timeRegion.from && !timeRegion.to) {
timeRegion.to = timeRegion.from;
}
if (!timeRegion.from && timeRegion.to) {
timeRegion.from = timeRegion.to;
}
const hRange = {
from: parseTimeRange(timeRegion.from),
to: parseTimeRange(timeRegion.to),
};
if (!timeRegion.fromDayOfWeek && timeRegion.toDayOfWeek) {
timeRegion.fromDayOfWeek = timeRegion.toDayOfWeek;
}
if (!timeRegion.toDayOfWeek && timeRegion.fromDayOfWeek) {
timeRegion.toDayOfWeek = timeRegion.fromDayOfWeek;
}
if (timeRegion.fromDayOfWeek) {
hRange.from.dayOfWeek = Number(timeRegion.fromDayOfWeek);
}
if (timeRegion.toDayOfWeek) {
hRange.to.dayOfWeek = Number(timeRegion.toDayOfWeek);
}
if (hRange.from.dayOfWeek && hRange.from.h == null && hRange.from.m == null) {
hRange.from.h = 0;
hRange.from.m = 0;
hRange.from.s = 0;
}
if (hRange.to.dayOfWeek && hRange.to.h == null && hRange.to.m == null) {
hRange.to.h = 23;
hRange.to.m = 59;
hRange.to.s = 59;
}
if (!hRange.from || !hRange.to) {
return [];
}
if (hRange.from.h == null) {
hRange.from.h = 0;
}
if (hRange.to.h == null) {
hRange.to.h = 23;
}
const regions: AbsoluteTimeRange[] = [];
const fromStart = dateTime(tRange.from);
fromStart.set('hour', 0);
fromStart.set('minute', 0);
fromStart.set('second', 0);
fromStart.add(hRange.from.h, 'hours');
fromStart.add(hRange.from.m, 'minutes');
fromStart.add(hRange.from.s, 'seconds');
while (fromStart.unix() <= tRange.to.unix()) {
while (hRange.from.dayOfWeek && hRange.from.dayOfWeek !== fromStart.isoWeekday()) {
fromStart.add(24, 'hours');
}
if (fromStart.unix() > tRange.to.unix()) {
break;
}
const fromEnd = dateTime(fromStart);
if (fromEnd.hour) {
if (hRange.from.h <= hRange.to.h) {
fromEnd.add(hRange.to.h - hRange.from.h, 'hours');
} else if (hRange.from.h > hRange.to.h) {
while (fromEnd.hour() !== hRange.to.h) {
fromEnd.add(1, 'hours');
}
} else {
fromEnd.add(24 - hRange.from.h, 'hours');
while (fromEnd.hour() !== hRange.to.h) {
fromEnd.add(1, 'hours');
}
}
}
fromEnd.set('minute', hRange.to.m ?? 0);
fromEnd.set('second', hRange.to.s ?? 0);
while (hRange.to.dayOfWeek && hRange.to.dayOfWeek !== fromEnd.isoWeekday()) {
fromEnd.add(24, 'hours');
}
const outsideRange =
(fromStart.unix() < tRange.from.unix() && fromEnd.unix() < tRange.from.unix()) ||
(fromStart.unix() > tRange.to.unix() && fromEnd.unix() > tRange.to.unix());
if (!outsideRange) {
regions.push({ from: fromStart.valueOf(), to: fromEnd.valueOf() });
}
fromStart.add(24, 'hours');
}
return regions;
}
function parseTimeRange(str?: string): ParsedTime {
const result: ParsedTime = {};
if (!str?.length) {
return result;
}
const timeRegex = /^([\d]+):?(\d{2})?/;
const match = timeRegex.exec(str);
if (!match) {
return result;
}
if (match.length > 1) {
result.h = Number(match[1]);
result.m = 0;
if (match.length > 2 && match[2] !== undefined) {
result.m = Number(match[2]);
}
if (result.h > 23) {
result.h = 23;
}
if (result.m > 59) {
result.m = 59;
}
}
return result;
}

@ -1,8 +1,9 @@
import 'vendor/flot/jquery.flot';
import { map } from 'lodash';
import { dateTime, DateTime, AbsoluteTimeRange, GrafanaTheme } from '@grafana/data';
import { dateTime, GrafanaTheme, TimeRange } from '@grafana/data';
import { config } from 'app/core/config';
import { calculateTimesWithin, TimeRegionConfig } from 'app/core/utils/timeRegions';
type TimeRegionColorDefinition = {
fill: string | null;
@ -68,9 +69,19 @@ function getColor(timeRegion: any, theme: GrafanaTheme): TimeRegionColorDefiniti
};
}
interface GraphTimeRegionConfig extends TimeRegionConfig {
colorMode: string;
fill: boolean;
fillColor: string;
line: boolean;
lineColor: string;
}
export class TimeRegionManager {
plot: any;
timeRegions: any;
timeRegions?: TimeRegionConfig[];
constructor(private panelCtrl: any) {}
@ -80,183 +91,47 @@ export class TimeRegionManager {
}
addFlotOptions(options: any, panel: any) {
if (!panel.timeRegions || panel.timeRegions.length === 0) {
if (!panel.timeRegions?.length) {
return;
}
const tRange = {
// The panel range
const tRange: TimeRange = {
from: dateTime(this.panelCtrl.range.from).utc(),
to: dateTime(this.panelCtrl.range.to).utc(),
raw: {
from: '',
to: '',
},
};
let i: number,
hRange: { from: any; to: any },
timeRegion: any,
regions: AbsoluteTimeRange[],
fromStart: DateTime,
fromEnd: DateTime,
timeRegionColor: TimeRegionColorDefinition;
const timeRegionsCopy = panel.timeRegions.map((a: any) => ({ ...a }));
for (i = 0; i < timeRegionsCopy.length; i++) {
timeRegion = timeRegionsCopy[i];
if (!(timeRegion.fromDayOfWeek || timeRegion.from) && !(timeRegion.toDayOfWeek || timeRegion.to)) {
continue;
}
if (timeRegion.from && !timeRegion.to) {
timeRegion.to = timeRegion.from;
}
if (!timeRegion.from && timeRegion.to) {
timeRegion.from = timeRegion.to;
}
hRange = {
from: this.parseTimeRange(timeRegion.from),
to: this.parseTimeRange(timeRegion.to),
};
if (!timeRegion.fromDayOfWeek && timeRegion.toDayOfWeek) {
timeRegion.fromDayOfWeek = timeRegion.toDayOfWeek;
}
if (!timeRegion.toDayOfWeek && timeRegion.fromDayOfWeek) {
timeRegion.toDayOfWeek = timeRegion.fromDayOfWeek;
}
if (timeRegion.fromDayOfWeek) {
hRange.from.dayOfWeek = Number(timeRegion.fromDayOfWeek);
}
if (timeRegion.toDayOfWeek) {
hRange.to.dayOfWeek = Number(timeRegion.toDayOfWeek);
}
if (hRange.from.dayOfWeek && hRange.from.h === null && hRange.from.m === null) {
hRange.from.h = 0;
hRange.from.m = 0;
hRange.from.s = 0;
}
if (hRange.to.dayOfWeek && hRange.to.h === null && hRange.to.m === null) {
hRange.to.h = 23;
hRange.to.m = 59;
hRange.to.s = 59;
}
if (!hRange.from || !hRange.to) {
continue;
}
regions = [];
fromStart = dateTime(tRange.from);
fromStart.set('hour', 0);
fromStart.set('minute', 0);
fromStart.set('second', 0);
fromStart.add(hRange.from.h, 'hours');
fromStart.add(hRange.from.m, 'minutes');
fromStart.add(hRange.from.s, 'seconds');
while (fromStart.unix() <= tRange.to.unix()) {
while (hRange.from.dayOfWeek && hRange.from.dayOfWeek !== fromStart.isoWeekday()) {
fromStart.add(24, 'hours');
}
if (fromStart.unix() > tRange.to.unix()) {
break;
}
fromEnd = dateTime(fromStart);
if (fromEnd.hour) {
if (hRange.from.h <= hRange.to.h) {
fromEnd.add(hRange.to.h - hRange.from.h, 'hours');
} else if (hRange.from.h > hRange.to.h) {
while (fromEnd.hour() !== hRange.to.h) {
fromEnd.add(1, 'hours');
}
} else {
fromEnd.add(24 - hRange.from.h, 'hours');
while (fromEnd.hour() !== hRange.to.h) {
fromEnd.add(1, 'hours');
}
for (const tr of panel.timeRegions) {
const timeRegion: GraphTimeRegionConfig = tr;
const regions = calculateTimesWithin(tr, tRange);
if (regions.length) {
const timeRegionColor = getColor(timeRegion, config.theme);
for (let j = 0; j < regions.length; j++) {
const r = regions[j];
if (timeRegion.fill) {
options.grid.markings.push({
xaxis: { from: r.from, to: r.to },
color: timeRegionColor.fill,
});
}
}
fromEnd.set('minute', hRange.to.m);
fromEnd.set('second', hRange.to.s);
while (hRange.to.dayOfWeek && hRange.to.dayOfWeek !== fromEnd.isoWeekday()) {
fromEnd.add(24, 'hours');
}
const outsideRange =
(fromStart.unix() < tRange.from.unix() && fromEnd.unix() < tRange.from.unix()) ||
(fromStart.unix() > tRange.to.unix() && fromEnd.unix() > tRange.to.unix());
if (!outsideRange) {
regions.push({ from: fromStart.valueOf(), to: fromEnd.valueOf() });
}
fromStart.add(24, 'hours');
}
timeRegionColor = getColor(timeRegion, config.theme);
for (let j = 0; j < regions.length; j++) {
const r = regions[j];
if (timeRegion.fill) {
options.grid.markings.push({
xaxis: { from: r.from, to: r.to },
color: timeRegionColor.fill,
});
}
if (timeRegion.line) {
options.grid.markings.push({
xaxis: { from: r.from, to: r.from },
color: timeRegionColor.line,
});
options.grid.markings.push({
xaxis: { from: r.to, to: r.to },
color: timeRegionColor.line,
});
if (timeRegion.line) {
options.grid.markings.push({
xaxis: { from: r.from, to: r.from },
color: timeRegionColor.line,
});
options.grid.markings.push({
xaxis: { from: r.to, to: r.to },
color: timeRegionColor.line,
});
}
}
}
}
}
parseTimeRange(str: string) {
const timeRegex = /^([\d]+):?(\d{2})?/;
const result: any = { h: null, m: null };
const match = timeRegex.exec(str);
if (!match) {
return result;
}
if (match.length > 1) {
result.h = Number(match[1]);
result.m = 0;
if (match.length > 2 && match[2] !== undefined) {
result.m = Number(match[2]);
}
if (result.h > 23) {
result.h = 23;
}
if (result.m > 59) {
result.m = 59;
}
}
return result;
}
}

Loading…
Cancel
Save