Browse Source

Fixed failing test case in Pie.add_data. Tests now pass on Python 3

pull/8/head
Jason R. Coombs 13 years ago
parent
commit
46a8dc70ad
  1. 80
      svg/charts/pie.py

80
svg/charts/pie.py

@ -1,5 +1,5 @@
import math import math
from operator import add import itertools
from lxml import etree from lxml import etree
from svg.charts.graph import Graph from svg.charts.graph import Graph
@ -14,15 +14,15 @@ RADIANS = math.pi/180
class Pie(Graph): class Pie(Graph):
""" """
A presentation-quality SVG pie graph A presentation-quality SVG pie graph
Synopsis Synopsis
======== ========
from svg.charts.pie import Pie from svg.charts.pie import Pie
fields = ['Jan', 'Feb', 'Mar'] fields = ['Jan', 'Feb', 'Mar']
data_sales_02 = [12, 45, 21] data_sales_02 = [12, 45, 21]
graph = Pie(dict( graph = Pie(dict(
height = 500, height = 500,
width = 300, width = 300,
@ -30,7 +30,7 @@ class Pie(Graph):
graph.add_data({'data': data_sales_02, 'title': 'Sales 2002'}) graph.add_data({'data': data_sales_02, 'title': 'Sales 2002'})
print "Content-type" image/svg+xml\r\n\r\n' print "Content-type" image/svg+xml\r\n\r\n'
print graph.burn() print graph.burn()
Description Description
=========== ===========
This object aims to allow you to easily create high quality This object aims to allow you to easily create high quality
@ -44,14 +44,14 @@ class Pie(Graph):
"if true, displays a drop shadow for the chart" "if true, displays a drop shadow for the chart"
show_shadow = True show_shadow = True
"Sets the offset of the shadow from the pie chart" "Sets the offset of the shadow from the pie chart"
shadow_offset = 10 shadow_offset = 10
show_data_labels = False show_data_labels = False
"If true, display the actual field values in the data labels" "If true, display the actual field values in the data labels"
show_actual_values = False show_actual_values = False
"If true, display the percentage value of each pie wedge in the data labels" "If true, display the percentage value of each pie wedge in the data labels"
show_percent = True show_percent = True
"If true, display the labels in the key" "If true, display the labels in the key"
show_key_data_labels = True show_key_data_labels = True
"If true, display the actual value of the field in the key" "If true, display the actual value of the field in the key"
@ -59,13 +59,13 @@ class Pie(Graph):
"If true, display the percentage value of the wedges in the key" "If true, display the percentage value of the wedges in the key"
show_key_percent = False show_key_percent = False
"If true, explode the pie (put space between the wedges)" "If true, explode the pie (put space between the wedges)"
expanded = False expanded = False
"If true, expand the largest pie wedge" "If true, expand the largest pie wedge"
expand_greatest = False expand_greatest = False
"The amount of space between expanded wedges" "The amount of space between expanded wedges"
expand_gap = 10 expand_gap = 10
show_x_labels = False show_x_labels = False
show_y_labels = False show_y_labels = False
@ -77,22 +77,38 @@ class Pie(Graph):
def add_data(self, data_descriptor): def add_data(self, data_descriptor):
""" """
Add a data set to the graph Add a data set to the graph
>>> graph.add_data({data:[1,2,3,4]}) # doctest: +SKIP >>> graph.add_data({data:[1,2,3,4]}) # doctest: +SKIP
Note that a 'title' key is ignored. Note that a 'title' key is ignored.
Multiple calls to add_data will sum the elements, and the pie will Multiple calls to add_data will sum the elements, and the pie will
display the aggregated data. e.g. display the aggregated data. e.g.
>>> graph.add_data({data:[1,2,3,4]}) # doctest: +SKIP >>> graph.add_data({data:[1,2,3,4]}) # doctest: +SKIP
>>> graph.add_data({data:[2,3,5,7]}) # doctest: +SKIP >>> graph.add_data({data:[2,3,5,7]}) # doctest: +SKIP
is the same as: is the same as:
graph.add_data({data:[3,5,8,11]}) # doctest: +SKIP >>> graph.add_data({data:[3,5,8,11]}) # doctest: +SKIP
If data is added of with differing lengths, the corresponding
values will be assumed to be zero.
>>> graph.add_data({data:[1,2,3,4]}) # doctest: +SKIP
>>> graph.add_data({data:[5,7]}) # doctest: +SKIP
is the same as:
>>> graph.add_data({data:[5,7]}) # doctest: +SKIP
>>> graph.add_data({data:[1,2,3,4]}) # doctest: +SKIP
and
>>> graph.add_data({data:[6,9,3,4]}) # doctest: +SKIP
""" """
self.data = map(robust_add, self.data, data_descriptor['data']) pairs = itertools.izip_longest(self.data, data_descriptor['data'])
self.data = list(itertools.starmap(robust_add, pairs))
def add_defs(self, defs): def add_defs(self, defs):
"Add svg definitions" "Add svg definitions"
@ -123,7 +139,7 @@ class Pie(Graph):
return [''] return ['']
def keys(self): def keys(self):
total = reduce(add, self.data) total = sum(self.data)
percent_scale = 100.0 / total percent_scale = 100.0 / total
def key(field, value): def key(field, value):
result = [field] result = [field]
@ -133,13 +149,13 @@ class Pie(Graph):
result.append(percent) result.append(percent)
return ' '.join(result) return ' '.join(result)
return map(key, self.fields, self.data) return map(key, self.fields, self.data)
def draw_data(self): def draw_data(self):
self.graph = etree.SubElement(self.root, 'g') self.graph = etree.SubElement(self.root, 'g')
background = etree.SubElement(self.graph, 'g') background = etree.SubElement(self.graph, 'g')
# midground is somewhere between the background and the foreground # midground is somewhere between the background and the foreground
midground = etree.SubElement(self.graph, 'g') midground = etree.SubElement(self.graph, 'g')
is_expanded = (self.expanded or self.expand_greatest) is_expanded = (self.expanded or self.expand_greatest)
diameter = min(self.graph_width, self.graph_height) diameter = min(self.graph_width, self.graph_height)
# the following assumes int(True)==1 and int(False)==0 # the following assumes int(True)==1 and int(False)==0
@ -157,7 +173,7 @@ class Pie(Graph):
wedge_text_pad = 5 wedge_text_pad = 5
wedge_text_pad = 20 * int(self.show_percent) * int(self.show_data_labels) wedge_text_pad = 20 * int(self.show_percent) * int(self.show_data_labels)
total = reduce(add, self.data) total = sum(self.data)
max_value = max(self.data) max_value = max(self.data)
percent_scale = 100.0 / total percent_scale = 100.0 / total
@ -166,7 +182,7 @@ class Pie(Graph):
rad_mult = 3.6 * RADIANS rad_mult = 3.6 * RADIANS
for index, (field, value) in enumerate(zip(self.fields, self.data)): for index, (field, value) in enumerate(zip(self.fields, self.data)):
percent = percent_scale * value percent = percent_scale * value
radians = prev_percent * rad_mult radians = prev_percent * rad_mult
x_start = radius+(math.sin(radians) * radius) x_start = radius+(math.sin(radians) * radius)
y_start = radius-(math.cos(radians) * radius) y_start = radius-(math.cos(radians) * radius)
@ -182,7 +198,7 @@ class Pie(Graph):
"%(percent_greater_fifty)s,1,", "%(percent_greater_fifty)s,1,",
"%(x_end)s %(y_end)s Z")) "%(x_end)s %(y_end)s Z"))
path = path % vars() path = path % vars()
wedge = etree.SubElement( wedge = etree.SubElement(
self.foreground, self.foreground,
'path', 'path',
@ -191,13 +207,13 @@ class Pie(Graph):
'class': 'fill%s' % (index+1), 'class': 'fill%s' % (index+1),
} }
) )
translate = None translate = None
tx = 0 tx = 0
ty = 0 ty = 0
half_percent = prev_percent + percent / 2 half_percent = prev_percent + percent / 2
radians = half_percent * rad_mult radians = half_percent * rad_mult
if self.show_shadow: if self.show_shadow:
shadow = etree.SubElement( shadow = etree.SubElement(
background, background,
@ -215,20 +231,20 @@ class Pie(Graph):
# consider getting the style from the stylesheet # consider getting the style from the stylesheet
style="fill:#fff; stroke:none;", style="fill:#fff; stroke:none;",
) )
if self.expanded or (self.expand_greatest and value == max_value): if self.expanded or (self.expand_greatest and value == max_value):
tx = (math.sin(radians) * self.expand_gap) tx = (math.sin(radians) * self.expand_gap)
ty = -(math.cos(radians) * self.expand_gap) ty = -(math.cos(radians) * self.expand_gap)
translate = "translate(%(tx)s %(ty)s)" % vars() translate = "translate(%(tx)s %(ty)s)" % vars()
wedge.set('transform', translate) wedge.set('transform', translate)
clear.set('transform', translate) clear.set('transform', translate)
if self.show_shadow: if self.show_shadow:
shadow_tx = self.shadow_offset + tx shadow_tx = self.shadow_offset + tx
shadow_ty = self.shadow_offset + ty shadow_ty = self.shadow_offset + ty
translate = 'translate(%(shadow_tx)s %(shadow_ty)s)' % vars() translate = 'translate(%(shadow_tx)s %(shadow_ty)s)' % vars()
shadow.set('transform', translate) shadow.set('transform', translate)
if self.show_data_labels and value != 0: if self.show_data_labels and value != 0:
label = [] label = []
if self.show_key_data_labels: if self.show_key_data_labels:
@ -243,7 +259,7 @@ class Pie(Graph):
mcr = math.cos(radians) mcr = math.cos(radians)
tx = radius + (msr * radius) tx = radius + (msr * radius)
ty = radius -(mcr * radius) ty = radius -(mcr * radius)
if self.expanded or (self.expand_greatest and value == max_value): if self.expanded or (self.expand_greatest and value == max_value):
tx += (msr * self.expand_gap) tx += (msr * self.expand_gap)
ty -= (mcr * self.expand_gap) ty -= (mcr * self.expand_gap)
@ -270,7 +286,7 @@ class Pie(Graph):
} }
) )
label_node.text = label label_node.text = label
prev_percent += percent prev_percent += percent
def round(self, val, to): def round(self, val, to):

Loading…
Cancel
Save