2

I am trying to render html template with Jinja2 - putting in variable values, which are extracted from Pandas dataframes. Currently I have variables hardcoded into render command, as seen below:

template_loader = jinja2.FileSystemLoader(searchpath=template_dir)
template_env = jinja2.Environment(loader=template_loader)
template_file = "report.html"
template = template_env.get_template(template_file)
html_code = template.render(reg=reg.iat[0, 0], ac=ac.iat[0, 0],
                            su_new=su_new.iat[0, 0], chu_new=chu_new.iat[0, 0],
                            title_0=play.iat[0, 0], title_0_count=play.iat[0, 1],
                            title_1=play.iat[1, 0], title_1_count=play.iat[1, 1],
                            title_2=play.iat[2, 0], title_2_count=play.iat[2, 1])

Issue is that sometimes, there is no 3rd row in "play" dataframe, which returns an index error (as there are no elements there).

I have created a loop to prepare variable, which could be inserted into render command, as shown below:

template_variables = "reg=reg.iat[0, 0], ac=ac.iat[0, 0],su_new=su_new.iat[0, 0], chu_new=chu_new.iat[0, 0], "

for i in range(play.shape[0]):
    template_variables += str("title_" + str(i) + "=play.iat[" + str(i) + ", 0], title_" + str(i) + "_count=play.iat[" + str(i) + ", 1], ")

template_variables = template_variables.rstrip(", ")

template_loader = jinja2.FileSystemLoader(searchpath=template_dir)
template_env = jinja2.Environment(loader=template_loader)
template_file = "report.html"
template = template_env.get_template(template_file)
html_code = template.render(template_variables)

With this, I recieve following error:

ValueError: dictionary update sequence element #0 has length 1; 2 is required

I presume reason behind it is that I have created a string, which can not be used in Jinja2 render command. Any idea if there is possibility to pass string to that command or do I have to find another way to address issue of extracting elements based on Dataframe size?

Thank you and best regards, Bostjan

2 Answers 2

1

I did some digging around and came up with solution. I am posting it here in case someone else will have similar issue. As expected, issue was that it was a single string. So initially I have prepared same string (changed "," with "|" as separator as "," is also included in iat command). Then I have created dictionary out of string and used it in template.render(). Below is the code:

template_variables = "reg=" + str(reg.iat[0, 0]) + "|ac=" + str(ac.iat[0, 0]) + "|su_new=" + str(su_new.iat[0, 0]) + "|chu_new=" + str(chu_new.iat[0, 0]) + "|"

for i in range(play.shape[0]):
    template_variables += "title_" + str(i) + "=" + str(play.iat[i, 0]) + "|title_" + str(i) + "_count=" + str(play.iat[i, 1]) + "|"

template_variables = template_variables.rstrip("|")
template_variables = dict(map(lambda variable: variable.split('='), template_variables.split('|')))

template_loader = jinja2.FileSystemLoader(searchpath=template_dir)
template_env = jinja2.Environment(loader=template_loader)
template_file = "report.html"
template = template_env.get_template(template_file)
html_code = template.render(template_variables)

Might not the the most elegant way of doing it, but it seems to do the trick.

Best regards, Bostjan

Sign up to request clarification or add additional context in comments.

1 Comment

Another way I found is an option is simply input df.as_matrix() into render function and then add "for" loop in html template itself.
0

Instead of doing all the magic to construct a dictionary from strings, you can create one easily by using a literal:

template_variables = dict(
    reg=reg.iat[0, 0],
    ac=ac.iat[0, 0],
    su_new=su_new.iat[0, 0],
    chu_new=chu_new.iat[0, 0],
    titles=[
        {"name": play_iat[i,0], "count": play_iat[i,1]}
        for i in range(len(play_iat))
    ]
)

html_code = template.render(template_variables)

In the template, you would then reference the reg, ac, su_new, chu_new variables as usual (e.g. {{ reg }}), and then use something like the following to list the titles variables:

{% for title in titles %}
    Name: {{ title.name }}, Count: {{ title.count }}
{% endfor %}

The special magic in the titles entry in the dict constructor is a list comprehension, which builds up a list of things based on items taken from the sequence you specify (integer indexes into play_iat in this case.) So each time the comprehension fetches an integer from the range sequence, it constructs another dictionary from the pieces of play_iat specified, and adds that dictionary onto the list it's building. It then puts the list into the outer dict under the titles key.

The template references the list of titles in a for loop, grabbing each one and placing it into the item variable, where you can reference the name and count fields of the corresponding inner dictionary using dot syntax.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.