Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minor: added seconds to human-readable format scale for test case graph #17926

Merged
merged 6 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -165,6 +174,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 @@ -254,7 +271,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 @@ -138,6 +139,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 @@ -27,6 +27,7 @@ import {
toLower,
toNumber,
} from 'lodash';
import { Duration } from 'luxon';
import {
CurrentState,
ExtraInfo,
Expand Down Expand Up @@ -605,6 +606,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
Loading