|
|
|
# -*- 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 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()
|
|
|
|
else:
|
|
|
|
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)
|
|
|
|
|
|
|
|
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)]
|
|
|
|
series.append((random_label(), values))
|
|
|
|
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 = getattr(pygal, type)(pickle.loads(b64decode(str(config))))
|
|
|
|
for title, values in pickle.loads(b64decode(str(series))):
|
|
|
|
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):
|
|
|
|
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
|
|
|
|
config.x_labels = [random_label() for i in range(data)]
|
|
|
|
svgs = []
|
|
|
|
for chart in pygal.CHARTS:
|
|
|
|
type = chart.__name__
|
|
|
|
svgs.append({'type': type,
|
|
|
|
'series': xy_series if type == 'XY' 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, 91, 5):
|
|
|
|
config.title = "%d rotation" % angle
|
|
|
|
config.x_labels = labels
|
|
|
|
config.x_label_rotation = angle
|
|
|
|
svgs.append({'type': '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': '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': '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['RotateStyle'](color),
|
|
|
|
width=400, height=300)
|
|
|
|
chart.title = color
|
|
|
|
chart.disable_xml_declaration = True
|
|
|
|
chart.explicit_size = True
|
|
|
|
chart.js = [
|
|
|
|
'http://l:2343/svg.jquery.js',
|
|
|
|
'http://l:2343/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
|