[graphy commit] r71 - in trunk/graphy: . backends/google_chart_api

1 view
Skip to first unread message

codesite...@google.com

unread,
Jan 7, 2009, 9:00:54 PM1/7/09
to gra...@googlegroups.com
Author: bugmaster
Date: Wed Jan 7 17:25:47 2009
New Revision: 71

Modified:
trunk/graphy/backends/google_chart_api/encoders.py
trunk/graphy/backends/google_chart_api/pie_chart_test.py
trunk/graphy/pie_chart.py
trunk/graphy/pie_chart_test.py

Log:
*** WARNING ***
This change will very likely BREAK your code if you're using pie charts !
*** WARNING ***

Rewrote the pie chart code to support concentric pie charts and chart
angles in Google Chart API. Removed deprecated AddSegments method, and
changed the way the data is represented internally in order to bring it
more in line with other charts.

(also, fixed some spelling errors)

Modified: trunk/graphy/backends/google_chart_api/encoders.py
==============================================================================
--- trunk/graphy/backends/google_chart_api/encoders.py (original)
+++ trunk/graphy/backends/google_chart_api/encoders.py Wed Jan 7 17:25:47
2009
@@ -248,7 +248,7 @@

"""Helper class to encode BarChart objects into Google Chart URLs."""

- __STYLE_DEPREACTION = ('BarChart.display.style is deprecated.' +
+ __STYLE_DEPRECATION = ('BarChart.display.style is deprecated.' +
' Use BarChart.style, instead.')

def __init__(self, chart, style=None):
@@ -259,7 +259,7 @@
"""
super(BarChartEncoder, self).__init__(chart)
if style is not None:
- warnings.warn(self.__STYLE_DEPREACTION, DeprecationWarning,
stacklevel=2)
+ warnings.warn(self.__STYLE_DEPRECATION, DeprecationWarning,
stacklevel=2)
chart.style = style

def _GetType(self, chart):
@@ -339,14 +339,14 @@
return util.JoinLists(bar_size = spec)

def __GetStyle(self):
- warnings.warn(self.__STYLE_DEPREACTION, DeprecationWarning,
stacklevel=2)
+ warnings.warn(self.__STYLE_DEPRECATION, DeprecationWarning,
stacklevel=2)
return self.chart.style

def __SetStyle(self, value):
- warnings.warn(self.__STYLE_DEPREACTION, DeprecationWarning,
stacklevel=2)
+ warnings.warn(self.__STYLE_DEPRECATION, DeprecationWarning,
stacklevel=2)
self.chart.style = value

- style=property(__GetStyle, __SetStyle, __STYLE_DEPREACTION)
+ style = property(__GetStyle, __SetStyle, __STYLE_DEPRECATION)


class PieChartEncoder(BaseChartEncoder):
@@ -357,42 +357,74 @@
is3d: if True, draw a 3d pie chart. Default is False.
"""

- def __init__(self, chart, is3d=False):
+ def __init__(self, chart, is3d=False, angle=None):
"""Construct a new PieChartEncoder.

Args:
- is3d: if True, draw a 3d pie chart. Default is False.
+ is3d: If True, draw a 3d pie chart. Default is False. If the pie
chart
+ includes multiple pies, is3d must be set to False.
+ angle: Angle of rotation of the pie chart, in radians.
"""
super(PieChartEncoder, self).__init__(chart)
self.is3d = is3d
+ self.angle = None
+
+ def _GetFormatters(self):
+ """Add a formatter for the chart angle."""
+ formatters = super(PieChartEncoder, self)._GetFormatters()
+ formatters.append(self._GetAngleParams)
+ return formatters

def _GetType(self, chart):
- if self.is3d:
- return {'chart_type': 'p3'}
+ if len(chart.data) > 1:
+ if self.is3d:
+ warnings.warn('3d charts with more than one pie not supported',
+ RuntimeWarning, stacklevel=2)
+ chart_type = 'pc'
else:
- return {'chart_type': 'p'}
+ if self.is3d:
+ chart_type = 'p3'
+ else:
+ chart_type = 'p'
+ return {'chart_type': chart_type}

def _GetDataSeriesParams(self, chart):
"""Collect params related to the data series."""
- points = []
+
+ pie_points = []
labels = []
- for segment in chart.data:
- if segment:
- points.append(segment.size)
- labels.append(segment.label or '_')
+ max_val = 1
+ for pie in chart.data:
+ points = []
+ for segment in pie:
+ if segment:
+ points.append(segment.size)
+ max_val = max(max_val, segment.size)
+ labels.append(segment.label or '')
+ if points:
+ pie_points.append(points)

- if points:
- max_val = max(points)
- else:
- max_val = 1
encoder = self._GetDataEncoder(chart)
- result = util.EncodeData(chart, [points], 0, max_val, encoder)
+ result = util.EncodeData(chart, pie_points, 0, max_val, encoder)
result.update(util.JoinLists(label=labels))
return result

def _GetColors(self, chart):
- colors = []
- for segment in chart.data:
- if segment and segment.color:
- colors.append(segment.color)
+ if chart._colors:
+ # Colors were overridden by the user
+ colors = chart._colors
+ else:
+ # Build the list of colors from individual segments
+ colors = []
+ for pie in chart.data:
+ for segment in pie:
+ if segment and segment.color:
+ colors.append(segment.color)
return util.JoinLists(color = colors)
+
+ def _GetAngleParams(self, chart):
+ """If the user specified an angle, add it to the params."""
+ if self.angle:
+ return {'chp' : str(self.angle)}
+ else:
+ return {}

Modified: trunk/graphy/backends/google_chart_api/pie_chart_test.py
==============================================================================
--- trunk/graphy/backends/google_chart_api/pie_chart_test.py (original)
+++ trunk/graphy/backends/google_chart_api/pie_chart_test.py Wed Jan 7
17:25:47 2009
@@ -16,6 +16,8 @@

"""Unittest for Graphy and Google Chart API backend."""

+import warnings
+
from graphy import graphy_test
from graphy import pie_chart
from graphy.backends import google_chart_api
@@ -26,6 +28,10 @@
# Pie Charts should continue to satisfy
class PieChartTest(base_encoder_test.BaseChartTest):

+ def tearDown(self):
+ warnings.resetwarnings()
+ super(PieChartTest, self).tearDown()
+
def GetChart(self, *args, **kwargs):
return google_chart_api.PieChart(*args, **kwargs)

@@ -60,18 +66,36 @@
self.assertEqual(self.Param('chd'), 's:Pfu9')
self.assertEqual(self.Param('chl'), 'Mouse|Cat|Dog|Horse')

- def testAddMultipleSegments(self):
- self.chart.AddSegments([1,2,3],
- ['Mouse', 'Cat', 'Dog'],
- ['ff0000', '00ff00', '0000ff'])
+ def testMultiplePies(self):
+ self.chart.AddPie([1,2,3],
+ ['Mouse', 'Cat', 'Dog'],
+ ['ff0000', '00ff00', '0000ff'])
self.assertEqual(self.Param('chd'), 's:Up9')
self.assertEqual(self.Param('chl'), 'Mouse|Cat|Dog')
self.assertEqual(self.Param('chco'), 'ff0000,00ff00,0000ff')
+ self.assertEqual(self.Param('cht'), 'p')
# skip two colors
- self.chart.AddSegments([4,5,6], ['Horse', 'Moose', 'Elephant'],
['cccccc'])
- self.assertEqual(self.Param('chd'), 's:KUfpz9')
+ self.chart.AddPie([4,5,6], ['Horse', 'Moose', 'Elephant'], ['cccccc'])
+ self.assertEqual(self.Param('chd'), 's:KUf,pz9')
self.assertEqual(self.Param('chl'), 'Mouse|Cat|Dog|Horse|Moose|
Elephant')
self.assertEqual(self.Param('chco'), 'ff0000,00ff00,0000ff,cccccc')
+ self.assertEqual(self.Param('cht'), 'pc')
+
+ def testMultiplePiesNo3d(self):
+ chart = self.GetChart([1,2,3], ['Mouse', 'Cat', 'Dog'])
+ chart.AddPie([4,5,6], ['Horse', 'Moose', 'Elephant'])
+ chart.display.is3d = True
+ warnings.filterwarnings('error')
+ self.assertRaises(RuntimeWarning, chart.display.Url, 320, 240)
+
+ def testAddSegmentByIndex(self):
+ self.chart = self.GetChart([1,2,3], ['Mouse', 'Cat', 'Dog'])
+ self.chart.AddSegment(4, 'Horse', pie_index=0)
+ self.assertEqual(self.Param('chd'), 's:Pfu9')
+ self.assertEqual(self.Param('chl'), 'Mouse|Cat|Dog|Horse')
+ self.chart.AddPie([4,5], ['Apple', 'Orange'], [])
+ self.chart.AddSegment(6, 'Watermelon', pie_index=1)
+ self.assertEqual(self.Param('chd'), 's:KUfp,pz9')

def testSetColors(self):
self.assertEqual(self.Param('chco'), '')
@@ -84,7 +108,7 @@

def testHugeSegmentSizes(self):
self.chart = self.GetChart([1000000000000000L,3000000000000000L],
- ['Big', 'Uber'])
+ ['Big', 'Uber'])
self.assertEqual(self.Param('chd'), 's:U9')
self.chart.display.enhanced_encoding = True
self.assertEqual(self.Param('chd'), 'e:VV..')
@@ -97,6 +121,13 @@
self.assertEquals(segment1.size, 1)
self.assertEquals(segment2.size, 3)
self.assertEqual(self.Param('chd'), 's:U9')
+
+ def testChartAngle(self):
+ self.assertTrue('chp' not in self.chart.display._Params(self.chart))
+ self.chart.display.angle = 3.1415
+ self.assertEqual(self.Param('chp'), '3.1415')
+ self.chart.display.angle = 0
+ self.assertTrue('chp' not in self.chart.display._Params(self.chart))


if __name__ == '__main__':

Modified: trunk/graphy/pie_chart.py
==============================================================================
--- trunk/graphy/pie_chart.py (original)
+++ trunk/graphy/pie_chart.py Wed Jan 7 17:25:47 2009
@@ -63,13 +63,21 @@


class PieChart(common.BaseChart):
- """Represent a pie chart."""
+ """Represents a pie chart.
+
+ The pie chart consists of a single "pie" by default, but additional pies
+ may be added using the AddPie method. The Google Chart API will display
+ the pies as concentric circles, with pie #0 on the inside; other backends
+ may display the pies differently.
+ """

def __init__(self, points=None, labels=None, colors=None):
- """Constructor for PieChart objects
+ """Constructor for PieChart objects.
+
+ Creates a pie chart with a single pie.

Args:
- data_points: A list of data points for the pie chart;
+ points: A list of data points for the pie chart;
i.e., relative sizes of the pie segments
labels: A list of labels for the pie segments.
TODO: Allow the user to pass in None as one of
@@ -80,23 +88,44 @@
"""
super(PieChart, self).__init__()
self.formatters = []
+ self._colors = None
if points:
- # BUG: This crashes if you specify points but not labels
- self.AddSegments(points, labels, colors)
+ self.AddPie(points, labels, colors)
+
+ def AddPie(self, points, labels=None, colors=None):
+ """Add a whole pie to the chart.

- def AddSegments(self, points, labels, colors):
- """Add more segments to this pie chart."""
+ Args:
+ points: A list of pie segment sizes
+ labels: A list of labels for the pie segments
+ colors: A list of colors for the segments. Missing colors will be
chosen
+ automatically.
+ Return:
+ The index of the newly added pie.
+ """
num_colors = len(colors or [])
+ num_labels = len(labels or [])
+ pie_index = len(self.data)
+ self.data.append(list())
for i, pt in enumerate(points):
assert pt >= 0
- label = labels[i]
+ label = None
+ if i < num_labels:
+ label = labels[i]
color = None
if i < num_colors:
color = colors[i]
- self.AddSegment(pt, label=label, color=color)
+ self.AddSegment(pt, label=label, color=color, pie_index=pie_index)
+ return pie_index

- def AddSegment(self, size, label=None, color=None):
- """Add a pie segment to this chart, and return the segment."""
+ def AddSegment(self, size, label=None, color=None, pie_index=0):
+ """Add a pie segment to this chart, and return the segment.
+
+ size: The size of the segment
+ label: The label for the segment
+ color: The color of the segment, or None to automatically choose the
color
+ pie_index: The index of the pie that will receive the new segment.
+ """
if isinstance(size, Segment):
warnings.warn("AddSegment(segment) is deprecated. Use
AddSegment(size, "
"label, color) instead", DeprecationWarning,
stacklevel=2)
@@ -104,30 +133,18 @@
else:
segment = Segment(size, label=label, color=color)
assert segment.size >= 0
- self.data.append(segment)
+ if pie_index == 0 and not self.data:
+ # Create the default pie
+ self.data.append(list())
+ self.data[pie_index].append(segment)
return segment

- def AddSeries(self, points, color=None, style=None, markers=None,
label=None):
- """DEPRECATED
-
- Add a new segment to the chart and return it.
-
- The segment must contain exactly one data point; all parameters
- other than color and label are ignored.
- """
- warnings.warn('PieChart.AddSeries is deprecated. Call AddSegment or '
- 'AddSegments instead.', DeprecationWarning)
- return self.AddSegment(Segment(points[0], color=color, label=label))

def SetColors(self, *colors):
"""Change the colors of this chart to the specified list of colors.

- Missing colors will be interpolated by the server.
+ Note that this will completely override the individual colors specified
+ in the pie segments. Missing colors will be interpolated, so that the
+ list of colors covers all segments in all the pies.
"""
- num_colors = len(colors)
- assert num_colors <= len(self.data)
- for i,segment in enumerate(self.data):
- if i >= num_colors:
- segment.color = None
- else:
- segment.color = colors[i]
+ self._colors = colors

Modified: trunk/graphy/pie_chart_test.py
==============================================================================
--- trunk/graphy/pie_chart_test.py (original)
+++ trunk/graphy/pie_chart_test.py Wed Jan 7 17:25:47 2009
@@ -45,18 +45,6 @@
def tearDown(self):
warnings.resetwarnings()

- # TODO: remove once AddSeries is deleted
- def testAddSeries(self):
- warnings.filterwarnings('ignore')
- chart = pie_chart.PieChart()
- chart.AddSeries(points=[1], color='color',
- markers='markers', label='label')
- series = chart.data[0]
- self.assertEqual(series.data, [1])
- self.assertEqual(series.color, 'color')
- self.assertEqual(series.markers, [])
- self.assertEqual(series.label, 'label')
-
def testNegativeSegmentSizes(self):
self.assertRaises(AssertionError, pie_chart.PieChart,
[-5, 10], ['Negative', 'Positive'])
@@ -75,23 +63,34 @@

# New order
chart.AddSegment(1, 'label', '0000FF')
- self.assertEqual('label', chart.data[0].label)
- self.assertEqual('0000FF', chart.data[0].color)
+ self.assertEquals('label', chart.data[0][0].label)
+ self.assertEquals('0000FF', chart.data[0][0].color)

- # TODO: remove once the deprecation warning is removed
- def testAddSegmentsOrder(self):
+ def testAddPie(self):
chart = pie_chart.PieChart()
- # Deprecated approach
- warnings.filterwarnings('error')
- # This test conflicts with testAddSegmentOrder. It passes in
isolation. I
- # don't know what the deal is.
- #self.assertRaises(DeprecationWarning, chart.AddSegments, [1],
- # ['0000FF'], ['label'])
-
- # New order
- chart.AddSegments([1], ['label'], ['0000FF'])
- self.assertEqual('label', chart.data[0].label)
- self.assertEqual('0000FF', chart.data[0].color)
+ idx = chart.AddPie([1], ['A'], ['ff0000'])
+ self.assertEquals(idx, 0)
+ self.assertEquals(len(chart.data), 1)
+ self.assertEquals(len(chart.data[0]), 1)
+ self.assertEquals(chart.data[0][0].size, 1)
+ idx = chart.AddPie([2], ['B'], ['0000ff'])
+ self.assertEquals(idx, 1)
+ self.assertEquals(len(chart.data), 2)
+ self.assertEquals(len(chart.data[0]), 1)
+ self.assertEquals(chart.data[0][0].size, 1)
+ self.assertEquals(len(chart.data[1]), 1)
+ self.assertEquals(chart.data[1][0].size, 2)
+
+ def testAddSegmentToPie(self):
+ chart = pie_chart.PieChart()
+ chart.AddPie([1], ['A'], ['ff0000'])
+ chart.AddPie([2], ['B'], ['0000ff'])
+ chart.AddSegment([10], ['AA'])
+ self.assertEquals(len(chart.data[0]), 2)
+ self.assertEquals(len(chart.data[1]), 1)
+ chart.AddSegment([20], ['BB'], pie_index=1)
+ self.assertEquals(len(chart.data[0]), 2)
+ self.assertEquals(len(chart.data[1]), 2)


if __name__ == '__main__':

Reply all
Reply to author
Forward
0 new messages