Skip to content

Commit

Permalink
feat(asset): added support for IncludeOnlyDataInTimeRange (#96)
Browse files Browse the repository at this point in the history
Co-authored-by: Robert Aradei <[email protected]>
  • Loading branch information
saradei-ni and Robert Aradei authored Oct 24, 2024
1 parent 0e9642c commit 2f49843
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 40 deletions.
8 changes: 8 additions & 0 deletions src/datasources/asset/data-sources/AssetDataSourceBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ export abstract class AssetDataSourceBase extends DataSourceBase<AssetQuery, Ass
}
}

getQueryParams(): URLSearchParams {
return new URLSearchParams(window.location.search);
}

getQueryParam(param: string): string | null {
return this.getQueryParams().get(param);
}

public getCachedSystems(): SystemMetadata[] {
return Array.from(this.systemAliasCache.values());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const dayGroupCalibrationForecastResponseMock: CalibrationForecastResponse =
{
calibrationForecast: {
columns: [
{ name: "", values: ["2022-01-01T00:00:00.0000000Z", "2022-01-02T00:00:00.0000000Z", "2022-01-03T00:00:00.0000000Z"], columnDescriptors: [{ value: "Day", type: ColumnDescriptorType.Time }] },
{ name: AssetCalibrationForecastKey.Day, values: ["2022-01-01T00:00:00.0000000Z", "2022-01-02T00:00:00.0000000Z", "2022-01-03T00:00:00.0000000Z"], columnDescriptors: [{ value: AssetCalibrationForecastKey.Day, type: ColumnDescriptorType.Time }] },
{ name: "", values: [1, 2, 2], columnDescriptors: [{ value: "Assets", type: ColumnDescriptorType.Count }] }
]
}
Expand All @@ -48,7 +48,7 @@ const weekGroupCalibrationForecastResponseMock: CalibrationForecastResponse =
{
calibrationForecast: {
columns: [
{ name: "", values: ["2022-01-03T00:00:00.0000000Z", "2022-01-10T00:00:00.0000000Z", "2022-01-17T00:00:00.0000000Z"], columnDescriptors: [{ value: "Week", type: ColumnDescriptorType.Time }] },
{ name: AssetCalibrationForecastKey.Week, values: ["2022-01-03T00:00:00.0000000Z", "2022-01-10T00:00:00.0000000Z", "2022-01-17T00:00:00.0000000Z"], columnDescriptors: [{ value: AssetCalibrationForecastKey.Week, type: ColumnDescriptorType.Time }] },
{ name: "", values: [1, 2, 2], columnDescriptors: [{ value: "Assets", type: ColumnDescriptorType.Count }] }
]
}
Expand All @@ -58,8 +58,8 @@ const weekGroupCalibrationForecastDataLinkResponseMock: CalibrationForecastRespo
{
calibrationForecast: {
columns: [
{ name: "", values: ["2022-01-03T00:00:00.0000000Z", "2022-01-10T00:00:00.0000000Z", "2022-01-17T00:00:00.0000000Z"], columnDescriptors: [{ value: "Week", type: ColumnDescriptorType.Time }] },
{ name: "", values: [1, 2, 2], columnDescriptors: [{ value: "Assets", type: ColumnDescriptorType.Count }] }
{ name: AssetCalibrationForecastKey.Week, values: ["2022-01-01T00:00:00.0000000Z", "2022-01-08T00:00:00.0000000Z", "2022-01-15T00:00:00.0000000Z"], columnDescriptors: [{ value: AssetCalibrationForecastKey.Week, type: ColumnDescriptorType.Time }] },
{ name: "", values: [3, 2, 2], columnDescriptors: [{ value: "Assets", type: ColumnDescriptorType.Count }] }
]
}
}
Expand Down Expand Up @@ -629,17 +629,17 @@ describe('Time based data links', () => {
const [_day, assets] = result.data[0].fields;
const [dataLink] = assets.config.links;

const dayDate = new Date('2022-01-01T00:00:00.0000000Z');
const from = new Date('2022-01-01T00:00:00.0000000Z');

expect(dataLink.title).toBe(`View ${AssetCalibrationForecastKey.Day}`);
expect(dataLink.targetBlank).toBe(true);
expect(dataLink.url).toContain('/d/${__dashboard.uid}/${__dashboard}?orgId=${__org.id}');

const builtUrl = dataLink.onBuildUrl({
replaceVariables: (value: string) => value.replace('${__data.fields.Day}', dayDate.toISOString())
replaceVariables: (value: string) => value.replace('${__data.fields.Day}', from.toISOString())
});

expect(builtUrl).toContain(`&from=${dayDate.valueOf()}&to=${dayDate.valueOf()}`);
expect(builtUrl).toMatchSnapshot();
});

test('creates data links for Week grouping', async () => {
Expand All @@ -662,7 +662,7 @@ describe('Time based data links', () => {
replaceVariables: (value: string) => value.replace('${__data.fields.Week}', `${weekStartDate.toISOString()} : ${weekEndDate.toISOString()}`)
});

expect(builtUrl).toContain(`&from=${weekStartDate.valueOf()}&to=${weekEndDate.valueOf()}`);
expect(builtUrl).toMatchSnapshot();
});

test('creates data links for Month grouping', async () => {
Expand All @@ -685,8 +685,90 @@ describe('Time based data links', () => {
replaceVariables: (value: string) => value.replace('${__data.fields.Month}', monthDate.toISOString())
});

monthDate.setHours(12);
expect(builtUrl).toMatchSnapshot();
});
});

describe('Run query with IncludeOnlyDataInTimeRange flag', () => {
let processCalibrationForecastQuerySpy: jest.SpyInstance;

beforeEach(() => {
processCalibrationForecastQuerySpy = jest.spyOn(datastore, 'processCalibrationForecastQuery').mockImplementation();
});

test('should set IncludeOnlyDataInTimeRange to true', async () => {
const query = buildCalibrationForecastQuery({
refId: '',
type: AssetQueryType.CalibrationForecast,
groupBy: [AssetCalibrationTimeBasedGroupByType.Month],
});

jest.spyOn(datastore, 'getQueryParam').mockReturnValue('true');
await datastore.query(query);

expect(processCalibrationForecastQuerySpy).toHaveBeenCalledWith(
expect.objectContaining({
groupBy: [AssetCalibrationTimeBasedGroupByType.Month],
IncludeOnlyDataInTimeRange: true
}),
expect.anything()
);
});

test('should set IncludeOnlyDataInTimeRange to true case insensitive', async () => {
const query = buildCalibrationForecastQuery({
refId: '',
type: AssetQueryType.CalibrationForecast,
groupBy: [AssetCalibrationTimeBasedGroupByType.Month],
});

jest.spyOn(datastore, 'getQueryParam').mockReturnValue('tRUe');
await datastore.query(query);

expect(builtUrl).toContain(`&from=${monthDate.valueOf()}&to=${monthDate.valueOf()}`);
expect(processCalibrationForecastQuerySpy).toHaveBeenCalledWith(
expect.objectContaining({
groupBy: [AssetCalibrationTimeBasedGroupByType.Month],
IncludeOnlyDataInTimeRange: true
}),
expect.anything()
);
});

test('should set IncludeOnlyDataInTimeRange to false', async () => {
const query = buildCalibrationForecastQuery({
refId: '',
type: AssetQueryType.CalibrationForecast,
groupBy: [AssetCalibrationTimeBasedGroupByType.Month],
});

jest.spyOn(datastore, 'getQueryParam').mockReturnValue('false');
await datastore.query(query);

expect(processCalibrationForecastQuerySpy).toHaveBeenCalledWith(
expect.objectContaining({
groupBy: [AssetCalibrationTimeBasedGroupByType.Month],
IncludeOnlyDataInTimeRange: false
}),
expect.anything()
);
});

test('should set IncludeOnlyDataInTimeRange to false if missing', async () => {
const query = buildCalibrationForecastQuery({
refId: '',
type: AssetQueryType.CalibrationForecast,
groupBy: [AssetCalibrationTimeBasedGroupByType.Month],
});

jest.spyOn(datastore, 'getQueryParam').mockReturnValue(null);
await datastore.query(query);

expect(processCalibrationForecastQuerySpy).toHaveBeenCalledWith(
expect.objectContaining({
groupBy: [AssetCalibrationTimeBasedGroupByType.Month],
IncludeOnlyDataInTimeRange: false
}),
expect.anything()
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ import { AssetModel, AssetsResponse } from '../../../asset-common/types';
export class CalibrationForecastDataSource extends AssetDataSourceBase {
private dependenciesLoadedPromise: Promise<void>;

private readonly forecastDateFormatterMap = new Map<AssetCalibrationForecastKey, (date: string) => string>([
[AssetCalibrationForecastKey.Day, this.formatDateForDay],
[AssetCalibrationForecastKey.Week, this.formatDateForWeek],
[AssetCalibrationForecastKey.Month, this.formatDateForMonth]
]);

constructor(
readonly instanceSettings: DataSourceInstanceSettings<AssetDataSourceOptions>,
readonly backendSrv: BackendSrv = getBackendSrv(),
Expand Down Expand Up @@ -46,6 +52,7 @@ export class CalibrationForecastDataSource extends AssetDataSourceBase {
);
}

calibrationForecastQuery.IncludeOnlyDataInTimeRange = this.getQueryParam('IncludeOnlyDataInTimeRange')?.toLowerCase() === 'true';
return await this.processCalibrationForecastQuery(calibrationForecastQuery, options);
}

Expand All @@ -63,7 +70,13 @@ export class CalibrationForecastDataSource extends AssetDataSourceBase {
const from = options.range!.from.toISOString();
const to = options.range!.to.toISOString();

const calibrationForecastResponse: CalibrationForecastResponse = await this.queryCalibrationForecast(query.groupBy, from, to, query.filter);
const calibrationForecastResponse: CalibrationForecastResponse = await this.queryCalibrationForecast(
query.groupBy,
from,
to,
query.filter,
query.IncludeOnlyDataInTimeRange
);

result.fields = calibrationForecastResponse.calibrationForecast.columns || [];

Expand Down Expand Up @@ -91,46 +104,65 @@ export class CalibrationForecastDataSource extends AssetDataSourceBase {
}

processResultsGroupedByTime(result: DataFrameDTO, timeGrouping: AssetCalibrationForecastKey) {
const timeFieldFirstValue = result.fields.find(field => field.name === timeGrouping)?.values?.at(0);
const startDate = this.forecastDateFormatterMap.get(timeGrouping)!(timeFieldFirstValue);

result.fields.forEach(field => {
field.name = this.createColumnNameFromDescriptor(field as FieldDTOWithDescriptor);
switch (field.name) {
case AssetCalibrationForecastKey.Day:
field.values = field.values!.map(this.formatDateForDay)
break;
case AssetCalibrationForecastKey.Week:
field.values = field.values!.map(this.formatDateForWeek)
break;
case AssetCalibrationForecastKey.Month:
field.values = field.values!.map(this.formatDateForMonth)
break;
default:
field.config = { links: this.createDataLinks(timeGrouping) };
break;
const formatter = this.forecastDateFormatterMap.get(field.name as AssetCalibrationForecastKey);

if (formatter) {
field.values = field.values!.map(formatter);
}

if (!formatter && !Boolean(this.getQueryParam('IncludeOnlyDataInTimeRange'))) {
field.config = { links: this.createDataLinks(timeGrouping, startDate) };
}
});
}

private createDataLinks(timeGrouping: AssetCalibrationForecastKey): DataLink[] {
private createDataLinks(timeGrouping: AssetCalibrationForecastKey, startDate: string): DataLink[] {
const url = '/d/${__dashboard.uid}/${__dashboard}?orgId=${__org.id}&${__all_variables}';

return [
{
title: `View ${timeGrouping}`, targetBlank: true, url: `${url}`,
onBuildUrl: function (options) {
if (options.replaceVariables) {
const value = options.replaceVariables(`\${__data.fields.${timeGrouping}}`);
if (timeGrouping === AssetCalibrationForecastKey.Week) {
const [from, to] = value.split(' : ').map(rangeItem => new Date(rangeItem).valueOf());
return `${url}&from=${from}&to=${to}`;
}

const date = new Date(value);
if (timeGrouping === AssetCalibrationForecastKey.Month) {
date.setHours(12);
}
return `${url}&from=${date.valueOf()}&to=${date.valueOf()}`;
if (!options.replaceVariables) {
return url;
}

const value = options.replaceVariables(`\${__data.fields.${timeGrouping}}`);
const IncludeOnlyDataInTimeRange = value !== startDate;

let from, to;

const parseDate = (dateStr: string) => {
const date = new Date(dateStr);
date.setUTCHours(0);
return date;
};

switch (timeGrouping) {
case AssetCalibrationForecastKey.Day:
const dayDate = parseDate(value);
from = dayDate.valueOf();
dayDate.setUTCDate(dayDate.getUTCDate() + 1);
to = dayDate.valueOf();
break;
case AssetCalibrationForecastKey.Week:
[from, to] = value.split(' : ').map(dateStr => parseDate(dateStr).valueOf());
break;
case AssetCalibrationForecastKey.Month:
const monthDate = parseDate(value);
monthDate.setUTCDate(monthDate.getUTCDate() + 1);
from = monthDate.valueOf();
monthDate.setUTCMonth(monthDate.getUTCMonth() + 1);
to = monthDate.valueOf();
break;
}
return url;

return `${url}&from=${from}&to=${to}&IncludeOnlyDataInTimeRange=${IncludeOnlyDataInTimeRange}`;
}
}
];
Expand Down Expand Up @@ -173,9 +205,11 @@ export class CalibrationForecastDataSource extends AssetDataSourceBase {
}

formatDateForWeek(date: string): string {
console.log(date);
const startDate = new Date(date);
const endDate = new Date(startDate);
endDate.setDate(startDate.getDate() + 6);
console.log(startDate)
return `${startDate.toISOString().split('T')[0]} : ${endDate.toISOString().split('T')[0]}`;
}

Expand All @@ -193,8 +227,8 @@ export class CalibrationForecastDataSource extends AssetDataSourceBase {
}
}

async queryCalibrationForecast(groupBy: string[], startTime: string, endTime: string, filter = ''): Promise<CalibrationForecastResponse> {
let data = { groupBy, startTime, endTime, filter };
async queryCalibrationForecast(groupBy: string[], startTime: string, endTime: string, filter = '', IncludeOnlyDataInTimeRange = false): Promise<CalibrationForecastResponse> {
let data = { groupBy, startTime, endTime, filter, IncludeOnlyDataInTimeRange };
try {
let response = await this.post<CalibrationForecastResponse>(this.baseUrl + '/assets/calibration-forecast', data);
return response;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Time based data links creates data links for Day grouping 1`] = `"/d/\${__dashboard.uid}/\${__dashboard}?orgId=\${__org.id}&\${__all_variables}&from=1640995200000&to=1641081600000&IncludeOnlyDataInTimeRange=true"`;

exports[`Time based data links creates data links for Month grouping 1`] = `"/d/\${__dashboard.uid}/\${__dashboard}?orgId=\${__org.id}&\${__all_variables}&from=1641081600000&to=1643760000000&IncludeOnlyDataInTimeRange=true"`;

exports[`Time based data links creates data links for Week grouping 1`] = `"/d/\${__dashboard.uid}/\${__dashboard}?orgId=\${__org.id}&\${__all_variables}&from=1641168000000&to=1641689999999&IncludeOnlyDataInTimeRange=true"`;

exports[`queries asset calibration forecast with day groupBy 1`] = `
[
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { AssetQuery } from './types';
export interface CalibrationForecastQuery extends AssetQuery {
groupBy: string[];
filter?: string;
IncludeOnlyDataInTimeRange?: boolean;
}

export interface CalibrationForecastResponse {
Expand Down

0 comments on commit 2f49843

Please sign in to comment.