mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Complete refactor of the test-result table for stock item
This commit is contained in:
parent
b9799e1824
commit
8ace71ef56
@ -22,7 +22,7 @@ from .settings import MEDIA_URL, STATIC_URL
|
|||||||
def generateTestKey(test_name):
|
def generateTestKey(test_name):
|
||||||
"""
|
"""
|
||||||
Generate a test 'key' for a given test name.
|
Generate a test 'key' for a given test name.
|
||||||
This must not have spaces as it will be used for dict lookup in a template.
|
This must not have illegal chars as it will be used for dict lookup in a template.
|
||||||
|
|
||||||
Tests must be named such that they will have unique keys.
|
Tests must be named such that they will have unique keys.
|
||||||
"""
|
"""
|
||||||
@ -30,6 +30,9 @@ def generateTestKey(test_name):
|
|||||||
key = test_name.strip().lower()
|
key = test_name.strip().lower()
|
||||||
key = key.replace(" ", "")
|
key = key.replace(" ", "")
|
||||||
|
|
||||||
|
# Remove any characters that cannot be used to represent a variable
|
||||||
|
key = re.sub(r'[^a-zA-Z0-9]', '', key)
|
||||||
|
|
||||||
return key
|
return key
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
{% include "stock/tabs.html" with tab='tests' %}
|
{% include "stock/tabs.html" with tab='tests' %}
|
||||||
|
|
||||||
<h4>{% trans "Test Results" %}</h4>
|
<h4>{% trans "Test Data" %}</h4>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<div id='button-toolbar'>
|
<div id='button-toolbar'>
|
||||||
@ -30,11 +30,8 @@
|
|||||||
|
|
||||||
loadStockTestResultsTable(
|
loadStockTestResultsTable(
|
||||||
$("#test-result-table"), {
|
$("#test-result-table"), {
|
||||||
params: {
|
part: {{ item.part.id }},
|
||||||
stock_item: {{ item.id }},
|
stock_item: {{ item.id }},
|
||||||
user_detail: true,
|
|
||||||
attachment_detail: true,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -53,6 +50,21 @@ $("#add-test-result").click(function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#test-result-table").on('click', '.button-test-add', function() {
|
||||||
|
var button = $(this);
|
||||||
|
|
||||||
|
var test_name = button.attr('pk');
|
||||||
|
|
||||||
|
launchModalForm(
|
||||||
|
"{% url 'stock-item-test-create' %}", {
|
||||||
|
data: {
|
||||||
|
stock_item: {{ item.id }},
|
||||||
|
test: test_name
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
$("#test-result-table").on('click', '.button-test-edit', function() {
|
$("#test-result-table").on('click', '.button-test-edit', function() {
|
||||||
var button = $(this);
|
var button = $(this);
|
||||||
|
|
||||||
|
@ -254,6 +254,8 @@ class StockItemTestResultCreate(AjaxCreateView):
|
|||||||
except (ValueError, StockItem.DoesNotExist):
|
except (ValueError, StockItem.DoesNotExist):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
initials['test'] = self.request.GET.get('test', '')
|
||||||
|
|
||||||
return initials
|
return initials
|
||||||
|
|
||||||
def get_form(self):
|
def get_form(self):
|
||||||
|
@ -22,79 +22,98 @@ function removeStockRow(e) {
|
|||||||
function passFailBadge(result) {
|
function passFailBadge(result) {
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
return `<span class='label label-green'>{% trans "PASS" %}</span>`;
|
return `<span class='label label-green float-right'>{% trans "PASS" %}</span>`;
|
||||||
} else {
|
} else {
|
||||||
return `<span class='label label-red'>{% trans "FAIL" %}</span>`;
|
return `<span class='label label-red float-right'>{% trans "FAIL" %}</span>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function noResultBadge() {
|
||||||
|
return `<span class='label label-blue float-right'>{% trans "NO RESULT" %}</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testKey(test_name) {
|
||||||
|
// Convert test name to a unique key without any illegal chars
|
||||||
|
|
||||||
|
test_name = test_name.trim().toLowerCase();
|
||||||
|
test_name = test_name.replace(' ', '');
|
||||||
|
|
||||||
|
test_name = test_name.replace(/[^0-9a-z]/gi, '');
|
||||||
|
|
||||||
|
return test_name;
|
||||||
|
}
|
||||||
|
|
||||||
function loadStockTestResultsTable(table, options) {
|
function loadStockTestResultsTable(table, options) {
|
||||||
/*
|
/*
|
||||||
* Load StockItemTestResult table
|
* Load StockItemTestResult table
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var params = options.params || {};
|
function formatDate(row) {
|
||||||
|
// Function for formatting date field
|
||||||
|
var html = row.date;
|
||||||
|
|
||||||
// HTML element to setup the filtering
|
if (row.user_detail) {
|
||||||
var filterListElement = options.filterList || '#filter-list-stocktests';
|
html += `<span class='badge'>${row.user_detail.username}</span>`;
|
||||||
|
|
||||||
var filters = {};
|
|
||||||
|
|
||||||
filters = loadTableFilters("stocktests");
|
|
||||||
|
|
||||||
var original = {};
|
|
||||||
|
|
||||||
for (var key in params) {
|
|
||||||
original[key] = params[key];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setupFilterList("stocktests", table, filterListElement);
|
|
||||||
|
|
||||||
// Override the default values, or add new ones
|
|
||||||
for (var key in params) {
|
|
||||||
filters[key] = params[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
table.inventreeTable({
|
|
||||||
method: 'get',
|
|
||||||
formatNoMatches: function() {
|
|
||||||
return '{% trans "No test results matching query" %}';
|
|
||||||
},
|
|
||||||
url: "{% url 'api-stock-test-result-list' %}",
|
|
||||||
queryParams: filters,
|
|
||||||
original: original,
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
field: 'pk',
|
|
||||||
title: 'ID',
|
|
||||||
visible: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'test',
|
|
||||||
title: '{% trans "Test" %}',
|
|
||||||
sortable: true,
|
|
||||||
formatter: function(value, row) {
|
|
||||||
var html = value;
|
|
||||||
|
|
||||||
if (row.attachment_detail) {
|
if (row.attachment_detail) {
|
||||||
html += `<a href='${row.attachment_detail.attachment}'><span class='fas fa-file-alt label-right'></span></a>`;
|
html += `<a href='${row.attachment_detail.attachment}'><span class='fas fa-file-alt label-right'></span></a>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeButtons(row, grouped) {
|
||||||
|
var html = `<div class='btn-group float-right' role='group'>`;
|
||||||
|
|
||||||
|
html += makeIconButton('fa-plus icon-green', 'button-test-add', row.test_name, '{% trans "Add test result" %}');
|
||||||
|
|
||||||
|
if (!grouped) {
|
||||||
|
var pk = row.pk;
|
||||||
|
html += makeIconButton('fa-edit icon-blue', 'button-test-edit', pk, '{% trans "Edit test result" %}');
|
||||||
|
html += makeIconButton('fa-trash-alt icon-red', 'button-test-delete', pk, '{% trans "Delete test result" %}');
|
||||||
|
}
|
||||||
|
|
||||||
|
html += "</div>";
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First, load all the test templates
|
||||||
|
table.inventreeTable({
|
||||||
|
url: "{% url 'api-part-test-template-list' %}",
|
||||||
|
method: 'get',
|
||||||
|
formatNoMatches: function() {
|
||||||
|
return "{% trans 'No test results found' %}";
|
||||||
},
|
},
|
||||||
|
queryParams: {
|
||||||
|
part: options.part,
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
field: 'pk',
|
||||||
|
title: 'ID',
|
||||||
|
visible: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'result',
|
field: 'test_name',
|
||||||
title: "{% trans "Result" %}",
|
title: "{% trans "Test Name" %}",
|
||||||
sortable: true,
|
sortable: true,
|
||||||
formatter: function(value) {
|
formatter: function(value, row) {
|
||||||
return passFailBadge(value);
|
var html = value;
|
||||||
|
|
||||||
|
if (row.result == null) {
|
||||||
|
html += noResultBadge();
|
||||||
|
} else {
|
||||||
|
html += passFailBadge(row.result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return html;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'value',
|
field: 'value',
|
||||||
title: "{% trans "Value" %}",
|
title: '{% trans "Value" %}',
|
||||||
sortable: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'notes',
|
field: 'notes',
|
||||||
@ -102,35 +121,85 @@ function loadStockTestResultsTable(table, options) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'date',
|
field: 'date',
|
||||||
title: '{% trans "Uploaded" %}',
|
title: '{% trans "Test Date" %}',
|
||||||
sortable: true,
|
|
||||||
formatter: function(value, row) {
|
formatter: function(value, row) {
|
||||||
var html = value;
|
return formatDate(row);
|
||||||
|
|
||||||
if (row.user_detail) {
|
|
||||||
html += `<span class='badge'>${row.user_detail.username}</span>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return html;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'buttons',
|
field: 'buttons',
|
||||||
formatter: function(value, row) {
|
formatter: function(value, row) {
|
||||||
|
return makeButtons(row, false);
|
||||||
var pk = row.pk;
|
|
||||||
|
|
||||||
var html = `<div class='btn-group float-right' role='group'>`;
|
|
||||||
|
|
||||||
html += makeIconButton('fa-edit icon-blue', 'button-test-edit', pk, '{% trans "Edit test result" %}');
|
|
||||||
html += makeIconButton('fa-trash-alt icon-red', 'button-test-delete', pk, '{% trans "Delete test result" %}');
|
|
||||||
|
|
||||||
html += `</div>`;
|
|
||||||
|
|
||||||
return html;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
|
groupBy: true,
|
||||||
|
groupByField: 'test_name',
|
||||||
|
groupByFormatter: function(field, id, data) {
|
||||||
|
|
||||||
|
// Extract the "latest" row (data are returned in date order from the server)
|
||||||
|
var latest = data[data.length-1];
|
||||||
|
|
||||||
|
switch (field) {
|
||||||
|
case 'test_name':
|
||||||
|
return latest.test_name + ` <i>(${data.length})</i>` + passFailBadge(latest.result);
|
||||||
|
case 'value':
|
||||||
|
return latest.value;
|
||||||
|
case 'notes':
|
||||||
|
return latest.notes;
|
||||||
|
case 'date':
|
||||||
|
return formatDate(latest);
|
||||||
|
case 'buttons':
|
||||||
|
// Buttons are done differently for grouped rows
|
||||||
|
return makeButtons(latest, true);
|
||||||
|
default:
|
||||||
|
return "---";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoadSuccess: function(tableData) {
|
||||||
|
// Once the test template data are loaded, query for results
|
||||||
|
inventreeGet(
|
||||||
|
"{% url 'api-stock-test-result-list' %}",
|
||||||
|
{
|
||||||
|
stock_item: options.stock_item,
|
||||||
|
user_detail: true,
|
||||||
|
attachment_detail: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
success: function(data) {
|
||||||
|
|
||||||
|
// Iterate through the returned test result data, and group by test
|
||||||
|
data.forEach(function(item) {
|
||||||
|
var match = false;
|
||||||
|
|
||||||
|
var key = testKey(item.test);
|
||||||
|
|
||||||
|
// Try to associate this result with a test row
|
||||||
|
tableData.forEach(function(row) {
|
||||||
|
// The result matches the test template row
|
||||||
|
if (key == testKey(row.test_name)) {
|
||||||
|
|
||||||
|
// Force the names to be the same!
|
||||||
|
item.test_name = row.test_name;
|
||||||
|
match = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// No match could be found (this is a new test!)
|
||||||
|
if (!match) {
|
||||||
|
|
||||||
|
item.test_name = item.test;
|
||||||
|
}
|
||||||
|
|
||||||
|
tableData.push(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Finally, push the data back into the table!
|
||||||
|
table.bootstrapTable("load", tableData);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user