Improvements for default StockItem test report template

- Fix bug in template
- Handle potential errors in template tags
- Add more helpers to report tags
- Improve test result rendering
This commit is contained in:
Oliver Walters 2023-03-16 00:21:49 +11:00
parent 76589a2fd8
commit c4bdacbd97
4 changed files with 142 additions and 11 deletions

View File

@ -2083,6 +2083,16 @@ class Part(InvenTreeBarcodeMixin, MetadataMixin, MPTTModel):
return tests
def getTestTemplateMap(self, **kwargs):
"""Return a map of all test templates associated with this Part"""
templates = {}
for template in self.getTestTemplates(**kwargs):
templates[template.key] = template
return templates
def getRequiredTests(self):
"""Return the tests which are required by this part"""
return self.getTestTemplates(required=True)

View File

@ -307,6 +307,30 @@ class TestReport(ReportTemplateBase):
return items.exists()
def get_test_keys(self, stock_item):
"""Construct a flattened list of test 'keys' for this StockItem:
- First, any 'required' tests
- Second, any 'non required' tests
- Finally, any test results which do not match a test
"""
keys = []
for test in stock_item.part.getTestTemplates(required=True):
if test.key not in keys:
keys.append(test.key)
for test in stock_item.part.getTestTemplates(required=False):
if test.key not in keys:
keys.append(test.key)
for result in stock_item.testResultList(include_installed=self.include_installed):
if result.key not in keys:
keys.append(result.key)
return list(keys)
def get_context_data(self, request):
"""Return custom context data for the TestReport template"""
stock_item = self.object_to_print
@ -316,6 +340,9 @@ class TestReport(ReportTemplateBase):
'serial': stock_item.serial,
'part': stock_item.part,
'parameters': stock_item.part.parameters_map(),
'test_keys': self.get_test_keys(stock_item),
'test_template_list': stock_item.part.getTestTemplates(),
'test_template_map': stock_item.part.getTestTemplateMap(),
'results': stock_item.testResultMap(include_installed=self.include_installed),
'result_list': stock_item.testResultList(include_installed=self.include_installed),
'installed_items': stock_item.get_installed_items(cascade=True),

View File

@ -33,6 +33,15 @@ content: "{% trans 'Stock Item Test Report' %}";
color: #F55;
}
.test-not-found {
color: #33A;
}
.required-test-not-found {
color: #EEE;
background-color: #F55;
}
.container {
padding: 5px;
border: 1px solid;
@ -84,7 +93,7 @@ content: "{% trans 'Stock Item Test Report' %}";
</div>
</div>
{% if resul_list|length > 0 %}
{% if test_keys|length > 0 %}
<h3>{% trans "Test Results" %}</h3>
<table class='table test-table'>
@ -101,22 +110,44 @@ content: "{% trans 'Stock Item Test Report' %}";
<tr>
<td colspan='5'><hr></td>
</tr>
{% for test in result_list %}
{% for key in test_keys %}
<!-- test key = {{ key }} -->
{% getkey test_template_map key as test_template %}
{% getkey results key as test_result %}
<tr class='test-row'>
<td>{{ test.test }}</td>
{% if test.result %}
<td>
{% if test_template %}
{% render_html_text test_template.test_name bold=test_template.required %}
{% elif test_result %}
{% render_html_text test_result.test italic=True %}
{% else %}
<!-- No matching test template or result for {{ key }} -->
<span style='color: red;'>{{ key }}</span>
{% endif %}
</td>
{% if test_result %}
{% if test_result.result %}
<td class='test-pass'>{% trans "Pass" %}</td>
{% else %}
<td class='test-fail'>{% trans "Fail" %}</td>
{% endif %}
<td>{{ test.value }}</td>
<td>{{ test.user.username }}</td>
<td>{{ test.date.date.isoformat }}</td>
<td>{{ test_result.value }}</td>
<td>{{ test_result.user.username }}</td>
<td>{{ test_result.date.date.isoformat }}</td>
{% else %}
{% if test_template.required %}
<td colspan='4' class='required-test-not-found'>{% trans "No result (required)" %}</td>
{% else %}
<td colspan='4' class='test-not-found'>{% trans "No result" %}</td>
{% endif %}
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<em>No tests defined for this stock item</em>
{% endif %}
{% if installed_items|length > 0 %}

View File

@ -19,17 +19,52 @@ logger = logging.getLogger('inventree')
@register.simple_tag()
def getkey(value: dict, arg):
def getindex(container: list, index: int):
"""Return the value contained at the specified index of the list.
This function is provideed to get around template rendering limitations.
Arguments:
container: A python list object
index: The index to retrieve from the list
"""
# Index *must* be an integer
try:
index = int(index)
except ValueError:
return None
if index < 0 or index >= len(container):
return None
try:
value = container[index]
except IndexError:
value = None
return value
@register.simple_tag()
def getkey(container: dict, key):
"""Perform key lookup in the provided dict object.
This function is provided to get around template rendering limitations.
Ref: https://stackoverflow.com/questions/1906129/dict-keys-with-spaces-in-django-templates
Arguments:
value: A python dict object
arg: The 'key' to be found within the dict
container: A python dict object
key: The 'key' to be found within the dict
"""
return value[arg]
if type(container) is not dict:
logger.warning("getkey() called with non-dict object")
return None
if key in container:
return container[key]
else:
return None
@register.simple_tag()
@ -215,3 +250,31 @@ def render_currency(money, **kwargs):
"""Render a currency / Money object"""
return InvenTree.helpers.render_currency(money, **kwargs)
@register.simple_tag
def render_html_text(text: str, **kwargs):
"""Render a text item with some simple html tags.
kwargs:
bold: Boolean, whether bold (or not)
italic: Boolean, whether italic (or not)
heading: str, heading level e.g. 'h3'
"""
tags = []
if kwargs.get('bold', False):
tags.append('strong')
if kwargs.get('italic', False):
tags.append('em')
if heading := kwargs.get('heading', ''):
tags.append(heading)
output = ''.join([f'<{tag}>' for tag in tags])
output += text
output += ''.join([f'</{tag}>' for tag in tags])
return mark_safe(output)