Reputation: 631
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.
Upvotes: 6
Views: 14778
Reputation: 9858
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)
Upvotes: -1
Reputation: 19641
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"),
...,
]
}
Upvotes: 4
Reputation: 8277
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
you can use css and customise the look .. I hope this helps !
Upvotes: 3