Profiler & Data Quality UI feedback & improvement #7090 part 4 (#7135)

* Profiler & Data Quality UI feedback & improvement #7090 part 4

* added label for type and status filter
This commit is contained in:
Shailesh Parmar 2022-09-02 09:27:16 +05:30 committed by GitHub
parent 4e335796ba
commit 8f58149881
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 167 additions and 15 deletions

View File

@ -0,0 +1,40 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_13827_30653)">
<g filter="url(#filter0_d_13827_30653)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M27.4785 21.2088L21.6481 14.1578C21.3683 13.8259 20.9594 13.625 20.5288 13.625H7.99314C7.56278 13.625 7.15386 13.8259 6.8741 14.1578L1.04395 21.2088V24.8924H27.4787V21.2088H27.4785Z" fill="#9CA3AF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.98357 4.52344H22.5385C22.797 4.52344 23.0449 4.62435 23.2278 4.80399C23.4106 4.98362 23.5133 5.22725 23.5133 5.48129V27.8337C23.5133 28.0877 23.4106 28.3313 23.2278 28.511C23.0449 28.6906 22.797 28.7915 22.5385 28.7915H5.98357C5.72504 28.7915 5.4771 28.6906 5.2943 28.511C5.11149 28.3313 5.00879 28.0877 5.00879 27.8337V5.48129C5.00879 5.22725 5.11149 4.98362 5.2943 4.80399C5.4771 4.62435 5.72504 4.52344 5.98357 4.52344V4.52344Z" fill="#DCE3EC"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M27.4249 29.6739C27.236 30.4093 26.5727 30.9575 25.7836 30.9575H2.73909C1.95 30.9575 1.28667 30.4091 1.09805 29.6739C1.06207 29.5338 1.0439 29.3898 1.04395 29.2453V21.2073H7.4575C8.16592 21.2073 8.7369 21.7935 8.7369 22.5052V22.5147C8.7369 23.2262 9.31446 23.8006 10.0229 23.8006H18.4998C19.2082 23.8006 19.7858 23.2209 19.7858 22.5092V22.5064C19.7858 21.7947 20.3568 21.207 21.0652 21.207H27.4787V29.2455C27.4787 29.3933 27.46 29.5367 27.4249 29.6739Z" fill="#C9D2DD"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.13985 6.90625H20.3823C20.5116 6.90625 20.6356 6.95671 20.727 7.04652C20.8184 7.13634 20.8697 7.25816 20.8697 7.38517V13.3614C20.8697 13.4885 20.8184 13.6103 20.727 13.7001C20.6356 13.7899 20.5116 13.8404 20.3823 13.8404H8.13985C8.01059 13.8404 7.88662 13.7899 7.79522 13.7001C7.70381 13.6103 7.65247 13.4885 7.65247 13.3614V7.38517C7.65247 7.25816 7.70381 7.13634 7.79522 7.04652C7.88662 6.95671 8.01059 6.90625 8.13985 6.90625ZM8.2037 16.4402H20.3185C20.4647 16.4402 20.6049 16.4973 20.7083 16.5989C20.8117 16.7004 20.8697 16.8382 20.8697 16.9819C20.8697 17.1255 20.8117 17.2633 20.7083 17.3649C20.6049 17.4665 20.4647 17.5235 20.3185 17.5235H8.2037C8.0575 17.5235 7.9173 17.4665 7.81392 17.3649C7.71054 17.2633 7.65247 17.1255 7.65247 16.9819C7.65247 16.8382 7.71054 16.7004 7.81392 16.5989C7.9173 16.4973 8.0575 16.4402 8.2037 16.4402ZM8.2037 19.257H20.3185C20.4647 19.257 20.605 19.3141 20.7084 19.4157C20.8118 19.5173 20.8699 19.6551 20.8699 19.7988C20.8699 19.9425 20.8118 20.0803 20.7084 20.1819C20.605 20.2835 20.4647 20.3406 20.3185 20.3406H8.2037C8.05747 20.3406 7.91723 20.2835 7.81383 20.1819C7.71043 20.0803 7.65234 19.9425 7.65234 19.7988C7.65234 19.6551 7.71043 19.5173 7.81383 19.4157C7.91723 19.3141 8.05747 19.257 8.2037 19.257Z" fill="#9CA3AF"/>
</g>
<g filter="url(#filter1_d_13827_30653)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.9778 7.44172L22.6112 7.95106C22.5761 7.96417 22.5378 7.9675 22.5008 7.96066C22.4639 7.95382 22.4296 7.93709 22.4021 7.91239C22.3746 7.88769 22.3549 7.85603 22.3452 7.82105C22.3356 7.78607 22.3364 7.74919 22.3477 7.71465L22.7352 6.52166C22.2172 5.95582 21.9131 5.26582 21.9131 4.52123C21.9131 2.60018 23.9376 1.04297 26.435 1.04297C28.9319 1.04297 30.9566 2.60018 30.9566 4.52123C30.9566 6.44228 28.9321 7.99949 26.4348 7.99949C25.5288 7.99949 24.6853 7.7946 23.9778 7.44172Z" fill="#1890FF"/>
<path d="M28.085 4.93784C28.3962 4.93784 28.6486 4.68855 28.6486 4.38103C28.6486 4.07351 28.3962 3.82422 28.085 3.82422C27.7738 3.82422 27.5215 4.07351 27.5215 4.38103C27.5215 4.68855 27.7738 4.93784 28.085 4.93784Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.1271 4.8689H24L24.5732 3.89453L25.1271 4.8689ZM25.8314 3.89453H26.8175V4.8689H25.8314V3.89453Z" fill="white"/>
</g>
</g>
<defs>
<filter id="filter0_d_13827_30653" x="-0.956055" y="2.52344" width="30.4346" height="30.4336" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="1"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_13827_30653"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_13827_30653" result="shape"/>
</filter>
<filter id="filter1_d_13827_30653" x="17.9131" y="-0.957031" width="17.0439" height="14.957" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="2"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_13827_30653"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_13827_30653" result="shape"/>
</filter>
<clipPath id="clip0_13827_30653">
<rect width="32" height="32" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -10,7 +10,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Button, Col, Radio, Row, Select, Space } from 'antd';
import { Button, Col, Form, Radio, Row, Select, Space } from 'antd';
import { RadioChangeEvent } from 'antd/lib/radio';
import { AxiosError } from 'axios';
import { EntityTags, ExtraInfo } from 'Models';
@ -35,6 +35,7 @@ import {
Table,
TestCaseStatus,
} from '../../generated/entity/data/table';
import { EntityType as TestType } from '../../generated/tests/testDefinition';
import { EntityReference } from '../../generated/type/entityReference';
import { LabelType, State } from '../../generated/type/tagLabel';
import jsonData from '../../jsons/en';
@ -86,6 +87,7 @@ const ProfilerDashboard: React.FC<ProfilerDashboardProps> = ({
);
const [selectedTestCaseStatus, setSelectedTestCaseStatus] =
useState<string>('');
const [selectedTestType, setSelectedTestType] = useState('');
const [selectedTimeRange, setSelectedTimeRange] =
useState<keyof typeof PROFILER_FILTER_RANGE>('last3days');
const [activeColumnDetails, setActiveColumnDetails] = useState<Column>(
@ -117,7 +119,22 @@ const ProfilerDashboard: React.FC<ProfilerDashboardProps> = ({
value: value,
}));
testCaseStatus.unshift({
label: 'All Test',
label: 'All',
value: '',
});
return testCaseStatus;
}, []);
const testCaseTypeOption = useMemo(() => {
const testCaseStatus: Record<string, string>[] = Object.entries(
TestType
).map(([key, value]) => ({
label: key,
value: value,
}));
testCaseStatus.unshift({
label: 'All',
value: '',
});
@ -145,11 +162,13 @@ const ProfilerDashboard: React.FC<ProfilerDashboardProps> = ({
},
{
name: getPartialNameFromTableFQN(fqn, [FqnPart.Database]),
url: getDatabaseDetailsPath(fqn),
url: getDatabaseDetailsPath(table.database?.fullyQualifiedName || ''),
},
{
name: getPartialNameFromTableFQN(fqn, [FqnPart.Schema]),
url: getDatabaseSchemaDetailsPath(fqn),
url: getDatabaseSchemaDetailsPath(
table.databaseSchema?.fullyQualifiedName || ''
),
},
{
name: getEntityName(table),
@ -331,12 +350,29 @@ const ProfilerDashboard: React.FC<ProfilerDashboardProps> = ({
}
};
const handleTestCaseTypeChange = (value: string) => {
if (value !== selectedTestType) {
setSelectedTestType(value);
}
};
const getFilterTestCase = () => {
return testCases.filter(
const dataByStatus = testCases.filter(
(data) =>
selectedTestCaseStatus === '' ||
data.testCaseResult?.testCaseStatus === selectedTestCaseStatus
);
return isColumnView
? dataByStatus
: dataByStatus.filter(
(data) =>
selectedTestType === '' ||
(selectedTestType === TestType.Table &&
entityTypeFQN === data.entityFQN) ||
(selectedTestType === TestType.Column &&
entityTypeFQN !== data.entityFQN)
);
};
useEffect(() => {
@ -390,13 +426,24 @@ const ProfilerDashboard: React.FC<ProfilerDashboardProps> = ({
/>
<Space size={16}>
{activeTab === ProfilerDashboardTab.DATA_QUALITY &&
!isColumnView && (
<Form.Item className="tw-mb-0 tw-w-40" label="Type">
<Select
options={testCaseTypeOption}
value={selectedTestType}
onChange={handleTestCaseTypeChange}
/>
</Form.Item>
)}
{activeTab === ProfilerDashboardTab.DATA_QUALITY && (
<Select
className="tw-w-32"
options={testCaseStatusOption}
value={selectedTestCaseStatus}
onChange={handleTestCaseStatusChange}
/>
<Form.Item className="tw-mb-0 tw-w-40" label="Status">
<Select
options={testCaseStatusOption}
value={selectedTestCaseStatus}
onChange={handleTestCaseStatusChange}
/>
</Form.Item>
)}
{activeTab === ProfilerDashboardTab.PROFILER && (
<Select

View File

@ -16,11 +16,18 @@ import { ColumnsType } from 'antd/lib/table';
import { isUndefined } from 'lodash';
import moment from 'moment';
import React, { useMemo, useState } from 'react';
import { Link } from 'react-router-dom';
import { ReactComponent as ArrowDown } from '../../../assets/svg/arrow-down.svg';
import { ReactComponent as ArrowRight } from '../../../assets/svg/arrow-right.svg';
import { getTableTabPath } from '../../../constants/constants';
import { TestCase, TestCaseResult } from '../../../generated/tests/testCase';
import { getEntityName, getNameFromFQN } from '../../../utils/CommonUtils';
import { getTestSuitePath } from '../../../utils/RouterUtils';
import SVGIcons from '../../../utils/SvgUtils';
import { getTestResultBadgeIcon } from '../../../utils/TableUtils';
import {
getEntityFqnFromEntityLink,
getTestResultBadgeIcon,
} from '../../../utils/TableUtils';
import EditTestCaseModal from '../../AddDataQualityTest/EditTestCaseModal';
import DeleteWidgetModal from '../../common/DeleteWidget/DeleteWidgetModal';
import { DataQualityTabProps } from '../profilerDashboard.interface';
@ -72,6 +79,37 @@ const DataQualityTab: React.FC<DataQualityTabProps> = ({
dataIndex: 'description',
key: 'description',
},
{
title: 'Table',
dataIndex: 'table',
key: 'table',
render: (_, record) => {
const tableFqn = getEntityFqnFromEntityLink(record.entityLink);
const name = getNameFromFQN(tableFqn);
return (
<Link
to={getTableTabPath(tableFqn, 'profiler')}
onClick={(e) => e.stopPropagation()}>
{name}
</Link>
);
},
},
{
title: 'Test Suite',
dataIndex: 'testSuite',
key: 'testSuite',
render: (_, record) => {
return (
<Link
to={getTestSuitePath(record?.testSuite?.fullyQualifiedName || '')}
onClick={(e) => e.stopPropagation()}>
{getEntityName(record?.testSuite)}
</Link>
);
},
},
{
title: 'Actions',
dataIndex: 'actions',

View File

@ -22,9 +22,10 @@ import {
} from 'antd';
import { AxiosError } from 'axios';
import classNames from 'classnames';
import { isEmpty } from 'lodash';
import { isEmpty, isUndefined } from 'lodash';
import React, { FC, useEffect, useMemo, useState } from 'react';
import { Link, useHistory } from 'react-router-dom';
import { ReactComponent as NoDataIcon } from '../../assets/svg/no-data-icon.svg';
import { getListTestCase } from '../../axiosAPIs/testAPI';
import { API_RES_MAX_SIZE } from '../../constants/constants';
import { NO_PERMISSION_FOR_ACTION } from '../../constants/HelperTextUtil';
@ -205,6 +206,25 @@ const TableProfilerV1: FC<TableProfilerProps> = ({
</Space>
</Row>
{isUndefined(profile) && (
<div className="tw-border tw-flex tw-items-center tw-border-warning tw-rounded tw-p-2 tw-mb-4">
<NoDataIcon />
<p className="tw-mb-0 tw-ml-2">
Data Profiler is an optional configuration in Ingestion. Please
enable the data profiler by following the documentation
<Link
className="tw-ml-1"
target="_blank"
to={{
pathname:
'https://docs.open-metadata.org/openmetadata/ingestion/workflows/profiler',
}}>
here.
</Link>
</p>
</div>
)}
<Row className="tw-rounded tw-border tw-p-4 tw-mb-4">
{overallSummery.map((summery) => (
<Col

View File

@ -68,8 +68,9 @@ const ProfilerDashboardPage = () => {
const fetchTestCases = async (fqn: string) => {
try {
const { data } = await getListTestCase({
fields: 'testDefinition,testCaseResult',
fields: 'testDefinition,testCaseResult,testSuite',
entityLink: fqn,
includeAllTests: !isColumnView,
limit: API_RES_MAX_SIZE,
});
setTestCases(data);

View File

@ -123,7 +123,7 @@ const TestSuiteDetailsPage = () => {
setIsTestCaseLoaded(false);
try {
const response = await getListTestCase({
fields: 'testCaseResult,testDefinition',
fields: 'testCaseResult,testDefinition,testSuite',
testSuiteId: testSuiteId,
limit: limit || PAGE_SIZE,
before: param && param.before,

View File

@ -323,6 +323,12 @@ export const generateEntityLink = (fqn: string, includeColumn = false) => {
}
};
export const getEntityFqnFromEntityLink = (entityLink: string) => {
const link = entityLink.split('>')[0];
return link.split('::')[2];
};
export const getTestResultBadgeIcon = (status?: TestCaseStatus) => {
switch (status) {
case TestCaseStatus.Success: