mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Graph: move time region calculation to a utility function (#51413)
This commit is contained in:
parent
7bb644d910
commit
ededf1dd6f
@ -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"],
|
||||
|
43
public/app/core/utils/timeRegions.test.ts
Normal file
43
public/app/core/utils/timeRegions.test.ts
Normal file
@ -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,
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
168
public/app/core/utils/timeRegions.ts
Normal file
168
public/app/core/utils/timeRegions.ts
Normal file
@ -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;
|
||||
for (const tr of panel.timeRegions) {
|
||||
const timeRegion: GraphTimeRegionConfig = tr;
|
||||
const regions = calculateTimesWithin(tr, tRange);
|
||||
if (regions.length) {
|
||||
const timeRegionColor = getColor(timeRegion, config.theme);
|
||||
|
||||
const timeRegionsCopy = panel.timeRegions.map((a: any) => ({ ...a }));
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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…
Reference in New Issue
Block a user