0

I need help with the following. I have the below json response and I'm trying to sum all the "value" objects in a jinja template. I have no ideea how to deal with json data in jinja templates. Can anyone point me in the right direction? Thanks a bunch!

{
    "body": [
        {
            "beg_time": 1689172769,
            "step_time": 3600,
            "value": [
                [3],
                [2.2],
                [1.2]
            ]
        },
        {
            "beg_time": 1689187169,
            "step_time": 3600,
            "value": [
                [0],
                [0],
                [0],
                [2.02],
                [2.6],
                [0],
                [0]
            ]
        },
        {
            "beg_time": 1689215969,
            "step_time": 3600,
            "value": [
                [0],
                [2.1],
                [0],
                [1.4],
                [0],
                [0],
                [0],
            ]
        }
    ],
    "status": "ok",
    "time_exec": 0.03644895553588867,
    "time_server": 1689257978
}

This is as close as I've gotten but the json response varies in length (sometimes is 2 arrays, sometimes is 3) and I get "UndefinedError: list object has no element 2"

{{ value_json2.body.0.value | sum(attribute=0) | round(1) }} + {{ value_json2.body.1.value | sum(attribute=0) | round(1) }} + {{ value_json2.body.2.value | sum(attribute=0) | round(1) }}

1 Answers1

0

Update -- as pointed out in the comments, flatten isn't a built-in Jinja filter. If we grab the flatten function from this answer, we can define the necessary filter:

import jinja2
import collections.abc
import itertools


def flatten(iterable, ltypes=collections.abc.Iterable):
    remainder = iter(iterable)
    while True:
        try:
            first = next(remainder)
        except StopIteration:
            break
        if isinstance(first, ltypes) and not isinstance(first, (str, bytes)):
            remainder = itertools.chain(first, remainder)
        else:
            yield first


# assume `data` holds your sample data
data = {"body": [...]}

env = jinja2.Environment()
env.filters['flatten'] = flatten
t = env.from_string("{{ data.body|map(attribute='value')|flatten|sum }}")
print(t.render(data=data))

Running the above code will produce the expected answer (14.52).


I think this gets you what you want:

{{ data.body|map(attribute='value')|flatten|sum }}

Which for your example data produces:

14.52

The first part of that expression -- data.body|map(attribute='value') -- gets us a list of nested lists:

[[[3], [2.2], [1.2]], [[0], [0], [0], [2.02], [2.6], [0], [0]], [[0], [2.1], [0], [1.4], [0], [0], [0]]]

Passing that through the flatten filter gets us a simple list of values:

[3, 2.2, 1.2, 0, 0, 0, 2.02, 2.6, 0, 0, 0, 2.1, 0, 1.4, 0, 0, 0 ]

Which is perfect for passing through the sum filter.

larsks
  • 277,717
  • 41
  • 399
  • 399
  • Thanks for your answer. However the "flatten" filter is not available in jinja2. I get error "TemplateAssertionError: No filter named 'flatten'." – ttlucian Jul 15 '23 at 09:50
  • Ah, you're right! That's an Ansible custom filter, not a standard Jinja one. I'll update the answer. – larsks Jul 15 '23 at 12:04