Creating a HTML table with Python with multiple columns for a specific row
Asked Answered
S

3

6

I wrote this code:

tr = ""
for author, projects in data.iteritems():
    tr + = "<tr><td>{}</td>".format(author)
    for project, branches in projects.iteritems():
        tr += "<td>{}</td>".format(project)
        for branch in branches:
            tr += "<td>{}</td>".format(branch)
    tr += </td></tr>
end = "</table>"

I have this dataset

{
'user_one': {'project_a': ['branch_1', 'branch_2'],
          'project_b': ['branch_1']},
'user_two': {'project_x': ['branch_x1', 'branch_b'] }
}

I want to print table like below:

+-------------------------------------------+
|    User    |    Project    |    Branch    |
+------------+---------------+--------------+
|  user_one  |   project_a   |   branch_1   |
+------------+---------------+--------------+
|            |               |   branch_2   |
+------------+---------------+--------------+
|            |   project_b   |   branch_1   |
+------------+---------------+--------------+
|  user_two  |  project_x    |   branch_x1  |
+------------+---------------+--------------+
|            |               |   branch_b   |
+------------+---------------+--------------+

if its single project it works fine but when it comes multiple projects, it doesn't. I can get the result using PrettyTable but I since I want project_a, _b , _x etc to be hyperlinks. I cannot achieve it in while using PrettyTable, so I started writing my own html generator based on data.

Succotash answered 12/10, 2018 at 9:54 Comment(1)
Possible duplicate of Creating HTML in pythonAikido
N
3

why depend on a whole package if your need is just rendering the table !

table = "<table border=1 ><tr><th>user</th><th>Project</th><th>Branch</th></tr>"
tr = ""
td_1 = ""
td_2 = ""
for author, projects in data.iteritems():
    # reset the value for new input.
    td_1 = ""
    td_2 = ""
    for project, branches in projects.iteritems():
        td_1 += "{}<hr>".format(project)
        for branch in branches:
            td_2 += "{}<hr>".format(branch)
    tr += "<tr><td valign='top'>{}</td><td valign='top'>{}</td><td valign='top'>{}</td></tr>".format(author, td_1, td_2)

end = "</table>"
table = table + tr + end

this renders

enter image description here

you can use css and customise the look .. I hope this helps !

Necrology answered 12/10, 2018 at 15:37 Comment(0)
L
4

Beyond trivial HTML (a table maybe not) I recommend using a template library.

I'd pick Jinja2. Its syntax is quite simple and intuitive (if you have seen aany other template language), it's well documented, and it's quite popular (=more SO support).

An example to render a table.

<table class="table table-striped">
    <thead><tr>
        <th>One</th>
        <th>Two</th>
        <th>Three</th>
        <th>Four</th>
    </tr></thead>
    <tbody>
    {% for row in tabular_data %}
    <tr>
        <td>{{ row.one }}</td>
        <td>{{ row.two }}</td>
        <td>{{ row.three }}</td>
        <td>{{ row.four }}</td>
    </tr>
    {% endfor %}
    </tbody>
</table>

If you're using a web framework it's probably supported out of the box, if not, rendering it are just a few lines:

from jinja2 import Environment, FileSystemLoader  # pip install Jinja2

env = Environment(loader=FileSystemLoader("/path/to/templates/folder")
template = env.get_template("TableTemplate.html")  # the template file name
html = template.render(**context_data)

Where context_data is a dictionary with the needed data. In the example above it expects a tabular_data field holding an array of objects (or dictionaries) with properties one, two, ...:

context_data = {
    # Row = namedtuple("Row", ["one", "two", "three", "four"])
    'tabular_data': [             
        Row(1, 2, 3, 4), 
        Row("a", "b", "c", "d"),
        ..., 
    ]
}
Ligula answered 12/10, 2018 at 18:45 Comment(1)
from collections import namedtuple import collections context_data = {} tabular_data = [] fields = ['one', 'two', 'three', 'four'] Row = collections.namedtuple('Row', fields) row1 = Row('1', 2, '3', '4') tabular_data.append(row1) tabular_data.append(row1) context_data['tabular_data'] = tabular_data html = template.render(context_data)Imre
N
3

why depend on a whole package if your need is just rendering the table !

table = "<table border=1 ><tr><th>user</th><th>Project</th><th>Branch</th></tr>"
tr = ""
td_1 = ""
td_2 = ""
for author, projects in data.iteritems():
    # reset the value for new input.
    td_1 = ""
    td_2 = ""
    for project, branches in projects.iteritems():
        td_1 += "{}<hr>".format(project)
        for branch in branches:
            td_2 += "{}<hr>".format(branch)
    tr += "<tr><td valign='top'>{}</td><td valign='top'>{}</td><td valign='top'>{}</td></tr>".format(author, td_1, td_2)

end = "</table>"
table = table + tr + end

this renders

enter image description here

you can use css and customise the look .. I hope this helps !

Necrology answered 12/10, 2018 at 15:37 Comment(0)
K
-1

I would first convert your dictionaries into a list of lists with a simpler table structure, with empty cells as required.

def dicts_to_lists(data):
    """ Convert data stored as lists within dicts within a dict, to a simple
        list of lists """
    r = []
    for user, projects in data.items():
        user_cell = user
        for project, branches in projects.items():
            project_cell = project
            for branch in branches:
                r.append([user_cell, project_cell, branch])
                user_cell = ""
                project_cell = ""
    return r

Dictionaries are not ordered in Python, so the function may output 'project_B' before 'project_A'. If you need to maintain the same order, use an OrderedDict to store the data. Otherwise you could write a more complicated function that sorts the keys alphabetically.

Then, you can use a templating language or write a short generic function to convert any list of lists into an html table:

def lists_to_html(data, has_header=True):
    html = "<table>"
    for i, row in enumerate(data):
        if has_header and i == 0:
            tag = "th"
        else:
            tag = "td"
        tds = ''.join("<{}>{}</{}>".format(tag, cell, tag) for cell in row)
        html += "<tr>{}</tr>".format(tds)
    html += "</table>"
    return html


data = {
'user_one': {'project_a': ['branch_1', 'branch_2'],
          'project_b': ['branch_1']},
'user_two': {'project_x': ['branch_x1', 'branch_b'] }
}
table_cells = dicts_to_lists(data)
table_cells = [["User", "Project", "Branch"]] + table_cells
print (lists_to_html(table_cells))

That lists_to_html function can be done using a jinja2 template like this:

def lists_to_html(data):
    template = """
    <table><tr><th>User</th><th>Project</th><th>Branch</th></tr>
    {% for r in data %}
    <tr><td>{{ r.author }}</td><td<{{ r.project }}</td><td>{{ r.branch }}</td></tr>
    {% endfor %}</table>"""
    return jinja2.Environment().from_string(template).render(data=data)

Alternatively, you could replace both functions with a slightly more complicated jinja2 template:

template = """
<table>
<tr><th>User</th><th>Project</th><th>Branch</th></tr>
{% for author, projects in data.items() %}
{% for project, branches in projects.items() %}
{% set project_loop = loop %}
{% for branch in branches %}
<tr><td>{% if project_loop.first and loop.first %}{{ author }}{% endif %}</td>
<td>{% if loop.first %}{{ project }}{% endif %}</td>
<td>{{ branch }}</td></tr>
{% endfor %}
{% endfor %}
{% endfor %}
</table>
"""
print jinja2.Environment().from_string(template).render(data=data)
Kingkingbird answered 12/10, 2018 at 19:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.