2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-04-14 07:18:44 +00:00

Additional report tags (#11718)

* Add some more convenience functions for report helpers

* Add "reverse" tag

* Add unit tests

* Add lstrip and rstrip

* Fix unit tests
This commit is contained in:
Oliver
2026-04-11 12:03:58 +10:00
committed by GitHub
parent a05d01b759
commit 62588a62a3
3 changed files with 370 additions and 0 deletions

View File

@@ -173,8 +173,144 @@ Generate a list of all active customers:
More advanced database filtering should be achieved using a [report plugin](../plugins/mixins/report.md), and adding custom context data to the report template.
## List Helpers
The following helper functions are available for working with list (or list-like) data structures:
### length
Return the length of a list (or list-like) data structure. Note that this will also work for other data structures which support the `len()` function, such as strings, dictionaries or querysets:
::: report.templatetags.report.length
options:
show_docstring_description: false
show_source: False
### first
Return the first element of a list (or list-like) data structure:
::: report.templatetags.report.first
options:
show_docstring_description: false
show_source: False
### last
Return the last element of a list (or list-like) data structure:
::: report.templatetags.report.last
options:
show_docstring_description: false
show_source: False
### reverse
Return a list (or list-like) data structure in reverse order:
::: report.templatetags.report.reverse
options:
show_docstring_description: false
show_source: False
### truncate
Return a truncated version of a list (or list-like) data structure, containing only the first N elements:
::: report.templatetags.report.truncate
options:
show_docstring_description: false
show_source: False
## String Formatting
### strip
Return a string with leading and trailing whitespace removed:
::: report.templatetags.report.strip
options:
show_docstring_description: false
show_source: False
### lstrip
Return a string with leading whitespace removed:
::: report.templatetags.report.lstrip
options:
show_docstring_description: false
show_source: False
### rstrip
Return a string with trailing whitespace removed:
::: report.templatetags.report.rstrip
options:
show_docstring_description: false
show_source: False
### split
Return a list of substrings by splitting a string based on a specified separator:
::: report.templatetags.report.split
options:
show_docstring_description: false
show_source: False
### join
Return a string by joining a list of strings into a single string, using a specified separator:
::: report.templatetags.report.join
options:
show_docstring_description: false
show_source: False
### replace
Return a string where occurrences of a specified substring are replaced with another substring:
::: report.templatetags.report.replace
options:
show_docstring_description: false
show_source: False
### lowercase
Return a string with all characters converted to lowercase:
::: report.templatetags.report.lowercase
options:
show_docstring_description: false
show_source: False
### uppercase
Return a string with all characters converted to uppercase:
::: report.templatetags.report.uppercase
options:
show_docstring_description: false
show_source: False
### titlecase
Return a string with the first character of each word converted to uppercase and the remaining characters converted to lowercase:
::: report.templatetags.report.titlecase
options:
show_docstring_description: false
show_source: False
## Number Formatting
A number of helper functions are available for formatting numbers in a particular way. These can be used to format numbers according to a particular number of decimal places, or to add leading zeros, for example.
### format_number
The helper function `format_number` allows for some common number formatting options. It takes a number (or a number-like string) as an input, as well as some formatting arguments. It returns a *string* containing the formatted number:

View File

@@ -1036,3 +1036,196 @@ def include_icon_fonts(ttf: bool = False, woff: bool = False):
"""
return mark_safe(icon_class + '\n'.join(fonts))
@register.simple_tag()
def lowercase(value: str) -> str:
"""Convert a string to lowercase.
Arguments:
value: The string to be converted
"""
if not value:
return ''
return str(value).lower()
@register.simple_tag()
def uppercase(value: str) -> str:
"""Convert a string to uppercase.
Arguments:
value: The string to be converted
"""
if not value:
return ''
return str(value).upper()
@register.simple_tag()
def titlecase(value: str) -> str:
"""Convert a string to title case.
Arguments:
value: The string to be converted
"""
if not value:
return ''
return str(value).title()
@register.simple_tag()
def strip(value: str, chars: Optional[str] = ' ') -> str:
"""Strip leading and trailing characters from a string.
Arguments:
value: The string to be stripped
chars: The set of characters to strip from the string (default = whitespace)
"""
if not value:
return ''
return str(value).strip(chars)
@register.simple_tag()
def lstrip(value: str, chars: Optional[str] = ' ') -> str:
"""Strip leading characters from a string.
Arguments:
value: The string to be stripped
chars: The set of characters to strip from the string (default = whitespace)
"""
if not value:
return ''
return str(value).lstrip(chars)
@register.simple_tag()
def rstrip(value: str, chars: Optional[str] = ' ') -> str:
"""Strip trailing characters from a string.
Arguments:
value: The string to be stripped
chars: The set of characters to strip from the string (default = whitespace)
"""
if not value:
return ''
return str(value).rstrip(chars)
@register.simple_tag()
def split(value: str, separator: str = ',') -> list:
"""Split a string into a list, using the provided separator (default = ',').
Arguments:
value: The string to be split
separator: The character to use as a separator (default = ',')
"""
if not value:
return []
return [v.strip() for v in str(value).split(separator)]
@register.simple_tag()
def join(value: list, separator: str = ',') -> str:
"""Join a list of items into a string, using the provided separator (default = ',').
Arguments:
value: The list of items to be joined
separator: The character to use as a separator (default = ',')
"""
if not value:
return ''
return separator.join(str(v) for v in value)
@register.simple_tag()
def length(value: Any) -> int:
"""Return the length of a list or string.
Arguments:
value: The value to be measured (e.g. a list or string)
"""
if value is None:
return 0
try:
return len(value)
except TypeError:
return 0
@register.simple_tag()
def replace(value: str, old: str, new: str = '') -> str:
"""Replace occurrences of a substring within a string with a new value.
Arguments:
value: The original string
old: The substring to be replaced
new: The value to replace the old substring with (default = "")
"""
if not value:
return ''
return str(value).replace(old, new)
@register.simple_tag()
def first(value: list, default: Any = None) -> Any:
"""Return the first item in a list, or a default value if the list is empty.
Arguments:
value: The list from which to retrieve the first item
default: The value to return if the list is empty (default = None)
"""
if not value:
return default
try:
return value[0]
except (IndexError, TypeError):
return default
@register.simple_tag()
def last(value: list, default: Any = None) -> Any:
"""Return the last item in a list, or a default value if the list is empty.
Arguments:
value: The list from which to retrieve the last item
default: The value to return if the list is empty (default = None)
"""
if not value:
return default
try:
return value[-1]
except (IndexError, TypeError):
return default
@register.simple_tag()
def reverse(value: list) -> list:
"""Return a reversed version of the provided list.
Arguments:
value: The list to be reversed
"""
if not value:
return []
try:
return value[::-1]
except TypeError:
return []
@register.simple_tag()
def truncate(value: list, length: int) -> list:
"""Return a truncated version of the provided list.
Arguments:
value: The list to be truncated
length: The maximum length of the returned list
"""
if not value:
return []
try:
return value[:length]
except TypeError:
return []

View File

@@ -231,6 +231,47 @@ class ReportTagTest(PartImageTestMixin, InvenTreeTestCase):
logo = report_tags.logo_image()
self.assertIn('inventree.png', logo)
def test_string_tags(self):
"""Simple tests for the string manipulation tags."""
self.assertEqual(report_tags.uppercase('hello world'), 'HELLO WORLD')
self.assertEqual(report_tags.lowercase('HELLO WORLD'), 'hello world')
self.assertEqual(report_tags.titlecase('hello world'), 'Hello World')
self.assertEqual(report_tags.strip(' hello world '), 'hello world') # noqa: B005
self.assertEqual(report_tags.strip(' hello world ', chars=' hd'), 'ello worl') # noqa: B005
self.assertEqual(report_tags.strip('xxhelloxx', chars='xy'), 'hello') # noqa: B005
self.assertEqual(report_tags.lstrip(' hello world '), 'hello world ') # noqa: B005
self.assertEqual(report_tags.rstrip(' world ', chars=' dl'), ' wor') # noqa: B005
self.assertEqual(report_tags.split('a,b,c', separator=','), ['a', 'b', 'c'])
self.assertEqual(report_tags.split('a,|,b|c', separator='|'), ['a,', ',b', 'c'])
self.assertEqual(report_tags.join(['a', 'b', 'c'], separator=','), 'a,b,c')
self.assertEqual(report_tags.join(['a', 'b', 'c'], separator='|'), 'a|b|c')
self.assertEqual(report_tags.join(None, separator=','), '')
self.assertEqual(report_tags.replace('hello world', 'world'), 'hello ')
self.assertEqual(
report_tags.replace('hello world', 'world', 'there'), 'hello there'
)
self.assertEqual(report_tags.first('hello world'), 'h')
self.assertEqual(report_tags.last('hello world'), 'd')
self.assertEqual(report_tags.length('hello world'), 11)
self.assertEqual(report_tags.truncate('hello world', length=3), 'hel')
self.assertEqual(report_tags.reverse('hello world'), 'dlrow olleh')
def test_list_tags(self):
"""Simple tests for list manipulation tags."""
self.assertEqual(report_tags.first([1, 2, 3]), 1)
self.assertEqual(report_tags.last([1, 2, 3]), 3)
self.assertEqual(report_tags.length([1, 2, 3]), 3)
self.assertEqual(report_tags.reverse([1, 2, 3]), [3, 2, 1])
self.assertEqual(report_tags.truncate([1, 2, 3], 2), [1, 2])
self.assertEqual(report_tags.join([1, 2, 3], separator=','), '1,2,3')
self.assertEqual(report_tags.join(None, separator=','), '')
def test_maths_tags(self):
"""Simple tests for mathematical operator tags."""
self.assertEqual(report_tags.add(1, 2), 3)