Skip to content

Commit

Permalink
Minor: added seconds to human-readable format scale for test case gra…
Browse files Browse the repository at this point in the history
…ph (#17926)

* Minor: added milliseconds to human-readable format scale for test case graph

* addressing comment

* fixed unit test

* addressing comment
  • Loading branch information
ShaileshParmar11 committed Sep 20, 2024
1 parent c75378f commit 3ba2fd8
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ const TestSummary: React.FC<TestSummaryProps> = ({ data }) => {
testCaseName={data.name}
testCaseParameterValue={data.parameterValues}
testCaseResults={results}
testDefinitionName={data.testDefinition.name}
/>
);
}, [isGraphLoading, data, results, selectedTimeRange]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ export interface TestSummaryGraphProps {
testCaseResults: TestCaseResult[];
selectedTimeRange: string;
minHeight?: number;
testDefinitionName?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ import {
GRAPH_BACKGROUND_COLOR,
HOVER_CHART_OPACITY,
} from '../../../../constants/constants';
import {
TABLE_DATA_TO_BE_FRESH,
TABLE_FRESHNESS_KEY,
} from '../../../../constants/TestSuite.constant';
import { ERROR_PLACEHOLDER_TYPE } from '../../../../enums/common.enum';
import {
Thread,
Expand All @@ -56,6 +60,7 @@ import {
axisTickFormatter,
updateActiveChartFilter,
} from '../../../../utils/ChartUtils';
import { formatTimeFromSeconds } from '../../../../utils/CommonUtils';
import { prepareChartData } from '../../../../utils/DataQuality/TestSummaryGraphUtils';
import { formatDateTime } from '../../../../utils/date-time/DateTimeUtils';
import { useActivityFeedProvider } from '../../../ActivityFeed/ActivityFeedProvider/ActivityFeedProvider';
Expand All @@ -70,6 +75,7 @@ function TestSummaryGraph({
testCaseResults,
selectedTimeRange,
minHeight,
testDefinitionName,
}: Readonly<TestSummaryGraphProps>) {
const { t } = useTranslation();
const { entityThread = [] } = useActivityFeedProvider();
Expand All @@ -92,15 +98,18 @@ function TestSummaryGraph({
: -200;
}, [chartRef, chartMouseEvent]);

const chartData = useMemo(() => {
const { chartData, isFreshnessTest } = useMemo(() => {
const data = prepareChartData({
testCaseParameterValue: testCaseParameterValue ?? [],
testCaseResults,
entityThread,
});
setShowAILearningBanner(data.showAILearningBanner);
const isFreshnessTest = data.information.some(
(value) => value.label === TABLE_FRESHNESS_KEY
);

return data;
return { chartData: data, isFreshnessTest };
}, [testCaseResults, entityThread, testCaseParameterValue]);

const incidentData = useMemo(() => {
Expand Down Expand Up @@ -164,6 +173,14 @@ function TestSummaryGraph({
setActiveMouseHoverKey('');
};

// Todo: need to find better approach to create dynamic scale for graph, need to work with @TeddyCr for the same!
const formatYAxis = (value: number) => {
// table freshness will always have output value in seconds
return testDefinitionName === TABLE_DATA_TO_BE_FRESH || isFreshnessTest
? formatTimeFromSeconds(value)
: axisTickFormatter(value);
};

const updatedDot: LineProps['dot'] = (props): ReactElement<SVGElement> => {
const { cx = 0, cy = 0, payload } = props;
let fill = payload.status === TestCaseStatus.Success ? GREEN_3 : undefined;
Expand Down Expand Up @@ -253,7 +270,8 @@ function TestSummaryGraph({
allowDataOverflow
domain={['min', 'max']}
padding={{ top: 8, bottom: 8 }}
tickFormatter={(value) => axisTickFormatter(value)}
tickFormatter={formatYAxis}
width={80}
/>
<Tooltip
content={<TestSummaryCustomTooltip />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import React, { Fragment } from 'react';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import { TooltipProps } from 'recharts';
import { TABLE_FRESHNESS_KEY } from '../../../../constants/TestSuite.constant';
import { Thread } from '../../../../generated/entity/feed/thread';
import { formatTimeFromSeconds } from '../../../../utils/CommonUtils';
import { formatDateTime } from '../../../../utils/date-time/DateTimeUtils';
import { getTaskDetailPath } from '../../../../utils/TasksUtils';
import { OwnerLabel } from '../../../common/OwnerLabel/OwnerLabel.component';
Expand Down Expand Up @@ -78,7 +80,10 @@ const TestSummaryCustomTooltip = (
{startCase(key)}
</span>
<span className="font-medium" data-testid={key}>
{value}
{/* freshness will always be in seconds */}
{key === TABLE_FRESHNESS_KEY && isNumber(value)
? formatTimeFromSeconds(value)
: value}
</span>
</li>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ jest.mock('../../../../utils/TasksUtils', () => ({
jest.mock('../../../common/OwnerLabel/OwnerLabel.component', () => ({
OwnerLabel: jest.fn().mockReturnValue(<div>OwnerLabel</div>),
}));
jest.mock('../../../../utils/CommonUtils', () => ({
formatTimeFromSeconds: jest.fn().mockReturnValue('1 hour'),
}));

describe('Test AddServicePage component', () => {
it('AddServicePage component should render', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ export const TEST_CASE_STATUS: Record<
};

export const TABLE_DIFF = 'tableDiff';
export const TABLE_DATA_TO_BE_FRESH = 'tableDataToBeFresh';
export const TABLE_FRESHNESS_KEY = 'freshness';

export const SUPPORTED_SERVICES_FOR_TABLE_DIFF = [
DatabaseServiceType.Snowflake,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
} from '../generated/type/tagLabel';
import {
digitFormatter,
formatTimeFromSeconds,
getBase64EncodedString,
getIngestionFrequency,
getIsErrorMatch,
Expand Down Expand Up @@ -137,6 +138,29 @@ describe('Tests for CommonUtils', () => {
});
});

// formatTimeFromSeconds test
it('formatTimeFromSeconds formatter should format mills to human readable value', () => {
const values = [
{ input: 1, expected: '1 second' },
{ input: 2, expected: '2 seconds' },
{ input: 30, expected: '30 seconds' },
{ input: 60, expected: '1 minute' },
{ input: 120, expected: '2 minutes' },
{ input: 3600, expected: '1 hour' },
{ input: 7200, expected: '2 hours' },
{ input: 86400, expected: '1 day' },
{ input: 172800, expected: '2 days' },
{ input: 2592000, expected: '1 month' },
{ input: 5184000, expected: '2 months' },
{ input: 31536000, expected: '1 year' },
{ input: 63072000, expected: '2 years' },
];

values.map(({ input, expected }) => {
expect(formatTimeFromSeconds(input)).toEqual(expected);
});
});

describe('Tests for sortTagsCaseInsensitive function', () => {
it('GetErrorMessage match function should return true if match found', () => {
const result = getIsErrorMatch(
Expand Down
40 changes: 40 additions & 0 deletions openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
toLower,
toNumber,
} from 'lodash';
import { Duration } from 'luxon';
import {
CurrentState,
ExtraInfo,
Expand Down Expand Up @@ -604,6 +605,45 @@ export const digitFormatter = (value: number) => {
}).format(value);
};

/**
* Converts a duration in seconds to a human-readable format.
* The function returns the largest time unit (years, months, days, hours, minutes, or seconds)
* that is greater than or equal to one, rounded to the nearest whole number.
*
* @param {number} seconds - The duration in seconds to be converted.
* @returns {string} A string representing the duration in a human-readable format,
* e.g., "1 hour", "2 days", "3 months", etc.
*
* @example
* formatTimeFromSeconds(1); // returns "1 second"
* formatTimeFromSeconds(60); // returns "1 minute"
* formatTimeFromSeconds(3600); // returns "1 hour"
* formatTimeFromSeconds(86400); // returns "1 day"
*/
export const formatTimeFromSeconds = (seconds: number): string => {
const duration = Duration.fromObject({ seconds });
let unit: keyof Duration;

if (duration.as('years') >= 1) {
unit = 'years';
} else if (duration.as('months') >= 1) {
unit = 'months';
} else if (duration.as('days') >= 1) {
unit = 'days';
} else if (duration.as('hours') >= 1) {
unit = 'hours';
} else if (duration.as('minutes') >= 1) {
unit = 'minutes';
} else {
unit = 'seconds';
}

const value = Math.round(duration.as(unit));
const unitSingular = unit.slice(0, -1);

return `${value} ${value === 1 ? unitSingular : unit}`;
};

export const getTeamsUser = (
data: ExtraInfo,
currentUser: User
Expand Down

0 comments on commit 3ba2fd8

Please sign in to comment.