Is there a way to write a Jinja set nested within a Jinja for loop - jinja2

I am currently using Jinja within DBT to create these three sets which all work fine:
{% set value1 %}
select
category1,
category2,
category3,
category4
from {{ ref('table') }}
where category1 = 'value1'
order by category2
{% endset %}
{% set value2 %}
select
category1,
category2,
category3,
category4
from {{ ref('table') }}
where category1 = value2
order by category2
{% endset %}
{% set value3 %}
select
category1,
category2,
category3,
category4
from {{ ref('table') }}
where category1 = value3
order by category2
{% endset %}
I am wondering if there is a way I can condense the creation of those three sets by using a for loop.
This was my attempt at doing so:
{% set attributes = ('value1', 'value2', 'value3') %}
{% for i in attributes%}
{% set {{i}} %}
select
category1,
category2,
category3,
category4
from {{ ref('table') }}
where category1 = {{i}}
order by category2
{% endset %}
{% endfor %}
I keep running into errors regarding the first line of the set within the for loop. I have attempted writing it several ways with syntax changes within the for loop but nothing has worked.
I am wondering if I can recreate the output of the three sets in anyway which would be more condescend then having to write out all three sets?

Your use case seems a perfect fit for a macro:
Define your macro:
{% macro get_set(value) %}
select
category1,
category2,
category3,
category4
from {{ ref('table') }}
where category1 = {{ value }}
order by category2
{% endmacro %}
Then to use it:
{{ get_set('value1') }}
{# and #}
{{ get_set('value2') }}
{# and #}
{{ get_set('value3') }}

Related

DBT : Getting error ‘>’ not supported between instances of ‘Table’ and ‘int’

I have to compare the result of query with hardcoded integer value in if condition
{%- set query %}
select cast(1 as integer)
{% endset -%}
{%- set main = run_query(query) -%}
{% if execute %}
{% if main > 2 %}
Getting below error…
'>' not supported between instances of 'Table' and 'int'
The query select cast(1 as integer) returns table(single column/single row) and it is not directly comparable with constant integer:
{% if main > 2 %}
should rather be:
{% if (main.columns[0].values()[0]) > 2 %}

Liquid filter by date (Jekyll)

I'm trying to filter posts by specific dates in Jekyll. For example, I want to filer all posts posts today, in the past 30 days and everything before that.
However I'm running into a couple of issues:
How can I get the current date, starting at 00:01AM
How do I get the current date, and subtract 30 days
How can I use where_exp to filter by this date
Currently I tried to do something like this, but this converts the date into a string, and can't be used in the where_exp:
{% capture thirty_days_ago %}{{'now' | date: '%s' | minus: 2592000 }}{% endcapture %}
{% assign last_30_days_posts = site.posts | where_exp:"post", "post.posted_on > thirty_days_ago" %}
Liquid error (line 22): comparison of Time with String failed in index.html
I could do a simple check when looping over the posts, but would prefer using a filter before this.
{% capture thirty_days_ago %}{{'now' | date: '%s' | minus: 2592000 }}{% endcapture %}
{% for post in site.posts %}
{% capture post_date %}{{ post.posted_on | date: '%s' | plus: 0 }}{% endcapture %}
{% if job_date > thirty_days_ago %}
{% include components/job.html job=job %}
{% endif %}
{% endfor %}
For anyone looking at a solution; I ended up writing a custom filter:
def last_month_filter(posts)
now = DateTime.now
today = DateTime.new(now.year, now.month, now.day, 0, 0, 0, now.zone)
target = today - 30
posts.select do |post|
postedOn = post.data['posted_on'].to_datetime
if postedOn < today && postedOn > target
post
end
end
end
Usage:
{% assign last_30_days = site.posts | last_month_filter | sort:"posted_on" | reverse %}

In Jekyll, how to show "posts from last week"

I'm not sure to get the liquid syntax to help me pull posts that were published during the same week as the build date. Is that possible?
There are simpler ways to get a list of recent posts, but this approach would be useful for my project. I've tried several things I found by searching but no joy yet.
Before even giving this answer a second thought, consider the fact that, at the very least the first part of this answer doesn't really work, because jekyll is a static site generator and therefore the shown posts are relative to the last build date, which may not be the same as current date.
The second part of the answer goes a bit deeper into the idea of actually generating a list of "recent posts", rather than "posts from last week".
To basically explain the code in words: First we get current year and current week, then we loop through every post and compare current year and current week to the week and year of the post. If they match, the post is shown.
Show — Build Week:
{% assign currentYear = site.time | date: "%Y" %}
{% assign currentWeek = site.time | date: "%W" %}
{%- for post in site.posts -%}
{% assign postYear = post.date | date: "%Y" %}
{% assign postWeek = post.date | date: "%W" %}
{%- if currentYear == postYear and currentWeek == postWeek -%}
{{ post.title }}
{%- endif -%}
{%- endfor -%}
Show — Build Day and 6 Days Prior:
{% assign currentYear = site.time | date: "%Y" %}
{% assign currentDay = site.time | date: "%j" | plus: 0 %}
{% assign currentDay_minus_week = site.time | date: "%j" | minus: 7 %}
{%- for post in site.posts -%}
{% assign postYear = post.date | date: "%Y" %}
{% assign postDay = post.date | date: "%j" | plus: 0 %}
{%- if currentYear == postYear and postDay > currentDay_minus_week and postDay <= currentDay -%}
{{ post.title }}
{%- endif -%}
{%- endfor -%}
In an effort to sort of salvage this answer, though it already kind of veered off course.... I wrote this code with similar logic, but this time it gets the year and week of the latest post and shows all posts posted that year and that week.
This would be bulletproof in terms of showing something even if you keep building the site without making new posts. But it also just shows only one post, if your last post is the only post posted that week, which may be a bit dumb...
On the other hand, the simplest method for showing "recent posts" is probably just using the limit and limit recent posts to like last 5 posts or something like that: {%- for post in site.posts limit: 5 -%}
Show — Latest Post Week:
{% assign latestPost_year = site.posts.first.date | date: "%Y" %}
{% assign latestPost_week = site.posts.first.date | date: "%W" %}
{%- for post in site.posts -%}
{% assign postYear = post.date | date: "%Y" %}
{% assign postWeek = post.date | date: "%W" %}
{%- if latestPost_year == postYear and latestPost_week == postWeek -%}
{{ post.title }}
{%- endif -%}
{%- endfor -%}
Show — Latest Post Day and 6 Days Prior
{% assign latestPost_year = site.posts.first.date | date: "%Y" %}
{% assign latestPost_day = site.posts.first.date | date: "%j" | plus: 0 %}
{% assign latestPost_day_minus_week = site.posts.first.date | date: "%j" | minus: 7 %}
{%- for post in site.posts -%}
{% assign postYear = post.date | date: "%Y" %}
{% assign postDay = post.date | date: "%j" | plus: 0 %}
{%- if latestPost_year == postYear and postDay > latestPost_day_minus_week and postDay <= latestPost_day -%}
{{ post.title }}
{%- endif -%}
{%- endfor -%}
The above solution works, but doesn't span years.
So I utilized the concepts and came up with a simpler and more flexible solution for filtering for a timeframe (which can be any variable time span of seconds, hours, days, weeks, or months). My solution has fewer variables, and less logic.
Liquid date/time uses unix timestamp (%s = seconds since 1970). So I kept the timeframe in seconds and do the conversion for the length of time. 86400 = 1 day, 604800 = 1 wk, 2678400 = 31 days, ... and so on.
The code also assumes your posts use last_modified_at in your post frontmatter. You could substitute for post.date if you're not.
Simple Solution
List posts within last week
{% assign timeframe = 604800 %}
{% for post in site.posts %}
{% assign post_in_seconds = post.last_modified_at | date: "%s" | plus: 0 %}
{% assign recent_posts = "now" | date: "%s" | minus: timeframe %}
{% if post_in_seconds > recent_posts %}
{{ post.title }}
{% endif %}
{% endfor %}
Alternative
List posts within timeframe and flag new or modified
This code will list all posts to within a limit and flag an posts as new or modified in timeframe. Length of list is limited to maxposts. Note: css class is designed to utilize Bootstrap, remove/edit to your liking.
timeframe 2419200 = seconds in 4 weeks
maxposts = 10
label_FOOBAR just clean way to handle html with liquid
Example Results with new/modified flags & dates
Code
{% assign timeframe = 2419200 %}
{% assign maxposts = 10 %}
{% assign date_format = site.minima.date_format | default: "%m/%d" %}
<ul class="post-list text-muted list-unstyled">
{% for post in site.posts limit: maxposts %}
{% assign post_in_seconds = post.last_modified_at | date: "%s" | plus: 0 %}
{% assign recent_posts = "now" | date: "%s" | minus: timeframe %}
{% assign post_updated = post.last_modified_at | date: date_format %}
{% capture post_date %}<small>{{ post.date | date: date_format }}</small>{% endcapture %}
{% if post_in_seconds > recent_posts %}
{% capture label_new %}<span class="label label-primary">new</span>{% endcapture %}
{% if post.last_modified_at > post.date %}
{% assign label_new = '' %}{% comment %}Clear NEW if modified{% endcomment %}
{% capture label_updated %}<span class="label label-info">Updated <span class="badge">{{ post_updated }}</span></span>{% endcapture %}
{% endif %}
{% endif %}
<li>
<h4>{{ post_date }}
<a class="post-link" href="{{ post.url | relative_url }}">
{{ post.title | escape }}</a> {{ label_new }}{{ label_updated }}
</h4>
</li>
{% assign post_date = '' %}
{% assign label_updated = '' %}
{% endfor %}
</ul>
You never know when Jekyll builds, so Jekyll should output a big(ger) list of posts. You can use the code of Joonas for this. Then you should use javascript to hide the non-relevant posts (those older than one week).
This can easily be done by adding a custom attribute to your list, like this:
<li date="{{ post.date }}">{{ post.title }}</li>
Use jQuery (or vanilla js) to hide old posts:
// loop through all list items with a date
$('li[date]').each(function(){
// create a postDate in a date object
var postDate = new Date($(this).attr('date'));
// create an object with the date of one week ago
var oneWeekAgo = new Date();
oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
// compare dates and hide old posts
if(postDate<oneWeekAgo) $(this).hide();
});

Can you assign date to a variable in jekyll?

This is what I'm currently attempting, however it does not process...
{% assign current_month = {{ site.time | date: '%m' }} %}
Remove extra brackets :
{% assign current_month = site.time | date: '%m' %}
How did you use the variable?
{% assign current_month = {{ site.time | date: '%m' }} %}
{{ current_month }}
Will out put the current month.

How do I add a parameter dynamically to jekyll-timeago plugin?

This seems like a really trivial problem, but I can't seem to make it work.
I'm trying to add jekyll-timeago plugin to jekyll. In the example docs, it says that if I wanted to add a parameter, I can do this:
{{ page.date | timeago: '2020-1-1' }}
But what if my parameter is a variable? Here is my code:
{% if exp.durationEnd == 'Present' %}
{% assign endDate = site.time %}
{% else %}
{% assign endDate = exp.durationEnd %}
{% endif %}
<div>{{ exp.durationStart }} – {{ exp.durationEnd }} ( {{ exp.durationStart | timeago: '{{ endDate }}' }} ) </div>
I can't seem to make it work. Extra emphasis on:
{{ exp.durationStart | timeago: '{{ endDate }}' }}
The code results in:
2 years and 8 months' }} with that extra ' }} which I think is wrong.
I have tried various expressions like the following but to no avail:
{{ exp.durationStart | timeago: endDate }}
{{ exp.durationStart | timeago: '{% endDate %}' }}
{% assign endDate = timeago exp.durationStart site.time %}
nvm.. I got it.
{{ exp.durationStart }} – {{ exp.durationEnd }} ( {{ exp.durationStart | timeago : endDate }} )