2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-17 12:35:46 +00:00

Added test statistics (#7164)

* Added test statistics

Fixed #5995

* Bump API version

* Fix javascript varible scopes

* Fix javascript exports

* Remove duplicated import

* Add files modified by the pre-commit scripts

* Move test statistics API urls to a separate endpoint

* Merge test-statistics urls

* Undo unrelated changes

* Formatting fix

* Fix API urls for test statistics in PUI

* Fix prefixing in test statistic functions

---------

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
This commit is contained in:
Miklós Márton
2024-07-31 08:23:00 +02:00
committed by GitHub
parent fdd9b7c77b
commit 3cbfcc11cb
19 changed files with 546 additions and 2 deletions

View File

@ -115,6 +115,8 @@ export enum ApiEndpoints {
stock_assign = 'stock/assign/',
stock_status = 'stock/status/',
stock_install = 'stock/:id/install',
build_test_statistics = 'test-statistics/by-build/:id',
part_test_statistics = 'test-statistics/by-part/:id',
// Generator API endpoints
generate_batch_code = 'generate/batch-code/',

View File

@ -11,6 +11,7 @@ import {
IconNotes,
IconPaperclip,
IconQrcode,
IconReportAnalytics,
IconSitemap
} from '@tabler/icons-react';
import { useMemo } from 'react';
@ -53,6 +54,7 @@ import { BuildOrderTable } from '../../tables/build/BuildOrderTable';
import BuildOutputTable from '../../tables/build/BuildOutputTable';
import { AttachmentTable } from '../../tables/general/AttachmentTable';
import { StockItemTable } from '../../tables/stock/StockItemTable';
import { TestStatisticsTable } from '../../tables/stock/TestStatisticsTable';
/**
* Detail page for a single Build Order
@ -305,6 +307,20 @@ export default function BuildDetail() {
<Skeleton />
)
},
{
name: 'test-statistics',
label: t`Test Statistics`,
icon: <IconReportAnalytics />,
content: (
<TestStatisticsTable
params={{
pk: build.pk,
apiEndpoint: ApiEndpoints.build_test_statistics
}}
/>
),
hidden: !build?.part_detail?.trackable
},
{
name: 'attachments',
label: t`Attachments`,

View File

@ -15,6 +15,7 @@ import {
IconNotes,
IconPackages,
IconPaperclip,
IconReportAnalytics,
IconShoppingCart,
IconStack2,
IconTestPipe,
@ -85,6 +86,7 @@ import { ManufacturerPartTable } from '../../tables/purchasing/ManufacturerPartT
import { SupplierPartTable } from '../../tables/purchasing/SupplierPartTable';
import { SalesOrderTable } from '../../tables/sales/SalesOrderTable';
import { StockItemTable } from '../../tables/stock/StockItemTable';
import { TestStatisticsTable } from '../../tables/stock/TestStatisticsTable';
import PartPricingPanel from './PartPricingPanel';
/**
@ -630,6 +632,22 @@ export default function PartDetail() {
<Skeleton />
)
},
{
name: 'test_statistics',
label: t`Test Statistics`,
icon: <IconReportAnalytics />,
hidden: !part.trackable,
content: part?.pk ? (
<TestStatisticsTable
params={{
pk: part.pk,
apiEndpoint: ApiEndpoints.part_test_statistics
}}
/>
) : (
<Skeleton />
)
},
{
name: 'related_parts',
label: t`Related Parts`,

View File

@ -0,0 +1,136 @@
import { t } from '@lingui/macro';
import { useCallback, useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { AddItemButton } from '../../components/buttons/AddItemButton';
import { ApiEndpoints } from '../../enums/ApiEndpoints';
import { ModelType } from '../../enums/ModelType';
import { UserRoles } from '../../enums/Roles';
import { stockLocationFields } from '../../forms/StockForms';
import { getDetailUrl } from '../../functions/urls';
import {
useCreateApiFormModal,
useEditApiFormModal
} from '../../hooks/UseForm';
import { useTable } from '../../hooks/UseTable';
import { apiUrl } from '../../states/ApiState';
import { useUserState } from '../../states/UserState';
import { TableColumn } from '../Column';
import { BooleanColumn, DescriptionColumn } from '../ColumnRenderers';
import { TableFilter } from '../Filter';
import { InvenTreeTable } from '../InvenTreeTable';
export function TestStatisticsTable({ params = {} }: { params?: any }) {
const initialColumns: TableColumn[] = [];
const [templateColumnList, setTemplateColumnList] = useState(initialColumns);
const testTemplateColumns: TableColumn[] = useMemo(() => {
let data = templateColumnList ?? [];
return data;
}, [templateColumnList]);
const tableColumns: TableColumn[] = useMemo(() => {
const firstColumn: TableColumn = {
accessor: 'col_0',
title: '',
sortable: true,
switchable: false,
noWrap: true
};
const lastColumn: TableColumn = {
accessor: 'col_total',
sortable: true,
switchable: false,
noWrap: true,
title: t`Total`
};
return [firstColumn, ...testTemplateColumns, lastColumn];
}, [testTemplateColumns]);
function statCountString(count: number, total: number) {
if (count > 0) {
let percentage =
' (' +
((100.0 * count) / total).toLocaleString(undefined, {
minimumFractionDigits: 0,
maximumFractionDigits: 2
}) +
'%)';
return count.toString() + percentage;
}
return '-';
}
// Format the test results based on the returned data
const formatRecords = useCallback((records: any[]): any[] => {
// interface needed to being able to dynamically assign keys
interface ResultRow {
[key: string]: string;
}
// Construct a list of test templates
let results: ResultRow[] = [
{ id: 'row_passed', col_0: t`Passed` },
{ id: 'row_failed', col_0: t`Failed` },
{ id: 'row_total', col_0: t`Total` }
];
let columnIndex = 0;
columnIndex = 1;
let newColumns: TableColumn[] = [];
for (let key in records[0]) {
if (key == 'total') continue;
let acc = 'col_' + columnIndex.toString();
const resultKeys = ['passed', 'failed', 'total'];
results[0][acc] = statCountString(
records[0][key]['passed'],
records[0][key]['total']
);
results[1][acc] = statCountString(
records[0][key]['failed'],
records[0][key]['total']
);
results[2][acc] = records[0][key]['total'].toString();
newColumns.push({
accessor: 'col_' + columnIndex.toString(),
title: key
});
columnIndex++;
}
setTemplateColumnList(newColumns);
results[0]['col_total'] = statCountString(
records[0]['total']['passed'],
records[0]['total']['total']
);
results[1]['col_total'] = statCountString(
records[0]['total']['failed'],
records[0]['total']['total']
);
results[2]['col_total'] = records[0]['total']['total'].toString();
return results;
}, []);
const table = useTable('teststatistics');
return (
<InvenTreeTable
url={apiUrl(params.apiEndpoint, params.pk) + '/'}
tableState={table}
columns={tableColumns}
props={{
dataFormatter: formatRecords,
enableDownload: false,
enableSearch: false,
enablePagination: false
}}
/>
);
}