mirror of https://github.com/Kozea/pygal.git
Python to generate nice looking SVG graph
http://pygal.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
318 lines
12 KiB
318 lines
12 KiB
# -*- coding: utf-8 -*- |
|
# This file is part of pygal |
|
# |
|
# A python svg graph plotting library |
|
# Copyright © 2012-2015 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/>. |
|
from flask import Flask, render_template, Response, request |
|
import pygal |
|
from pygal.config import Config |
|
from pygal.util import cut |
|
from pygal.etree import etree |
|
from pygal.style import styles, parametric_styles |
|
from base64 import ( |
|
urlsafe_b64encode as b64encode, |
|
urlsafe_b64decode as b64decode) |
|
import string |
|
import random |
|
import pickle |
|
|
|
|
|
def get(type): |
|
from importlib import import_module |
|
module = '.'.join(type.split('.')[:-1]) |
|
name = type.split('.')[-1] |
|
return getattr(import_module(module), name) |
|
|
|
|
|
def random_label(): |
|
chars = string.ascii_letters + string.digits + u' àéèçêâäëï' |
|
return ''.join( |
|
[random.choice(chars) |
|
for i in range(random.randrange(4, 30))]) |
|
|
|
|
|
def random_value(min=0, max=15): |
|
return random.randrange(min, max, 1) |
|
|
|
|
|
def create_app(): |
|
"""Creates the pygal test web app""" |
|
|
|
app = Flask(__name__) |
|
|
|
@app.before_request |
|
def before_request(): |
|
if request.args.get('etree'): |
|
etree.to_etree() |
|
elif request.args.get('lxml'): |
|
etree.to_lxml() |
|
|
|
def _random(data, order): |
|
max = 10 ** order |
|
min = 10 ** random.randrange(0, order) |
|
|
|
series = [] |
|
for i in range(random.randrange(1, 10)): |
|
values = [( |
|
random_value((-max, min)[random.randrange(0, 2)], max), |
|
random_value((-max, min)[random.randrange(0, 2)], max) |
|
) for i in range(data)] |
|
series.append((random_label(), values)) |
|
return series |
|
|
|
def _random_series(type, data, order): |
|
max = 10 ** order |
|
min = 10 ** random.randrange(0, order) |
|
with_2nd = bool(random.randint(0, 1)) |
|
series = [] |
|
for i in range(random.randrange(1, 10)): |
|
if type == 'Pie': |
|
values = random_value(min, max) |
|
elif type == 'XY': |
|
values = [( |
|
random_value((-max, min)[random.randrange(0, 2)], max), |
|
random_value((-max, min)[random.randrange(0, 2)], max)) |
|
for i in range(data)] |
|
else: |
|
values = [random_value((-max, min)[random.randrange(1, 2)], |
|
max) for i in range(data)] |
|
is_2nd = False |
|
if with_2nd: |
|
is_2nd = bool(random.randint(0, 1)) |
|
series.append((random_label(), values, is_2nd)) |
|
return series |
|
|
|
from .tests import get_test_routes |
|
links = get_test_routes(app) |
|
|
|
@app.route("/") |
|
def index(): |
|
return render_template( |
|
'index.jinja2', styles=styles, parametric_styles=parametric_styles, |
|
parametric_colors=( |
|
'#ff5995', '#b6e354', '#feed6c', '#8cedff', '#9e6ffe'), |
|
links=links, charts_name=pygal.CHARTS_NAMES) |
|
|
|
@app.route("/svg/<type>/<series>/<config>") |
|
def svg(type, series, config): |
|
graph = get(type)( |
|
pickle.loads(b64decode(str(config)))) |
|
for title, values, is_2nd in pickle.loads(b64decode(str(series))): |
|
graph.add(title, values, secondary=is_2nd) |
|
return graph.render_response() |
|
|
|
@app.route("/table/<type>/<series>/<config>") |
|
def table(type, series, config): |
|
graph = get(type)(pickle.loads(b64decode(str(config)))) |
|
for title, values, is_2nd in pickle.loads(b64decode(str(series))): |
|
graph.add(title, values, secondary=is_2nd) |
|
return graph.render_table() |
|
|
|
@app.route("/sparkline/<style>") |
|
@app.route("/sparkline/parameric/<style>/<color>") |
|
def sparkline(style, color=None): |
|
if color is None: |
|
style = styles[style] |
|
else: |
|
style = parametric_styles[style](color) |
|
|
|
line = pygal.Line(style=style, pretty_print=True) |
|
line.add('_', [random.randrange(0, 10) for _ in range(25)]) |
|
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>") |
|
@app.route("/all/<style>/<color>/<base_style>") |
|
@app.route("/all/interpolate=<interpolate>") |
|
def all(style='default', color=None, interpolate=None, base_style=None): |
|
width, height = 600, 400 |
|
data = random.randrange(1, 10) |
|
order = random.randrange(1, 10) |
|
if color is None: |
|
style = styles[style] |
|
else: |
|
style = parametric_styles[style]( |
|
color, base_style=styles[base_style or 'default']) |
|
|
|
xy_series = _random(data, order) |
|
other_series = [] |
|
for title, values in xy_series: |
|
other_series.append( |
|
(title, cut(values, 1))) |
|
xy_series = b64encode(pickle.dumps(xy_series)) |
|
other_series = b64encode(pickle.dumps(other_series)) |
|
config = Config() |
|
config.width = width |
|
config.height = height |
|
config.fill = bool(random.randrange(0, 2)) |
|
config.human_readable = True |
|
config.interpolate = interpolate |
|
config.style = style |
|
svgs = [] |
|
for chart in pygal.CHARTS: |
|
type = '.'.join((chart.__module__, chart.__name__)) |
|
if chart._dual: |
|
config.x_labels = None |
|
else: |
|
config.x_labels = [random_label() for i in range(data)] |
|
svgs.append({'type': type, |
|
'series': xy_series if chart._dual else other_series, |
|
'config': b64encode(pickle.dumps(config))}) |
|
|
|
return render_template('svgs.jinja2', |
|
svgs=svgs, |
|
width=width, |
|
height=height) |
|
|
|
@app.route("/rotation") |
|
def rotation(): |
|
width, height = 375, 245 |
|
config = Config() |
|
config.width = width |
|
config.height = height |
|
config.fill = True |
|
config.style = styles['neon'] |
|
data = random.randrange(1, 10) |
|
order = random.randrange(1, 10) |
|
series = b64encode(pickle.dumps(_random_series(type, data, order))) |
|
labels = [random_label() for i in range(data)] |
|
svgs = [] |
|
config.show_legend = bool(random.randrange(0, 2)) |
|
for angle in range(0, 370, 10): |
|
config.title = "%d rotation" % angle |
|
config.x_labels = labels |
|
config.x_label_rotation = angle |
|
config.y_label_rotation = angle |
|
svgs.append({'type': 'pygal.Bar', |
|
'series': series, |
|
'config': b64encode(pickle.dumps(config))}) |
|
|
|
return render_template('svgs.jinja2', |
|
svgs=svgs, |
|
width=width, |
|
height=height) |
|
|
|
@app.route("/interpolation") |
|
def interpolation(): |
|
width, height = 600, 400 |
|
config = Config() |
|
config.width = width |
|
config.height = height |
|
config.fill = True |
|
config.style = styles['neon'] |
|
data = random.randrange(1, 10) |
|
order = random.randrange(1, 10) |
|
series = b64encode(pickle.dumps(_random_series(type, data, order))) |
|
svgs = [] |
|
for interpolation in 'quadratic', 'cubic', 'lagrange', 'trigonometric': |
|
config.title = "%s interpolation" % interpolation |
|
config.interpolate = interpolation |
|
svgs.append({'type': 'pygal.StackedLine', |
|
'series': series, |
|
'config': b64encode(pickle.dumps(config))}) |
|
|
|
for params in [ |
|
{'type': 'catmull_rom'}, |
|
{'type': 'finite_difference'}, |
|
{'type': 'cardinal', 'c': .25}, |
|
{'type': 'cardinal', 'c': .5}, |
|
{'type': 'cardinal', 'c': .75}, |
|
{'type': 'cardinal', 'c': 1.5}, |
|
{'type': 'cardinal', 'c': 2}, |
|
{'type': 'cardinal', 'c': 5}, |
|
{'type': 'kochanek_bartels', 'b': 1, 'c': 1, 't': 1}, |
|
{'type': 'kochanek_bartels', 'b': -1, 'c': 1, 't': 1}, |
|
{'type': 'kochanek_bartels', 'b': 1, 'c': -1, 't': 1}, |
|
{'type': 'kochanek_bartels', 'b': 1, 'c': 1, 't': -1}, |
|
{'type': 'kochanek_bartels', 'b': -1, 'c': 1, 't': -1}, |
|
{'type': 'kochanek_bartels', 'b': -1, 'c': -1, 't': 1}, |
|
{'type': 'kochanek_bartels', 'b': -1, 'c': -1, 't': -1} |
|
]: |
|
config.title = "Hermite interpolation with params %r" % params |
|
config.interpolate = 'hermite' |
|
config.interpolation_parameters = params |
|
svgs.append({'type': 'pygal.StackedLine', |
|
'series': series, |
|
'config': b64encode(pickle.dumps(config))}) |
|
|
|
return render_template('svgs.jinja2', |
|
svgs=svgs, |
|
width=width, |
|
height=height) |
|
|
|
@app.route("/raw_svgs/") |
|
def raw_svgs(): |
|
svgs = [] |
|
for color in styles['neon'].colors: |
|
chart = pygal.Pie(style=parametric_styles['rotate'](color), |
|
width=400, height=300) |
|
chart.title = color |
|
chart.disable_xml_declaration = True |
|
chart.explicit_size = True |
|
chart.js = ['http://l:2343/2.0.x/pygal-tooltips.js'] |
|
for i in range(6): |
|
chart.add(str(i), 2 ** i) |
|
svgs.append(chart.render()) |
|
return render_template('raw_svgs.jinja2', svgs=svgs) |
|
|
|
return app
|
|
|