Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added printable percent values for a series. #335

Open
wants to merge 45 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
1cadf77
Added the clockwise gauge changes from niosega
ConnorMooreLUC Jul 14, 2016
1eda900
Merge pull request #1 from ConnorMooreLUC/master
Jul 15, 2016
c8311ee
Merge pull request #1 from ConnorMooreLUC/master
psheu Jul 19, 2016
f081a82
Revert "Added the clockwise gauge changes from niosega"
psheu Jul 19, 2016
f9383a6
Merge pull request #2 from psheu/revert-1-master
psheu Jul 19, 2016
7e02da5
Revert "Added the clockwise gauge changes from niosega"
Jul 19, 2016
163e7c0
Merge pull request #2 from feargswalsh92/revert-1-master
Jul 19, 2016
760d53e
Added tests to see if the clockwise graph plots
Jul 19, 2016
78e7989
Revert "Revert "Added the clockwise gauge changes from niosega""
psheu Jul 19, 2016
8798b08
Merge pull request #3 from psheu/revert-2-revert-1-master
psheu Jul 19, 2016
f831811
Fixed the clockwise plotting, and added tests.
ConnorMooreLUC Jul 19, 2016
4354a1c
Merge branch 'master' of https://github.com/psheu/pygal
psheu Jul 19, 2016
f759e76
Merge remote-tracking branch 'origin/master'
Jul 19, 2016
38f1f99
Exception too broad warning fixed
psheu Jul 19, 2016
00cae97
Added a test to check clockwise graphs renders
Jul 20, 2016
6737195
Merge pull request #2 from psheu/master
ConnorMooreLUC Jul 20, 2016
2d1fddc
Merge pull request #3 from feargswalsh92/master
ConnorMooreLUC Jul 20, 2016
766ec10
Added description to a gauge test, and changed clockwise variable in …
ConnorMooreLUC Jul 20, 2016
a7944a7
Preliminary work for bar values.
ConnorMooreLUC Jul 20, 2016
6fa975b
Added test that checks if bar_values has been instantiated properly,
Jul 21, 2016
dcd6279
Just familiarising myself with tests really,but this just proves that…
Jul 21, 2016
c20e1f2
Added some half pseudocode/half real code to the bar_values method in…
Jul 21, 2016
47aad7a
Added percentages for each series to graph and bar
ConnorMooreLUC Jul 24, 2016
dec511a
ease enter the commit message for your changes. Lines starting
Jul 25, 2016
5f2d5d5
Merge remote-tracking branch 'upstream/master'
Jul 25, 2016
3347dac
I added the percent values as a *kwargs argument, and added
ConnorMooreLUC Jul 26, 2016
fe89647
Merge remote-tracking branch 'upstream/master'
Jul 26, 2016
9514357
Fixed the printing a little bit.
ConnorMooreLUC Jul 26, 2016
8c162e6
Tests added
Jul 26, 2016
00d7bbb
Merge pull request #1 from ConnorMooreLUC/master
psheu Jul 26, 2016
beb8121
Test that needs to pass in order for the graph to be formatted correc…
Jul 27, 2016
85da39e
Ratio bar value font size
psheu Jul 27, 2016
5b45dcc
Ratio bar value font size
psheu Jul 27, 2016
14b2f2b
Reconfigure print values loop in graph
psheu Jul 27, 2016
5f009ce
Commit
Jul 27, 2016
c4fad3d
merge
Jul 27, 2016
5265c95
Render test added
Jul 27, 2016
af22b32
Merge pull request #2 from feargswalsh92/master
psheu Jul 27, 2016
c159326
Reconfigure print values loop in graph
psheu Jul 27, 2016
2deef7a
Merge remote-tracking branch 'origin/master'
psheu Jul 27, 2016
9b33603
Merge pull request #7 from psheu/master
ConnorMooreLUC Jul 27, 2016
e40c581
Removed Gauge functionality.
ConnorMooreLUC Jul 27, 2016
3a87fc1
cleaning up again.
ConnorMooreLUC Jul 28, 2016
9295272
removed .idea
ConnorMooreLUC Jul 28, 2016
67a81a9
fixed testing problems
ConnorMooreLUC Jul 28, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion pygal/config.py
Expand Up @@ -256,7 +256,6 @@ class Config(CommonConfig):
list, "Misc", "Extraneous defs to be inserted in svg",
"Useful for adding gradients / patterns…",
str)

# Look #
title = Key(
None, str, "Look",
Expand Down Expand Up @@ -498,6 +497,11 @@ class Config(CommonConfig):
"Label string length truncation threshold",
"None = auto, Negative for none")

percent_values = Key(
False, bool, "Look",
"Bar Values",
"Set to True to display a bar's percentage contribution per series atop the bar.")

# Misc #
js = Key(
('//kozea.github.io/pygal.js/2.0.x/pygal-tooltips.min.js',),
Expand Down
12 changes: 7 additions & 5 deletions pygal/graph/bar.py
Expand Up @@ -62,7 +62,8 @@ def _bar(self, serie, parent, x, y, i, zero, secondary=False):

def _tooltip_and_print_values(
self, serie_node, serie, parent, i, val, metadata,
x, y, width, height):
x, y, width, height, total):
percent = ((float(val)/total)*100)
transpose = swap if self.horizontal else ident
x_center, y_center = transpose((x + width / 2, y + height / 2))
x_top, y_top = transpose((x + width, y + height))
Expand All @@ -75,7 +76,6 @@ def _tooltip_and_print_values(
self._tooltip_data(
parent, val, x_center, y_center, "centered",
self._get_x_label(i))

if self.print_values_position == 'top':
if self.horizontal:
x = x_bottom + sign * self.style.value_font_size / 2
Expand All @@ -93,7 +93,9 @@ def _tooltip_and_print_values(
else:
x = x_center
y = y_center
self._static_value(serie_node, val, x, y, metadata, "middle")

self.style.value_font_size = width / 1.41421
self._static_value(serie_node, val, percent, x, y, metadata, "middle")

def bar(self, serie, rescale=False):
"""Draw a bar graph for a serie"""
Expand All @@ -103,7 +105,7 @@ def bar(self, serie, rescale=False):
points = self._rescale(serie.points)
else:
points = serie.points

total = sum(list(filter(None, serie.values)))
for i, (x, y) in enumerate(points):
if None in (x, y) or (self.logarithmic and y <= 0):
continue
Expand All @@ -124,7 +126,7 @@ def bar(self, serie, rescale=False):

self._tooltip_and_print_values(
serie_node, serie, bar, i, val, metadata,
x_, y_, width, height)
x_, y_, width, height, total)

def _compute(self):
"""Compute y min and max and y scale and set labels"""
Expand Down
32 changes: 20 additions & 12 deletions pygal/graph/graph.py
Expand Up @@ -491,12 +491,12 @@ def _tooltip_data(self, node, value, x, y, classes=None, xlabel=None):
self.svg.node(node, 'desc',
class_="x_label").text = to_str(xlabel)

def _static_value(self, serie_node, value, x, y, metadata,
def _static_value(self, serie_node, value, percent, x, y, metadata,
align_text='left', classes=None):
"""Write the print value"""
label = metadata and metadata.get('label')
classes = classes and [classes] or []

per_str = ('{0:.2f}%'.format(percent))
if self.print_labels and label:
label_cls = classes + ['label']
if self.print_values:
Expand All @@ -509,18 +509,26 @@ def _static_value(self, serie_node, value, x, y, metadata,
).text = label
y += self.style.value_font_size

if self.print_values or self.dynamic_print_values:
if self.print_values or self.dynamic_print_values or self.percent_values:
val_cls = classes + ['value']
if self.dynamic_print_values:
val_cls.append('showable')

self.svg.node(
serie_node['text_overlay'], 'text',
class_=' '.join(val_cls),
x=x,
y=y + self.style.value_font_size / 3,
attrib={'text-anchor': align_text}
).text = value if self.print_zeroes or value != '0' else ''
if self.print_values:
self.svg.node(
serie_node['text_overlay'], 'text',
class_=' '.join(val_cls),
x=x,
y=y + self.style.value_font_size / 3,
attrib={'text-anchor': align_text}
).text = value if self.print_zeroes or value != '0' else ''
if self.percent_values:
self.svg.node(
serie_node['text_overlay'], 'text',
class_=' '.join(val_cls),
x=x,
y=y + 4/3*self.style.value_font_size,
attrib={'text-anchor': align_text}
).text = per_str if self.print_zeroes or per_str != '0.00%' else ''

def _points(self, x_pos):
"""
Expand Down Expand Up @@ -836,7 +844,7 @@ def _compute_x_labels_major(self):
elif self.x_labels_major_count:
label_count = len(self._x_labels)
major_count = self.x_labels_major_count
if (major_count >= label_count):
if major_count >= label_count:
self._x_labels_major = [label[0] for label in self._x_labels]

else:
Expand Down
2 changes: 1 addition & 1 deletion pygal/test/conftest.py
Expand Up @@ -23,7 +23,7 @@
import pygal
from pygal.etree import etree
import sys
from . import get_data
#from . import get_data


@pytest.fixture
Expand Down
46 changes: 45 additions & 1 deletion pygal/test/test_bar.py
Expand Up @@ -18,7 +18,6 @@
# along with pygal. If not, see <http://www.gnu.org/licenses/>.

"""Bar chart related tests"""

from pygal import Bar


Expand All @@ -35,3 +34,48 @@ def test_simple_bar():
assert len(q(".axis.y")) == 1
assert len(q(".legend")) == 2
assert len(q(".plot .series rect")) == 2 * 3


def test_difference():
"""Tests the difference between labeled graphs and unlabeled graphs"""
bar = Bar(bar_values=False)
rng = [-3, -32, -39]
bar.add('test1', rng)
bar.add('test2', map(abs, rng))
bar.x_labels = map(str, rng)
bar_labelled = Bar(bar_values=True)
rng = [-3, -32, -39]
bar_labelled.add('test1', rng)
bar_labelled.add('test2', map(abs, rng))
bar.labelled = map(str, rng)

assert bar != bar_labelled


def test_bar_percent_difference():
"""Tests the difference between percent labeled graphs and unlabeled graphs"""
bar = Bar()
rng = [-3, -32, -39]
bar.add('test1', rng)
bar.add('test2', map(abs, rng))
bar.x_labels = map(str, rng)

barpercent = Bar(percent_values=True)
rng = [-3, -32, -39]
barpercent.add('test1', rng)
barpercent.add('test2', map(abs, rng))
barpercent.x_labels = map(str, rng)

assert (bar != barpercent)


def test_chart_renders():
"""Tests that print values and percent values renders"""
line_chart = Bar(print_values=True, percent_values=True, print_values_position='top')
line_chart.title = 'Browser usage evolution (in %)'
line_chart.x_labels = map(str, range(2002, 2013))
line_chart.add('Firefox', [None, None, 0, 16.6, 25, 31, 36.4, 45.5, 46.3, 42.8, 37.1])
line_chart.add('Chrome', [None, None, None, None, None, None, 0, 3.9, 10.8, 23.8, 35.3])
line_chart.add('IE', [85.8, 84.6, 84.7, 74.5, 66, 58.6, 54.7, 44.8, 36.2, 26.6, 20.1])
line_chart.add('Others', [14.2, 15.4, 15.3, 8.9, 9, 10.4, 8.9, 5.8, 6.7, 6.8, 7.5])
assert line_chart.render()
14 changes: 11 additions & 3 deletions pygal/test/test_config.py
Expand Up @@ -102,6 +102,7 @@ class LineConfig(Config):

def test_config_alterations_class():
"""Assert a config can be changed on config class"""

class LineConfig(Config):
no_prefix = True
show_legend = False
Expand All @@ -125,6 +126,7 @@ class LineConfig(Config):

def test_config_alterations_instance():
"""Assert a config can be changed on instance"""

class LineConfig(Config):
no_prefix = True
show_legend = False
Expand All @@ -149,6 +151,7 @@ class LineConfig(Config):

def test_config_alterations_kwargs():
"""Assert a config can be changed with keyword args"""

class LineConfig(Config):
no_prefix = True
show_legend = False
Expand Down Expand Up @@ -541,15 +544,15 @@ def test_formatters(Chart):
if Chart._dual or Chart == Box:
return
chart = Chart(formatter=lambda x, chart, serie: '%s%s$' % (
x, serie.title))
x, serie.title))
chart.add('_a', [1, 2, {'value': 3, 'formatter': lambda x: u('%s¥') % x}])
chart.add('_b', [4, 5, 6], formatter=lambda x: u('%s€') % x)
chart.x_labels = [2, 4, 6]
chart.x_labels_major = [4]
q = chart.render_pyquery()
assert set([v.text for v in q(".value")]) == set((
u('4€'), u('5€'), u('6€'), '1_a$', '2_a$', u('3¥')) + (
('6_a$', u('15€')) if Chart in (Pie, SolidGauge) else ()))
u('4€'), u('5€'), u('6€'), '1_a$', '2_a$', u('3¥')) + (
('6_a$', u('15€')) if Chart in (Pie, SolidGauge) else ()))


def test_classes(Chart):
Expand All @@ -574,3 +577,8 @@ def test_classes(Chart):

chart = Chart(classes=('graph', _ellipsis))
assert chart.render_pyquery().attr('class') == 'graph pygal-chart'


def test_bar_value(Chart):
chart = Chart()
assert chart.bar_values == False
33 changes: 33 additions & 0 deletions pygal/test/test_gauge.py
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# This file is part of pygal
#
# A python svg graph plotting library
# Copyright © 2012-2016 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/>.

"""Gauge Chart related tests."""


from pygal import Gauge


def test_render():
"""Tests that a gauge plots"""
chart = Gauge()
chart.range = [0,40]
chart.add('Connor', 21)
chart.add('Mike', 30)
chart.add('Jules', 10)
assert chart.render()
13 changes: 9 additions & 4 deletions pygal/test/test_graph.py
Expand Up @@ -20,6 +20,10 @@
"""Generate tests for different chart types with different data"""

import os
from unittest import TestCase

from pygal.graph.bar import Bar

import pygal
import uuid
import sys
Expand Down Expand Up @@ -431,10 +435,11 @@ def test_long_title(Chart, datas):
"""Test chart rendering with a long title"""
chart = Chart(
title="A chart is a graphical representation of data, in which "
"'the data is represented by symbols, such as bars in a bar chart, "
"lines in a line chart, or slices in a pie chart'. A chart can "
"represent tabular numeric data, functions or some kinds of "
"qualitative structure and provides different info.")
"'the data is represented by symbols, such as bars in a bar chart, "
"lines in a line chart, or slices in a pie chart'. A chart can "
"represent tabular numeric data, functions or some kinds of "
"qualitative structure and provides different info.")
chart = make_data(chart, datas)
q = chart.render_pyquery()
assert len(q('.titles text')) == 5

2 changes: 1 addition & 1 deletion pygal/view.py
Expand Up @@ -26,7 +26,7 @@

class Margin(object):

"""Class reprensenting a margin (top, right, left, bottom)"""
"""Class representing a margin (top, right, left, bottom)"""

def __init__(self, top, right, bottom, left):
"""Create the margin object from the top, right, left, bottom margin"""
Expand Down