Summing up values in jinja2 - jinja2

What would be the right way of getting the sum of the value in details.sections.details filtered by another attribute which would be the the value in name, i.e.: Earnings / Deduction?
Given the data:
{
"details":{
"totalPay":482.66,
"currency":"OMR",
"contributions":48.24,
"sections":[
{
"name":"Earnings",
"value":519.41,
"details":[
{
"name":"Overtime",
"value":60,
"notes":"Border OT",
"currency":"OMR"
},
{
"name":"H R A",
"value":96,
"notes":"",
"currency":"OMR"
},
{
"name":"LIVING ALLOWANCE",
"value":32,
"notes":"",
"currency":"OMR"
},
{
"name":"T A",
"value":32,
"notes":"",
"currency":"OMR"
},
{
"name":"Basic salary",
"value":299.408,
"notes":"",
"currency":"OMR"
}
]
},
{
"name":"Deductions",
"value":36.75,
"details":[
{
"name":"PASI - Employee (7%) - Employee",
"value":32.1586,
"notes":null,
"currency":"OMR"
},
{
"name":"Social Security Recovery (1%) - Employee",
"value":4.5941,
"notes":null,
"currency":"OMR"
}
]
}
]
}
}
I would expect to get, for Earnings:
60 + 96 + 32 + 32 + 299.408 = 519.408
and, for the Deductions:
32.1586 + 4.5941 = 36.7527
I will then dispalay the summed up values in a table.
I tried
{% set sum = details.sections.details|sum(attribute='value') -%}
But, the script does not load and I think there is an error in it.
I am able to get the individual values by looping.
I have declared a variable:
{% set sumAdditions = 0 %}
{% sumAdditions = sumAdditions + details.value %}
but this seems to fails as the script errors out.

details.sections is a list, so you either have to filter the list for earnings and deductions:
{% set earnings = (
details.sections | selectattr('name', 'eq', 'Earnings') | first
).details | sum(attribute='value')
%}
{% set deductions = (
details.sections | selectattr('name', 'eq', 'Deductions') | first
).details | sum(attribute='value')
%}
Or do it dynamically in a loop:
{% set sums = {} %}
{% for section in details.sections %}
{% do sums.update({
section.name: section.details | sum(attribute='value')
})
%}
{% endfor %}
Where sums will now contain a dictionary:
{'Earnings': 519.408, 'Deductions': 36.7527}
If, for some reason the {% do %} construct is not available for you, you can use a dummy set, so assign the return of the dictionary update to a variable you won't use:
{% set sums = {} %}
{% for section in details.sections %}
{% set dummy = sums.update({
section.name: section.details | sum(attribute='value')
})
%}
{% endfor %}

Related

How can I fetch a value from a JSON dictionary?

I am trying to grab the value green from the below JSON data dictionary.
The API endpoint located at http://localhost:9200/api/status give the below data:
{
"name":"prod01",
"uuid":"3430c40-e786-4325-bc48-e0a096956000",
"version":{
"number":"7.17.0",
"build_hash":"60a9838d21b6420bbdb5a4d07099111b74c68ceb",
"build_number":46534,
"build_snapshot":false
},
"status":{
"overall":{
"since":"2023-02-13T22:47:05.381Z",
"state":"green",
"title":"Green",
"nickname":"Looking good",
"icon":"success",
"uiColor":"secondary"
},
"statuses":[
{
"id":"core:elasticsearch#7.17.0",
"message":"Elasticsearch is available",
"since":"2023-02-13T22:47:05.381Z",
"state":"green",
"icon":"success",
"uiColor":"secondary"
},
{
"id":"core:savedObjects#7.17.0",
"message":"SavedObjects service has completed migrations and is available",
"since":"2023-02-13T22:47:05.381Z",
"state":"green",
"icon":"success",
"uiColor":"secondary"
}
]
}
}
And the test.sls file doing the job is:
{% set json_data = salt.cp.get_url('http://localhost:9200/api/status', dest=None) | load_json %}
{% for key, value in json_data.items() %}
{% if value['state'] == 'green' %}
{{ key }}: {{ value }} (found)
{% else %}
{{ key }}: {{ value }}
{% endif %}
{% endfor %}
Executing it, I get the error:
Rendering SLS 'base:svr.salt.dev.test' failed: Jinja variable 'str object' has no attribute 'state'
You are looping on all the key-value pairs of the object json_data, with json_data.items(), so, you do not have a my_dictionary['state'] anymore, what you have is a key which will be state and its value will be green.
This said, your {"state": "green"} is not in the root of your dictionary, so you will never find any key-value pair matching what you need.
What you can do, though is:
{% if load_json.status.overall.state == 'green' -%}
state: {{ load_json.status.overall.nickname }}
{% endif %}
Which would yield:
state: Looking good

loop through a for statement using an if/else with nunjucks and json

I'm trying to loop through a nested json file with nunjucks, give each object type a specific layout and sort all based on date.
So in my case I have two collections events and videos. Both collections will have an array of events and videos.
My file is named /content.json and structured as followed:
{
media: {
events: [
{
content_id: "1",
content_type: "event",
date: "01-11-2019",
etc: "etc"
},
{
content_id: "2",
content_type: "event",
date: "01-08-2019",
etc: "etc"
}
],
videos: [
{
content_id: "3",
content_type: "video",
date: "01-12-2019",
etc: "etc"
},
{
content_id: "4",
content_type: "video",
date: "01-09-2019",
etc: "etc"
}
]
}
}
I have tried to get the different object assigned with an if/else statement and then use a for loop to cycle through the array, but that has failed, see below:
{% for item in content.media %}
{% if item == events %}
{% for item in content.media.events %}
{% include "components/event.njk" %}
{% endfor %}
{% elif item == video %}
{% for item in content.media.videos %}
{% include "components/video.njk" %}
{% endfor %}
{% endif %}
{% endfor %}
I never got to try and sort all the content by date, but I have found:
{% for item in items|sort(attribute='date')%}
Can anyone guide me in right direction?
Thanks in advance.
AENM
This code outputs separated feeds by type of elements (event or video).
{% for event in content.media.events | sort(attribute = 'date') %}
{% include "components/event.njk" %}
{% endfor %}
{% for video in content.media.videos | sort(attribute = 'date') %}
{% include "components/video.njk" %}
{% endfor %}
If you need to output a mixed feed, you should join arrays to one and run trought it (fortunately each elements already have the type):
{% for item in [].concat(content.media.events, content.media.videos) | sort(attribute = 'date') %}
{% include "components/" + item.content_type + ".njk" %}
{% endfor %}
Aikon,
I got it now!!
It was another typo, you switched the media and events. (did you try to keep me sharp?! :-))
But that's why the concat didn't work!!
So this is the final working result:
{% for item in [].concat(media.content.events, media.content.videos) | sort(attribute = 'date') %}
{% include "components/" + item.type + ".njk" %}
{% endfor %}
Only the date is not in the correct order, but I think that has to with my grid setup.
Thanks for helping me out......

Iterate array object in liquid template

I have a problem when my Liquid template iterate in array object. I want to transform an input json into another output json.
It is my Liquid Template:
{% assign clientList = Clients.value %}
{
"value": [
{% for client in clientList %}
{
"ACCOUNTNUM": "{{client.ACCOUNTNUM}}",
"EMAIL": "{{client.EMAIL}}",
"NAME": "{{client.NAME}}",
"PHONE": "{{client.PHONE}}",
"VATNUM": "{{client.VATNUM}}",
"RECID": "{{client.RECID}}",
"CANALID": "{{client.CANALID}}",
"CANALDESC": "{{client.CANALDESC}}"
}
{% if forloop.Last == false %}
,
{% endif %}
{% endfor %}
]
}
It is an input json example:
{
"Clients":{
"value":[
{
"#odata.etag":"",
"ItemInternalId":"3a93f2aa-dd77-4297-88c4-13241343321",
"ACCOUNTNUM":"01",
"EMAIL":"info#example.es",
"LANGUAGEID":"es",
"NAME":"Lili S.A.",
"PHONE":"943444666",
"VATNUM":"A01",
"DATAAREAID":"tr2",
"RECID":1412,
"DATAAREAID_x0023_2":"tr2",
"DATAAREAID_x0023_3":"tr2",
"CANALID":"C0010",
"CANALDESC":"Group gg"
},
{
"#odata.etag":"",
"ItemInternalId":"3a23f2aa-dd77-4297-88c4-13241343321",
"ACCOUNTNUM":"02",
"EMAIL":"info#example.es",
"LANGUAGEID":"es",
"NAME":"Lili2 S.A.",
"PHONE":"943444656",
"VATNUM":"A02",
"DATAAREAID":"tr2",
"RECID":1412,
"DATAAREAID_x0023_2":"tr2",
"DATAAREAID_x0023_3":"tr2",
"CANALID":"C0011",
"CANALDESC":"Group2 gg"
}
]
}
}
When I have launched my Logic App I got this error:
{\"StatusCode\":400,\"ErrorCode\":18,\"Details\":null,\"Message\":\"An error occurred while converting the transformed value to JSON. The transformed value is not a valid JSON. 'After parsing a value an unexpected character was encountered: :. Path 'print1', line 5, position 11.'\"
According to some test, it should be caused by the part shown as below in your liquid:
{% if forloop.Last == false %}
,
{% endif %}
I removed this part and the liquid map works fine, here I post my liquid map below for your reference:
{
"value": [
{% for client in content.Clients.value %}
{
"ACCOUNTNUM": "{{client.ACCOUNTNUM}}",
"EMAIL": "{{client.EMAIL}}",
"NAME": "{{client.NAME}}",
"PHONE": "{{client.PHONE}}",
"VATNUM": "{{client.VATNUM}}",
"RECID": "{{client.RECID}}",
"CANALID": "{{client.CANALID}}",
"CANALDESC": "{{client.CANALDESC}}"
},
{% endfor %}
]
}
Don't worry about the last "," because the "Transform JSON to JSON" action will deal with it. In the output of the "Transform JSON to JSON" action, it will not show an extra "," at the end of the json.

HTML If class paragraph contains string do

I am trying to edit code using Shopify and their Liquid templates. It should be simple and I've searched their site and online. Their suggestions have not provided me a solution.
This is the original line of code:
{% if settings.display_quickview_vendor %}
<p class="product-vendor"><label>Vendor</label><span></span></p>
{% endif %}
This statement will always be true, referencing the json file. Using the class="product-vendor" and the .js file, it inserts the vendor name with the Vendor label from product setup.
My goal is that when my vendor name is set to "--" (using a drop down in product setup) the vendor line of code will not be executed.
I've tried this
{% if settings.display_quickview_vendor %}
{% if product-vendor != "--" %}
<p class="product-vendor"><label>Vendor</label><span></span></p>
{% endif %}
{% endif %}
I've also replace the second if statement with the following.
{% if product.vendor != "--" %}
{% if product.vendor contains '--' %}
{% if product-vendor contains '--' %}
My experience is with C# and VBA. I have a feeling that I'm not fully understanding what it is that I need to be asking or searching for.
This is the .js file where the quickview window is called.
initQuickView: function() {
e(".quickview-button a").click(function() {
var i = e(this).attr("id");
return Shopify.getProduct(i, function(i) {
var a = e("#quickview-template").html();
e(".quick-view").html(a);
var o = e(".quick-view");
if (o.find(".product-title a").text(i.title), o.find(".product-title a").attr("href", i.url), o.find(".product-vendor span").length > 0 && o.find(".product-vendor span").text(i.vendor), o.find(".product-type span").length > 0 && o.find(".product-type span").text(i.type), o.find(".product-inventory span").length > 0) {
var n = i.variants[0].inventory_quantity;
o.find(".product-inventory span").text(n > 0 ? null != i.variants[0].inventory_management ? n + " in stock" : "Many in stock" : "Out of stock")
}
This is from the schema.json file.
{
"type": "checkbox",
"id": "display_quickview_vendor",
"label": "Display Vendor?",
"default": true
},
This is from the data.json file.
"display_quickview_vendor": true,

How to limit number of iterations when looping through object in nunjucks

I have a js object like this:
var data = [
{ src: "src1", name: "name 1" },
{ src: "src2", name: "name 2" },
{ src: "src3", name: "name 3" }
]
I am looping through it with Nunjucks:
{% for object in data %}
{{object.src}}
{% endfor %}
But I want to limit the number of iterations to 2.
How do I do that with Nunjucks?
I know there is a range option but I couldn't find how to use it in this case.
You could accomplish this a couple different ways:
A) Use loop.index0 special variable (Demo in Codepen)
Inside a for loop, you can use loop.index0 instead limit-var
{% for obj in data %}
{% if loop.index0 < 2 %}
{{obj.src}}: {{obj.name}}
{% endif %}
{% endfor %}
B) Add Custom Filter (Demo in Codepen)
But more readable is add custom filter limit and use it
var nunjucks = require('nunjucks');
var env = nunjucks.configure();
var data = [
{src: "src1", name: "name 1"},
{src: "src2", name: "name 2"},
{src: "src3", name: "name 3"}
];
env.addFilter('limit', function(arr, limit) {
return arr.slice(0, limit);
});
var res = nunjucks.renderString(`
{% for obj in data | limit(2) %}
{{obj.src}}: {{obj.name}}
{% endfor %}`,
{data}
);
console.log(res);
C) Use native slice() function (Demo in Codepen)
{% for obj in data.slice(0, 2) %}
{{obj.src}}: {{obj.name}}
{% endfor %}