If condition in django templates not working - html

I have tried the if condition based on the value defined in the django template
{% if randomgen == 2 %}
<p style="float:right;text-align: center;padding:5px 5px;"><b>{% randomgen %}1</p>
{% else %}
<p style="float:right;text-align: center;padding:5px 5px;"><b>{% randomgen %} 2</p>
{% endif %}
the randomgen is defined to pick in random between 1 and 2 and the value is being displayed correctly in tag but irrespective of the value it always going to else condition
register = template.Library()
#register.tag(name="randomgen")
def randomgen(parser, token):
items = []
bits = token.split_contents()
for item in bits:
items.append(item)
return RandomgenNode(items[1:])
def render(self, context):
arg1 = 0
arg2 = 10
if "float" in self.items:
result = random.randint(1,20)
elif not self.items:
result = random.randint(1,20)
else:
result = random.randint(1,2)
return result

In your HTML, set randomgen to another variable:
{% randomgen as rgen %}
Then use the newly set variable for your conditional:
{% if rgen == 2 %}
Honestly, I was surprised your code didn't work as your usage makes sense intuitively. Knowing that it doesn't work though, my guess is the template is comparing a function with an integer which is always going to return False. Good question!

Related

Jinja2 using numeric expressions in Template Expressions

I need to do something apparently very simple:
typedef enum {
{% for e in mylist %}
{{ e }} = 0x{{ '%04X' % (1 << loop.index0) }},
{%- endfor %}
ALL = 0x0FFF
} Sound_Region_t;
but this bombs with "jinja2.exceptions.TemplateSyntaxError: unexpected '<'"
Intention is to get something like:
typedef enum {
foo = 0x0001,
bar = 0x0002,
fie = 0x0004,
fom = 0x0008,
...,
ALL = 0x0FFF
} Sound_Region_t;
I.e.: value is a "walking bit" so I can "bitwise or" together them.
Same behavior if I try to use other variations including "{% with bit = 1 << loop.index %}" or similar.
What am I missing?
Jinja2 does not allow bitwise operators inside the templates, so we need to create a small global function that executes such operators and returns a number:
def leftshift(amount):
return 1 << amount
# Get or create a Jinja2 environment
from jinja2 import Environment
env = Environment(...)
# Add leftshift function to the global context
env.globals['leftshift'] = leftshift
And in the templates now we can call leftshift function with the loop index:
typedef enum {
{% for e in mylist %}
{{ e }} = 0x{{ '{:04X}'.format(leftshift(loop.index0))}},
{%- endfor %}
ALL = 0x0FFF
} Sound_Region_t;

saltstack state file: how to access a list element in a salt grain via Jinja

I try to evaluate the value of a grains list in jinja but do not know how. The list entry I am looking for is the minor osversion in
grain:osrelease_info
salt-call -g |grep -C2 osrelease_info
osrelease:
15.99
osrelease_info:
- 15
- 99
In a state-file I would like to evaluate the minior osrelease value in a jinja expression like this one:
{% if grains['osmajorrelease'] == '15' and grains['osrelease_info'][1] >= 99 %}
...
{% endif %}
However the syntax I tried above to access index:1 of the osrelease_info list does not report an error but doesn't work either.
So how can I access the list entry containing value "99" in Jinja?
I think I found the answer myself alltough it is a little strange. The problem was not the expression
{% ... and grains['osrelease_info'][1] >= 99 %}
but instead the string comparison before:
{% if grains['osmajorrelease'] == '15' ... %}
All I changed was to convert the string '15' to an int. So the following expression does what I originally wanted:
{% if grains['osmajorrelease'] == 15 and grains['osrelease_info'][1] >= 99 %}
...
{% endif %}
This is weird since I use exactly the same comparision (grains['osmajorrelease'] == '15') in other state files with success.

Insert Environment Variable using Jinja in SaltStack

I am trying to read a JSON file inside a folder. using import_json.
Here is my code
{% set instance_id = grains['INSTANCE_ID'] %}
INSTANCE_ID Env Var:
environ.setenv:
- name: INSTANCE_ID
- value: {{ grains['INSTANCE_ID'] }}
- update_minion: True
{% import_json "/tmp/$INSTANCE_ID/conf.json" as config_properties %}
But I am getting this error
Data failed to compile:
Rendering SLS 'base:cloud.steps.conf' failed: Jinja error: /tmp/$INSTANCE_ID/conf.json.
Although when I insert the INSTANCE_ID manually it works as expected.
What I want is to be able to insert either $INSTANCE_ID or directly the grain value {{ grains['INSTANCE_ID'] }}
Can someone please help me with this?
Thanks.
{% import_json "/tmp/$INSTANCE_ID/conf.json" as config_properties %}
I imagine you are trying to evaluate the variable $INSTANCE_ID in the above statement. Jinja template evaluates the variables in expression statements.
In this case, the variable is set in the first line, using set
{% set instance_id = grains['INSTANCE_ID'] %}
So, you can use it in expression along with string appends, like
{% import_json "/tmp/" ~ instance_id ~ "/conf.json" as config_properties %}
The above statement should resolve your error.
Also, I would suggest using a variable to evaluate the value of the string expression above, like
{% set conf_json_path = "/tmp/" ~ instance_id ~ "/conf.json" %}
and use it like this
{% import_json conf_json_path as config_properties %}
Hope this help!
In case, you wish to use grains dictionary directly, you can use the value like so
{% set conf_json_path = "/tmp/" ~ grains['INSTANCE_ID'] ~ "/conf.json" %}

Concatenate arrays in liquid

I am trying to concatenate three arrays in liquid/jekyll but in the final array (publications) I get only the elements of the first one (papers)
{% assign papers = (site.publications | where:"type","paper" | sort: 'date') | reverse %}
{% assign posters = (site.publications | where:"type","poster" | sort: 'date') | reverse %}
{% assign abstracts = (site.publications | where:"type","abstract" | sort: 'date') | reverse %}
{% assign publications = papers | concat: posters | concat: abstracts %}
What am I missing?
New answer
Jekyll now uses Liquid 4.x. So we can use the concat filter !
Old answer
concat filter is not part of current liquid gem (3.0.6) used by jekyll 3.2.1.
It will only be available in liquid 4 (https://github.com/Shopify/liquid/blob/v4.0.0.rc3/lib/liquid/standardfilters.rb#L218).
I will probably be available for Jekyll 4.
In the meantime, this plugin can do the job :
=begin
Jekyll filter to concatenate arrays
Usage:
{% assign result = array-1 | concatArray: array-2 %}
=end
module Jekyll
module ConcatArrays
# copied from https://github.com/Shopify/liquid/blob/v4.0.0.rc3/lib/liquid/standardfilters.rb
def concat(input, array)
unless array.respond_to?(:to_ary)
raise ArgumentError.new("concat filter requires an array argument")
end
InputIterator.new(input).concat(array)
end
class InputIterator
include Enumerable
def initialize(input)
#input = if input.is_a?(Array)
input.flatten
elsif input.is_a?(Hash)
[input]
elsif input.is_a?(Enumerable)
input
else
Array(input)
end
end
def concat(args)
to_a.concat(args)
end
def each
#input.each do |e|
yield(e.respond_to?(:to_liquid) ? e.to_liquid : e)
end
end
end
end
end
Liquid::Template.register_filter(Jekyll::ConcatArrays)

How to iterate through the top 3 posts from the last two weeks in liquid/jekyll?

Let me add some context: i'm trying to automate my boring reports to management using jekyll!
I'd like to write a post for every activity i do and, every week, ship the compiled report with the "top 3 highlights" from this week and from last week.
My shot at it, as a jekyll newbie, was adding the week of publishing and if that post is a highlight or not in the yaml front matter:
---
layout: event
title: "Gave a Jelyll Talk!"
date: 2015-04-23 16:05:04
highlight: week
week: 17
---
And get the last two weeks in the template, like this:
---
layout: email
---
Here are my activities from the last two weeks:
{% assign hls = (site.posts | where: "highlight" , "week") %}
{% assign weeks = (hls | group_by: "week") %}
{% assign thisw = weeks[0] %}
{% assign lastw = weeks[1] %}
<h1>Week #{{thisw.name}}</h1>
<ul>
{% for post in thisw.items %}
<li>{{post.title}}</li>
{% endfor %}
</ul>
<h1>Week #{{lastw.name}}</h1>
<ul>
{% for post in lastw.items %}
<li>{{post.title}}</li>
{% endfor %}
</ul>
That kinda works, but, i'd like to ask:
Is it possible to automatically calculate the current week, get only posts from the past two weeks (not future or prior), only the latest 3 highlights from each week and avoid repeating the html template?
I would consider writing a plugin for Jekyll, something like this:
WeeklyHighlights.rb
module Jekyll
class WeeklyHighlights < Generator
safe true
priority :high
def week_id(time)
# to handle turn of year properly
return time.strftime('%Y-%W')
end
def generate(site)
# hash (dict) to store highlights grouped by week number
highlights_by_week = {}
today = Time.now
# initialize recent weeks with empty highlights
(0 .. 4).each do |i|
w = week_id(today - i)
highlights_by_week[w] = []
end
# group highlights according to week number
site.posts.each do |post|
if post['highlight'] != 'week'
next
end
week = week_id(post.date)
highlights_by_week[week] ||= []
highlights_by_week[week] << post
end
# make array of arrays of highlights, in the required order
weekly_highlights = []
highlights_by_week.keys.sort.reverse.each do |w|
highlights_by_week[w].sort! {|a,b| b.date <=> a.date} # = hl.sort{|a,b| b.date <=> a.date }
weekly_highlights << {'id' => w, 'hls' => highlights_by_week[w]}
end
# store prepared highlights
site.config['weekly_highlights'] = weekly_highlights
end
end
end
Put the file in the plugins directory (typically plugins or _plugins in the root of Jekyll site), and then in the template:
{% for week in site.weekly_highlights limit:2 %}
<h2>week {{week.id}}</h2>
<ol>
{% for p in week.hls limit:3 %}
<li>{{p.title}}</li>
{% endfor %}
</ol>
{% endfor %}
More on Jekyll plugins: official documentation
Yep... that should work.
At least you can get the current (i.e. build) time with {{ site.time }} and format it with a liquid filter to get the week number like so:
{% assign current_week = site.time | date: "%w" | plus: 0 %}
(You need to apply the plus: 0 filter too, to get a number instead of a string assigned.) Now things get nasty, as you might want to consider turn of year properly... but it should be doable.
I can think of getting strings like YEAR-WEEKNUMBER for the last three weeks, assign all posts with same date string to a new variable, say posts_lastweek and so on. Then, loop over the posts, compare and limit the for loop ({% for post in posts_lastweek | limit: 3 %}) or work with counters to limit the output.
Let me know if that works for you...