How can I specify a column for a variable in a Jinja2 template? - jinja2

I have a jinja2 template that looks like this.
{% for file in result['files'] %}
descriptiontxt {{ inventory_hostname }}{{ file['path'] | truncate(50)}} {{ {{ '%Y%m%dT%H%M%S' | strftime(file['lastwritetime']) }} MoreDescriptiveText
In this example, the output might look like this.
descriptiontxt host1C:\dir\directory\directory\directory\txt.txt 20210106T081330 MoreDescriptiveText
descriptiontxt host2C:\dir\directory\txt.txt 20210106T081330 MoreDescriptiveText
descriptiontxt host3C:\dir\directory\truncated\example\123456677 20210106T081330 MoreDescriptiveText
I want the output to look like this.
descriptiontxt host1C:\dir\directory\directory\directory\txt.txt 20210106T081330 MoreDescriptiveText
descriptiontxt host2C:\dir\directory\txt.txt 20210106T081330 MoreDescriptiveText
descriptiontxt host3C:\dir\directory\truncated\example\123456677 20210106T081330 MoreDescriptiveText
How can I make sure that the date string starts on column 72 (or some other position) for every line of the file?
Regardless of whether the previous entry is 10 characters or 50 characters, given that truncate on the previous variable ensures that the path variable never overflows into that portion of the file.
Thank you.

Maybe by using the format filter, like this:
{% for file in result['files'] %}
descriptiontxt {{ inventory_hostname }}{{ "%-50s"|format(file['path'] | truncate(50))}} {{ {{ '%Y%m%dT%H%M%S' | strftime(file['lastwritetime']) }} MoreDescriptiveText
{% endfor %}
This ensure that file['path'] takes up 50 characters even if the actual string is shorter.

Related

Passing list of Relation object to dbt_utils.union_relation macro fails

Related to dbt and jinja2
I am using union_relations from dbt_utils package (0.5.0).
I created my macro which takes list of fully qualified name (like database.schema.identifier) splits it and uses api.Relations.create (link) to create a relation and append each relation to a list.
{{ list_of_relation }} is given to dbt_utils.union_relations(as relations=my_macro([list of fully qualified names])), it's giving me an _is_relation error, I did use log to debug and see if it actually creates a relation and it does. What could be wrong?
It sounds like you have a macro written something like this:
{% macro my_macro(names) %}
{% set list_of_relations = [] %}
{% for name in names %}
{% set relation = something(name) %}
{% do list_of_relations.append(relation) %}
{% endfor %}
{{ list_of_relations }}
{% endmacro %}
Instead of using {{ list_of_relation }}, you’ll want {{ return(list_of_relation) }} or {% do return(list_of_relation) %}. The problem is that {{ ... }} turns things into strings in jinja macros, and macros by default return strings.
The documentation on return is here.

Is there a way to dynamically get a list of documents in a Jekyll collection

I'm fairly new to Jekyll, and am currently trying to build a site to hold a bunch of pages containing course training material. I'm trying to use Jeykll collections to do this and have a site structure like this:
/
|
|_ "_course001"
|
|_ subdirs/subdocs
|_ "_course002"
|
|_ subdirs/subdocs
When rendering content for each course (setup as a collection) I would like a navbar to display the other documents in the current course/collection only (i.e. not in other courses).
Using something like this works:
{% for each coursedoc in site.course001 %}
[Create anchors and text]
{% endfor %}
However I want to have the collection name by dynamically assigned during the site build rather than hardcoded. I've tried doing something like this:
{% for each coursedoc in {{ page.collection | prepend: "site." }} %}
[Create anchors and text]
{% endfor %}
But this does not work, I guess because {{ page.collection | prepend: "site." }} returns a string.
Anyone have some suggestions on how I could do this?
And I think I just answered my own question:
{% assign collection = site.collections | where: "label", page.collection | first %}
{% for node in collection.docs %}
[Create anchors and text]
{% endfor %}

Why does this jekyll template not work?

Short Version:
Why does the following code not produce an output when navbox.next_article is the string '2018-01-05-man-command'?!
{% capture np %} {{ site.posts | where:"post","navbox.next_article contains post.title" }} {% endcapture %}
The next post is {{ np.title }}
Details
My post 2018-01-05-man-command.md has a YAML front matter:
---
layout : post
title : 'Man Command'
tags : [RHCSA, RHCSA_mod, Using Essential Tools, Man Command]
categories: [RHCSA]
navbox:
# prev_article:
next_article: 2018-01-05-understanding-globbing-and-wildcards
---
This is accessed by the _includes/post.html file through:
{% unless include.excerpt %}
{{ post.content }}
{% include navbox.html navbox=page.navbox %}
{% endunless %}
This is used by the _layout/post.html which sets the layout for the post:
{% include post.html post=page link_title=false %}
My navbox.html contains:
{% assign navbox = include.navbox %}
{% capture np %} {{ site.posts | where:"post","navbox.next_article contains post.title" }} {% endcapture %}
The next post is {{ np.title }}
However, all I get when I run bundle exec jekyll serve is:
The next post is
Why does that line not work? I'm new to jekyll so it's possible I've made a blunder somewhere that's intuitive to most. Please tell me what I can fix.
I believe that the capture tag only captures strings, not posts. See here for more info.
I'm not convinced that a where filter supports the contains syntax you're using. See here for more info.
On top of that, where returns an array. You have to get the first item from that array.
You need to fix these issues. Use an assign instead of a capture to store a post. And change your where filter to not use the contains syntax, which isn't valid. (Unless it's been added since the issue I just linked.)
Here is how I've done it:
{% assign post = site.posts | where:"url", targetUrl | first %}

ansible jinja2 concatenate IP addresses

I would like to cocatenate a group of ips into a string.
example ip1:2181,ip2:2181,ip3:2181,etc
{% for host in groups['zookeeper'] %}
{{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}
{% endfor %}
I have the above code, but can't seem to quite figure out how to concatenate into a string.
searching for "Jinja2 concatenate" doesn't give me the info I need.
Updated this answer, because I think I misunderstood your question.
If you want to concatenate the IP's of each host with some string, you can work with the loop controls, to check if you're in the last iteration:
{% for host in groups['zookeeper'] -%}
{{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}
{%- if not loop.last %}, {% endif -%}
{%- endfor %}
Old answer:
The word you're looking for is join:
{{ hostvars[host]['ansible_eth0']['ipv4']['address'] | join(", ") }}
You can use the 'extract' filter for this (provided you use ansible>=2.1):
{{ groups['zookeeper'] | map('extract', hostvars, ['ansible_eth0', 'ipv4', 'address']) | join(',') }}
More info:
http://docs.ansible.com/ansible/playbooks_filters.html#extracting-values-from-containers
Found a similar solution at https://adamj.eu/tech/2014/10/02/merging-groups-and-hostvars-in-ansible-variables/ .
I did a set_fact using a groups variable as suggested in the post:
- hosts: all
connection: local
tasks:
- set_fact:
fqdn_list: |
{% set comma = joiner(",") %}
{% for item in play_hosts -%}
{{ comma() }}{{ hostvars[item].ansible_default_ipv4.address }}
{%- endfor %}
This relies on joiner, which has the advantage of not having to worry about the last loop conditional. Then with set_fact I can make use of the new string in later tasks.

Titleize Jekyll category

I'd like to convert the printed category names of my posts into title case. I couldn't find a Liquid filter that would work. I tried using dashes and the camelcase filter, but no dice.
Alternatively, I'd like to print the category name as it's written in the YAML frontmatter.
For instance, for a post with:
category: Here's the Category
When I reference the name:
{% for cat in site.categories %}
<h1>{{ cat[0] }}</h1>
{% endfor %}
I see "here's the category" on the page. I would like to see "Here's the Category" or even "Here's The Category," and I could replace (replace: 'The', 'the') the few articles that I wanted to be downcase.
EDIT
For anyone as desperate as I am, this disgusting hack works, where n is the max number of words you have in a category title.
{% for cat in site.categories %}
{% assign words = cat[0] | split: ' ' %}
<h1>{{ words[0] | capitalize | replace:'The','the'}} {{ words[1] | capitalize }} {{ words[2] }} {{ words[3] | capitalize }} {{ words[4] | capitalize }} {{ words[n] | capitalize }}</h1>
{% endfor %}
I'm going to leave the question unanswered in case someone knows a more elegant method.
You can achieve a part of what you want by using the capitalize filter:
Input
{{ 'capitalize me' | capitalize }}
Output
Capitalize me
Source.
Another possibility, which I didn't test for its edge cases, is to use join and camelize:
{{ "here's the category" | join: '-' | camelize }}
It should print "Here's The Category", but camelize might have a problem with here's.
Just use {{ category.first | capitalize }} is OK for my case, without additional '' on category.
Instead of manually stepping through words 0 to n, one can use another for loop as follows:
{% for category in site.categories %}
{% assign words = category | first | split: ' ' %}
{% for word in words %}
{{ word | capitalize | replace: 'The','the' }}
{% endfor %}
{% endfor %}
I know that this is old but it helped me to solve my problem and it could help others.
The solution I came was:
{% for category in site.categories %}
<a href="/{{category[0] | slugify }}/">
{{category[0]}}
</a>
{% endfor %}
This list all the blog categories (most used for building a menu)
It turns into:
<a href="/dicas/">
Dicas
</a>
<a href="/diário-de-bordo/">
Diário de bordo
</a>
Thank you for giving me a clue.
You can check another filters in: http://jekyllrb.com/docs/templates/#filters
Name pages in Kabab-case as you want them to appear, then use replace:
{{ category.name | replace: '-', ' '}}
Don't-Capitalize-the becomes Don't Capitalize the and Capitalize-The becomes Capitalize The
This solutions is a little more overwrought, but you can adapt the author data file solution from the Jekyll site to work with categories.
In your _data folder, make a file called categories.yml. Put in something like this:
my-first-category:
name: My First Category
another-category:
name: Another Category
Then, you can loop through your categories like this:
{% for category in site.categories %}
{% assign category_slug = category[0] %}
{% assign category_data = site.data.categories[category_slug] %}
{% if category_data %}
{{ category_data.name }}
{% endif %}
{% endfor %}
Side note: I'm not sure why I needed the category_slug assign. I didn't need it when looping through post.categories.
This increases overhead by adding an extra file, but opens the door for other data that can get attached to objects like a category image or description. You can be more granular over your names. This is a pretty readable solution too, which is nice.
I went the other way around. I hard coded my page title in Title case and use downcase, upcase and capitalize liquid filter in other pages.