Recursively display file tree structure with Tornado template - html

I want to display the file tree inside a particular folder (RESULTS) on my html page. I am using python Tornado. I used the answer to this question to get me most of the way, and modified to try and work with Tornado.
With the code below, the top directory is displayed as the header, and the folders inside the top directory are displayed, but the template doesn't loop through the items in the subtrees.
Here is the render call:
def get(self):
logging.info("Loading Results tree...")
self.render("results.html", tree=make_results_tree('RESULTS'))
Here is the make_results_tree function:
def make_results_tree(path):
tree = dict(name=path, children=[])
lst = os.listdir(path)
if (path == 'RESULTS' and len(lst) == 1):
tree['children'].append(dict(name="No results recorded for today"))
else:
for name in lst:
fn = os.path.join(path, name)
if os.path.isdir(fn):
tree['children'].append(make_results_tree(fn))
elif (name != '.gitkeep'):
tree['children'].append(dict(name=name))
return tree
I have verified that the python code above all works as intended, thus the issue is in the template code below (results.html), likely in the if or loop block:
<div class="center">
<h2>{{ tree['name'] }}</h2>
<ul>
{% for item in tree['children'] %}
<li>{{ item['name'] }}
{% if locals().get('item["children"]', False) %}
<ul>{{ loop(item['children']) }}</ul>
{% end %}</li>
{% end %}
</ul>
</div>
Why does the template code not loop through multiple levels of the tree?

loop() is a feature of Jinja2; there is no equivalent in Tornado templates. Instead of anonymous recursion, split the file into two files so that the inner file can call itself recursively.
Also, locals().get() only performs simple lookups, it can't resolve complex expressions like eval() can. You don't need locals here, just operate on item directly.
results.html:
<div class="center">
<h2>{{ tree['name'] }}</h2>
<ul>{% module Template('items.html', children=tree['children']) %}</ul>
</div>
items.html:
{% for item in children %}
<li>{{ item['name'] }}
{% if "children" in item %}
<ul>{% module Template('items.html', children=item['children']) %}</ul>
{% end %}</li>
{% end %}

Related

Is it possible to pass a list into includes variables with Django?

I'm trying to create a reusable sub template to use on my pages that's something like this:
<div>
<h1>{{ heading }}</h1>
<p>{{ copy }}</p>
<ul>
{% for list_item in list %}
<li>{{ list_item }}</li>
{% endfor %}
</ul>
</div>
But, I'm wondering if it's possible to pass a list into the include? Maybe something like this?
{% includes 'template.html' with list="['list_item1', 'list_item2']" %}
If you want use variables with same data on many pages you may want to use context processors. You can use zip() in return for your list if you are looking for this.
context_processors.py (in correct app folder)
def extras(request):
var1 = item1
var2 = item2
zipped_vars = zip(var1, var2)
return {'zipped_vars': zipped_vars}

Using Jekyll, how do you alter an array's contents using a for loop?

Say I have an array thingy.foo = ['abc', 'def'] in my scope.
My goal is to be able to loop over all the items in thingy.foo and apply some conditional logic to it, overwriting the existing item in the array... Something like this:
{% for item in thingy.foo %}
{% assign thingy.foo[forloop.index0] = site.data.lookups[item] | default: item %}
{% endfor %}
What I am doing do the item is a bit irrelevant, the part I'm having issues with is updating the item in the array. The code compiles and runs. Within the loop, I can confirm that the "lookup" part works (if I assign it to t and inspect t then I get a looked up value, but thingy.foo[0] is still the original value).
Is it possible to update/overwrite arrays in Jekyll?
(this is intended for use on GitHub Pages, so I cannot use custom plugins).
It looks like you cannot mutate existing arrays... but you can loop over the initial array and mutate items into a new array, like this:
{% assign newArray = '' | split: '' %}
{% for item in thingy.foo %}
{% assign newItem = site.data.lookups[item] | default: item %}
{% assign newArray = newArray | push: newItem %}
{% endfor %}
The newArray now contains a list of altered items from thingy.foo.

Flask, jinja2 - Dynamically append templates one after another

I am building a chatbot. There are few child templates like login.html, messages.html, transaction.html, etc. I want to append these templates in base.html dynamically. I am extending base.html in all these templates. My problem is only one template is rendered at a time. Is there any solution for appending these templates one after another? I have used {%include%} but it's a static approach. I need dynamic.
printer.py looks like -
#app.route('/respond', methods=['GET','POST'])
def respond_def():
message = request.form['message_input']
if message == "l":
return render_template('printer/login.html')
elif message == "t":
return render_template('printer/transactionID.html')
base.html looks like -
//some code here
<li>
{% block template %}{% endblock %}
</li>
//some code here
message.html looks like -
{% extends "base.html" %}
{% block template %}
<div> Message template called </div>
{% endblock %}
I resolved it.
I made a list of templates in printer.py and then appended those templates in base.html whenever user asked for it.
printer.py
dictionary = []
// append name of template in this whenever needed.
return render_template('printer/base.html', dictionary=dictionary)
base.html
{% for d in dicts %}
{% set template = 'printer/' + d + '.html' %}
// can add conditions for different templates
{% include template %}
{% endfor %}

How to loop through all files in Jekyll's _data folder?

How can I loop through every file in my _data folder in Jekyll?
Currently I have a list of files in a file called sidebarlist.yml like this:
- file1
- file2
- file3
In order to loop through all of these files, I use this code:
{% for sidebar in site.data.sidebarlist %}
{% for entry in site.data.sidebars[sidebar].entries %}
...
{% endfor %}
{% endfor %}
I would like to avoid using sidebarlist.yml and just iterate through all files within _data automatically. Can I do this?
Nesting loops allows you to loop through the contents of _data files.
When I did this I used a subdirectory, since I didn't want to loop through every data file, and I think that applies to many use cases. It also keeps my _data directory a little tidier.
My _data directory looks like this:
_data/
navigation.yml
news.yml
people/
advisors.yml
board.yml
staff.yml
Each of the files within people/ uses a structure like this:
- name: Anne Smith
role: Role A
url: mysite.com
- name: Joe Shmoe
role: Role B
url: mysite.org
And on the page where I'm looping through each of these data files:
{% for people_hash in site.data.people %}
{% assign people = people_hash[1] %}
{% for person in people %}
<li>{{ person.name }}, {{ person.role }}</li>
{% endfor %}
{% endfor %}
This results in:
<li>Anne Smith, Role A</li>
<li>Joe Shmoe, Role B</li>
It's very similar to what you've already done, but eliminates the need for that extra yaml file.
Note the use of people_hash[1] - this is what is targeting the appropriate values within the array.
If instead you do:
{% for people_hash in site.data.people %}
{% assign people = people_hash[1] %}
<pre>{{ people }}</pre>
{% endfor %}
You'll get the array of values that is returned, which should help you debug your template.
I have read your question title, and I will answer your last question:
You can't loop through files you keep in _data folder. According to Jekyll Variable doc and Jekyll Directory structure all the file in _data with supported extension .yml .yaml .csv .jsonby default will be loaded in site.data like #wasthishelpfull's answered and you access it via {{site.data.*filename.data*}} and loop though like this answer
If you wanna loop through files, create a folder (no underscore) serve it as static files, and use jquery.get() for the data in the file.
Or change _data to data in _config.yml by adding data_source: data and access at a url endpoint /data see this post for more
According to the documentation, jekyll will load YAML resources (.yml, .yaml, .json, and .csv files) directly into site.data. If your files use one of these formats, you can do:
{% for data in site.data %}
...
{% endfor %}
I assume you need to access jekyll site.data in a way of looping multi levels object:
{% assign my_data = site.data %}
{% assign my_level = "sidebarlist.sidebars.sidebar" | split: "." %}
{% for level in my_level %}
{% assign my_data = my_data[level[i]] %}
{% for data in my_data %}
{{ data }} : {{ my_data[data] }}
{% endfor %}
{% endfor %}

Jekyll Child Page Navigation

I'm finishing up my site redesign and just need to complete my portfolio page. Rather than using posts for portfolio entries, I want to use subdirectories/child pages:
...
work
project
index.html
project-2
index.html
index.html
...
I want to loop through those subpages in a list to show on work/index.html.
Something similar to:
<ul>
{% for page in site.work.pages %}
<li>
<figure>
<img src="/img/foo.jpg" alt="foo">
</figure>
</li>
{% endfor %}
</ul>
How can this be done?
Jekyll does not support this as simply as in your example yet, it is coming in 2.0 however.
You could add a key/value pair to the YAML header of the child pages to signify that it should appear on the main index page. I have a similar setup that I use to define what pages should appear in the main navigation for the site.
project/index.html etc
---
group: work
---
work/index.html
<ul>
{% for node in site.pages %}
{% if 'work' == node.group %}
<li>{{node.title}}</li>
{% endif %}
{% endfor %}
</ul>
You may be able to avoid requiring the group attribute if you changed the if condition to do substring matching of the URL, but this solution is easier to understand.
If you do your own page build with jekyll build you can simply create a file called _plugins/page_filters.rb with the following contents:
module Jekyll
module PageFilters
def children_of(all_pages, parent)
all_pages.select { |p| child_of?(p, parent) }
end
private
def child_of?(child, parent)
parent_path = parent["path"]
child_path = child.path
# Exclude 'index.md' from becoming a child of itself
return false if parent_path == child_path
# Remove 'index.md' from the parent path
parent_path = parent_path.split("index.md", 2).first
child_path.start_with? parent_path
end
end
end
Liquid::Template.register_filter(Jekyll::PageFilters)
And then invoke the children_of filter as such:
{% assign children = site.pages | children_of: page %}
<ul>
{% for child in children %}
<li>{{ child.title }}</li>
{% endfor %}
</ul>