Skip to content

Commit

Permalink
Start working on a render_table method to generate a HTML table with …
Browse files Browse the repository at this point in the history
…the chart values.
  • Loading branch information
paradoxxxzero committed May 21, 2014
1 parent 3352e02 commit 30a1660
Show file tree
Hide file tree
Showing 8 changed files with 315 additions and 44 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
V 1.5.0 UNRELEASED
Add per serie configuration
Add half pie (thanks philt2001)
Add render_table (WIP)

V 1.4.6
Add support for \n separated multiline titles (thanks sirlark)
Expand Down
52 changes: 52 additions & 0 deletions demo/moulinrouge/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ def svg(type, series, config):
graph.add(title, values)
return graph.render_response()

@app.route("/table/<type>/<series>/<config>")
def table(type, series, config):
graph = getattr(pygal, type)(pickle.loads(b64decode(str(config))))
for title, values in pickle.loads(b64decode(str(series))):
graph.add(title, values)
return graph.render_table()

@app.route("/sparkline/<style>")
@app.route("/sparkline/parameric/<style>/<color>")
def sparkline(style, color=None):
Expand All @@ -109,6 +116,51 @@ def sparkline(style, color=None):
return Response(
line.render_sparkline(height=40), mimetype='image/svg+xml')

@app.route("/with/table/<type>")
def with_table(type):
chart = pygal.StackedBar(
disable_xml_declaration=True,
x_label_rotation=35)
chart.title = (
'What Linux distro do you primarily use'
' on your server computers? (Desktop'
' users vs Server Users)')

if type == 'series':
chart.add('Debian', [1775, 82])
chart.add('Ubuntu', [1515, 80])
chart.add('CentOS', [807, 60])
chart.add('Arch Linux', [549, 12])
chart.add('Red Hat Enterprise Linux', [247, 10])
chart.add('Gentoo', [129, 7])
chart.add('Fedora', [91, 6])
chart.add('Amazon Linux', [60, 0])
chart.add('OpenSUSE', [58, 0])
chart.add('Slackware', [50, 3])
chart.add('Xubuntu', [38, 1])
chart.add('Rasbian', [33, 4])
chart.add('SUSE Linux Enterprise Server', [33, 1])
chart.add('Linux Mint', [30, 4])
chart.add('Scientific Linux', [32, 0])
chart.add('Other', [187, 5])

elif type == 'labels':
chart.x_labels = [
'Debian', 'Ubuntu', 'CentOS', 'Arch Linux',
'Red Hat Enterprise Linux', 'Gentoo', 'Fedora', 'Amazon Linux',
'OpenSUSE', 'Slackware', 'Xubuntu', 'Rasbian',
'SUSE Linux Enterprise Server', 'Linux Mint',
'Scientific Linux', 'Other']
chart.add('Desktop Users', [
1775, 1515, 807, 549, 247, 129, 91, 60, 58, 50, 38, 33, 33,
30, 32, 187
])
chart.add('Server Users', [
82, 80, 60, 12, 10, 7, 6, 0, 0, 3, 1, 4, 1, 4, 0, 5
])

return render_template('table.jinja2', chart=chart)

@app.route("/all")
@app.route("/all/<style>")
@app.route("/all/<style>/<color>")
Expand Down
84 changes: 62 additions & 22 deletions demo/moulinrouge/static/js.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,64 @@
$(function () {
$('figure figcaption').append(
$('<button>')
.text('⟳')
.click(function() {
var $fig, $embed, w, h, src;
$fig = $(this).closest('figure');
$embed = $fig.find('embed');
w = $embed.width();
h = $embed.height();
src = $embed.attr('src');
$embed.remove();
$fig.prepend(
$('<embed>')
.attr({
src: src,
type: 'image/svg+xml',
width: w,
height: h
})
);
})
);
$('figure figcaption').append(
$('<button>')
.text('⟳')
.click(function() {
var $fig, $embed, w, h, src;
$fig = $(this).closest('figure');
$embed = $fig.find('embed');
w = $embed.width();
h = $embed.height();
src = $embed.attr('src');
$embed.remove();
$fig.prepend(
$('<embed>')
.attr({
src: src,
type: 'image/svg+xml',
width: w,
height: h
})
);
}),
$('<button>')
.text('📄')
.click(function() {
var $fig, $embed, w, h, src;
$fig = $(this).closest('figure');
$embed = $fig.find('embed');
w = $embed.width();
h = $embed.height();
src = $embed.attr('src');
$embed.remove();
$fig.prepend(
$('<embed>')
.attr({
src: src.replace('/svg/', '/table/'),
type: 'text/html',
width: w,
height: h
})
);
}),
$('<button>')
.text('📈')
.click(function() {
var $fig, $embed, w, h, src;
$fig = $(this).closest('figure');
$embed = $fig.find('embed');
w = $embed.width();
h = $embed.height();
src = $embed.attr('src');
$embed.remove();
$fig.prepend(
$('<embed>')
.attr({
src: src.replace('/table/', '/svg/'),
type: 'text/html',
width: w,
height: h
})
);
})
);
});
55 changes: 33 additions & 22 deletions demo/moulinrouge/templates/index.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,53 @@

{% block section %}
<h1>Moulin Rouge</h1>
<ul>
<li>Styles</li>
{% for style in styles %}
<li>
<embed src="{{ url_for('sparkline', style=style) }}" type="image/svg+xml" />
<a href="{{ url_for('all', style=style) }}">{{ style | title }}</a>
</li>
{% endfor %}
<li>Parametric Styles</li>
{% for color in parametric_colors %}
<li><embed src="{{ url_for('sparkline', style='RotateStyle', color=color) }}" type="image/svg+xml" /></li>
{% for style in parametric_styles %}
<li>
<a href="{{ url_for('all', style=style, color=color) }}">{{ style }} for {{ color }}</a>
</li>
{% endfor %}
{% endfor %}
</ul>

<a href="{{ url_for('interpolation') }}">Interpolation</a>
<a href="{{ url_for('rotation') }}">Rotations test</a>
<a href="{{ url_for('raw_svgs') }}">Raw svgs</a>
{% for kind in ('series', 'labels') %}
<a href="{{ url_for('with_table', type=kind) }}">Chart with HTML table ({{ kind }})</a>
{% endfor %}

<hr />
<h3>Quick tests</h3>
<dl>
{% for link in links %}
{% if link.endswith('_for') %}
<dd>{{ link.replace('test_', '').replace('_for', '').replace('_', ' ') | title }}
<small>
{% for chart_name in charts_name %}
<a href="{{ url_for(link, chart=chart_name) }}">{{ chart_name | title }}</a>
{% endfor %}
{% for chart_name in charts_name %}
<a href="{{ url_for(link, chart=chart_name) }}">{{ chart_name | title }}</a>
{% endfor %}
</small>
</dd>
{% else %}
<dd><a href="{{ url_for(link) }}">{{ link.replace('test_', '').replace('_', ' ') | title }}</a></dd>
{% endif %}
{% endif %}
{% endfor %}
</dl>

<hr />
<h3>Styles</h3>
<ul>
{% for style in styles %}
<li>
<embed src="{{ url_for('sparkline', style=style) }}" type="image/svg+xml" />
<a href="{{ url_for('all', style=style) }}">{{ style | title }}</a>
</li>
{% endfor %}
</ul>

<hr />
<h3>Parametric Styles</h3>
<ul>
{% for color in parametric_colors %}
<li><embed src="{{ url_for('sparkline', style='RotateStyle', color=color) }}" type="image/svg+xml" /></li>
{% for style in parametric_styles %}
<li>
<a href="{{ url_for('all', style=style, color=color) }}">{{ style }} for {{ color }}</a>
</li>
{% endfor %}
{% endfor %}
</ul>
{% endblock section %}
18 changes: 18 additions & 0 deletions demo/moulinrouge/templates/table.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{% extends '_layout.jinja2' %}

{% block section %}
<h3>Normal</h3>
{{ chart.render_table() }}

<h3>Total</h3>
{{ chart.render_table(total=True) }}

<h3>Transposed</h3>
{{ chart.render_table(transpose=True) }}

<h3>Transposed + total</h3>
{{ chart.render_table(transpose=True, total=True) }}

<h3>Chart</h3>
{{ chart.render() }}
{% endblock section %}
7 changes: 7 additions & 0 deletions pygal/ghost.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from pygal.graph import CHARTS_NAMES
from pygal.config import Config, CONFIG_ITEMS
from pygal.util import prepare_values
from pygal.table import Table
from uuid import uuid4


Expand Down Expand Up @@ -113,6 +114,12 @@ def render(self, is_unicode=False, **kwargs):
def render_tree(self):
return self.make_instance().render_tree()

def render_table(self, **kwargs):
real_cls, self.cls = self.cls, Table
rv = self.make_instance().render(**kwargs)
self.cls = real_cls
return rv

def render_pyquery(self):
"""Render the graph, and return a pyquery wrapped tree"""
from pyquery import PyQuery as pq
Expand Down
110 changes: 110 additions & 0 deletions pygal/table.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# -*- coding: utf-8 -*-
# This file is part of pygal
#
# A python svg graph plotting library
# Copyright © 2012-2014 Kozea
#
# This library is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Table maker
"""

from pygal.graph.base import BaseGraph
from lxml.html import builder, tostring


class HTML(object):
def __getattribute__(self, attr):
return getattr(builder, attr.upper())


class Table(BaseGraph):
_dual = None

def __init__(self, config, series, secondary_series, uuid, xml_filters):
"Init the table"
self.uuid = uuid
self.series = series or []
self.secondary_series = secondary_series or []
self.xml_filters = xml_filters or []
self.__dict__.update(config.to_dict())
self.config = config

def render(self, total=False, transpose=False):
html = HTML()
table = []

_ = lambda x: x if x is not None else ''

table.append([None])
labels = []
if self.x_labels:
labels += self.x_labels
if len(labels) < self._len:
labels += [None] * (self._len - len(labels))
if len(labels) > self._len:
labels = labels[:self._len]

for label in labels:
table[0].append(label)

if total:
table[0].append('Total')
acc = [0] * (self._len + 1)

for i, serie in enumerate(self.series):
row = [serie.title]
if total:
sum_ = 0
for j, value in enumerate(serie.values):
if total:
acc[j] += value
sum_ += value
row.append(self._format(value))
if total:
acc[-1] += sum_
row.append(self._format(sum_))
table.append(row)

width = self._len + 1
if total:
width += 1
table.append(['Total'])
for val in acc:
table[-1].append(self._format(val))

# Align values
len_ = max([len(r) for r in table] or [0])

for i, row in enumerate(table[:]):
len_ = len(row)
if len_ < width:
table[i] = row + [None] * (width - len_)

if not transpose:
table = list(zip(*table))

table = tostring(
html.table(
html.tbody(
*[html.tr(
*[html.td(_(col)) for col in r]
) for r in table]
)
)
)
if self.disable_xml_declaration:
table = table.decode('utf-8')
return table
Loading

0 comments on commit 30a1660

Please sign in to comment.