367
390
# -------------------------------------------------------------------------
369
def download(self, file_name):
370
opener = urllib2.urlopen(self.get_url())
392
def download(self, file_name, use_post=True):
394
opener = urllib.request.urlopen(self.BASE_URL, self.get_url_extension())
396
opener = urllib.request.urlopen(self.get_url())
372
398
if opener.headers['content-type'] != 'image/png':
373
399
raise BadContentTypeException('Server responded with a ' \
374
400
'content-type of %s' % opener.headers['content-type'])
376
open(file_name, 'wb').write(urllib.urlopen(self.get_url()).read())
402
open(file_name, 'wb').write(opener.read())
378
404
# Simple settings
379
405
# -------------------------------------------------------------------------
381
407
def set_title(self, title):
383
self.title = urllib.quote(title)
409
self.title = urllib.parse.quote(title)
385
411
self.title = None
413
def set_title_style(self, colour=None, font_size=None):
414
if not colour is None:
415
_check_colour(colour)
416
if not colour and not font_size:
418
self.title_colour = colour or '333333'
419
self.title_font_size = font_size or 13.5
387
421
def set_legend(self, legend):
388
422
"""legend needs to be a list, tuple or None"""
389
423
assert(isinstance(legend, list) or isinstance(legend, tuple) or
392
self.legend = [urllib.quote(a) for a in legend]
426
self.legend = [urllib.parse.quote(a) for a in legend]
394
428
self.legend = None
430
def set_legend_position(self, legend_position):
432
self.legend_position = urllib.parse.quote(legend_position)
434
self.legend_position = None
397
437
# -------------------------------------------------------------------------
615
677
url_bits.append('chxt=%s' % ','.join(available_axis))
617
url_bits.append('chxl=%s' % '|'.join(label_axis))
679
url_bits.append('chxl=%s' % '%7c'.join(label_axis))
619
url_bits.append('chxr=%s' % '|'.join(range_axis))
681
url_bits.append('chxr=%s' % '%7c'.join(range_axis))
621
url_bits.append('chxp=%s' % '|'.join(positions))
683
url_bits.append('chxp=%s' % '%7c'.join(positions))
623
url_bits.append('chxs=%s' % '|'.join(styles))
685
url_bits.append('chxs=%s' % '%7c'.join(styles))
624
686
return '&'.join(url_bits)
626
688
# Markers, Ranges and Fill area (chm)
627
689
# -------------------------------------------------------------------------
629
def markers_to_url(self):
630
return 'chm=%s' % '|'.join([','.join(a) for a in self.markers])
691
def markers_to_url(self):
692
return 'chm=%s' % '%7c'.join([','.join(a) for a in self.markers])
632
694
def add_marker(self, index, point, marker_type, colour, size, priority=0):
633
695
self.markers.append((marker_type, colour, str(index), str(point), \
634
696
str(size), str(priority)))
636
698
def add_horizontal_range(self, colour, start, stop):
637
self.markers.append(('r', colour, '1', str(start), str(stop)))
699
self.markers.append(('r', colour, '0', str(start), str(stop)))
701
def add_data_line(self, colour, data_set, size, priority=0):
702
self.markers.append(('D', colour, str(data_set), '0', str(size), \
705
def add_marker_text(self, string, colour, data_set, data_point, size, \
707
self.markers.append((str(string), colour, str(data_set), \
708
str(data_point), str(size), str(priority)))
639
710
def add_vertical_range(self, colour, start, stop):
640
self.markers.append(('R', colour, '1', str(start), str(stop)))
711
self.markers.append(('R', colour, '0', str(start), str(stop)))
642
713
def add_fill_range(self, colour, index_start, index_end):
643
714
self.markers.append(('b', colour, str(index_start), str(index_end), \
887
966
Chart.__init__(self, *args, **kwargs)
888
967
self.geo_area = 'world'
969
self.__areas = ('africa', 'asia', 'europe', 'middle_east',
970
'south_america', 'usa', 'world')
972
'AD', 'AE', 'AF', 'AG', 'AI', 'AL', 'AM', 'AN', 'AO', 'AQ', 'AR',
973
'AS', 'AT', 'AU', 'AW', 'AX', 'AZ', 'BA', 'BB', 'BD', 'BE', 'BF',
974
'BG', 'BH', 'BI', 'BJ', 'BL', 'BM', 'BN', 'BO', 'BR', 'BS', 'BT',
975
'BV', 'BW', 'BY', 'BZ', 'CA', 'CC', 'CD', 'CF', 'CG', 'CH', 'CI',
976
'CK', 'CL', 'CM', 'CN', 'CO', 'CR', 'CU', 'CV', 'CX', 'CY', 'CZ',
977
'DE', 'DJ', 'DK', 'DM', 'DO', 'DZ', 'EC', 'EE', 'EG', 'EH', 'ER',
978
'ES', 'ET', 'FI', 'FJ', 'FK', 'FM', 'FO', 'FR', 'GA', 'GB', 'GD',
979
'GE', 'GF', 'GG', 'GH', 'GI', 'GL', 'GM', 'GN', 'GP', 'GQ', 'GR',
980
'GS', 'GT', 'GU', 'GW', 'GY', 'HK', 'HM', 'HN', 'HR', 'HT', 'HU',
981
'ID', 'IE', 'IL', 'IM', 'IN', 'IO', 'IQ', 'IR', 'IS', 'IT', 'JE',
982
'JM', 'JO', 'JP', 'KE', 'KG', 'KH', 'KI', 'KM', 'KN', 'KP', 'KR',
983
'KW', 'KY', 'KZ', 'LA', 'LB', 'LC', 'LI', 'LK', 'LR', 'LS', 'LT',
984
'LU', 'LV', 'LY', 'MA', 'MC', 'MD', 'ME', 'MF', 'MG', 'MH', 'MK',
985
'ML', 'MM', 'MN', 'MO', 'MP', 'MQ', 'MR', 'MS', 'MT', 'MU', 'MV',
986
'MW', 'MX', 'MY', 'MZ', 'NA', 'NC', 'NE', 'NF', 'NG', 'NI', 'NL',
987
'NO', 'NP', 'NR', 'NU', 'NZ', 'OM', 'PA', 'PE', 'PF', 'PG', 'PH',
988
'PK', 'PL', 'PM', 'PN', 'PR', 'PS', 'PT', 'PW', 'PY', 'QA', 'RE',
989
'RO', 'RS', 'RU', 'RW', 'SA', 'SB', 'SC', 'SD', 'SE', 'SG', 'SH',
990
'SI', 'SJ', 'SK', 'SL', 'SM', 'SN', 'SO', 'SR', 'ST', 'SV', 'SY',
991
'SZ', 'TC', 'TD', 'TF', 'TG', 'TH', 'TJ', 'TK', 'TL', 'TM', 'TN',
992
'TO', 'TR', 'TT', 'TV', 'TW', 'TZ', 'UA', 'UG', 'UM', 'US', 'UY',
993
'UZ', 'VA', 'VC', 'VE', 'VG', 'VI', 'VN', 'VU', 'WF', 'WS', 'YE',
994
'YT', 'ZA', 'ZM', 'ZW')
891
996
def type_to_url(self):
894
999
def set_codes(self, codes):
1000
'''Set the country code map for the data.
1001
Codes given in a list.
1012
if cc in self.__ccodes:
1015
raise UnknownCountryCodeException(cc)
1017
self.codes = codemap
1019
def set_geo_area(self, area):
1020
'''Sets the geo area for the map.
1031
if area in self.__areas:
1032
self.geo_area = area
1034
raise UnknownChartType('Unknown chart type for maps: %s' %area)
897
1036
def get_url_bits(self, data_class=None):
898
1037
url_bits = Chart.get_url_bits(self, data_class=data_class)
901
1040
url_bits.append('chld=%s' % ''.join(self.codes))
1043
def add_data_dict(self, datadict):
1044
'''Sets the data and country codes via a dictionary.
1046
i.e. {'DE': 50, 'GB': 30, 'AT': 70}
1049
self.set_codes(datadict.keys())
1050
self.add_data(datadict.values())
905
1053
class GoogleOMeterChart(PieChart):
906
1054
"""Inheriting from PieChart because of similar labeling"""
1056
def __init__(self, *args, **kwargs):
1057
PieChart.__init__(self, *args, **kwargs)
1058
if self.auto_scale and not self.x_range:
1059
warnings.warn('Please specify an x_range with GoogleOMeterChart, '
1060
'otherwise one arrow will always be at the max.')
908
1062
def type_to_url(self):
909
1063
return 'cht=gom'
1066
class QRChart(Chart):
1068
def __init__(self, *args, **kwargs):
1069
Chart.__init__(self, *args, **kwargs)
1070
self.encoding = None
1071
self.ec_level = None
1074
def type_to_url(self):
1077
def data_to_url(self, data_class=None):
1079
raise NoDataGivenException()
1080
return 'chl=%s' % urllib.parse.quote(self.data[0])
1082
def get_url_bits(self, data_class=None):
1083
url_bits = Chart.get_url_bits(self, data_class=data_class)
1085
url_bits.append('choe=%s' % self.encoding)
1087
url_bits.append('chld=%s%%7c%s' % (self.ec_level, self.margin))
1090
def set_encoding(self, encoding):
1091
self.encoding = encoding
1093
def set_ec(self, level, margin):
1094
self.ec_level = level
1095
self.margin = margin
912
1098
class ChartGrammar(object):
914
def __init__(self, grammar):
1104
def parse(self, grammar):
915
1105
self.grammar = grammar
916
1106
self.chart = self.create_chart_instance()
1108
for attr in self.grammar:
1109
if attr in ('w', 'h', 'type', 'auto_scale', 'x_range', 'y_range'):
1110
continue # These are already parsed in create_chart_instance
1111
attr_func = 'parse_' + attr
1112
if not hasattr(self, attr_func):
1113
warnings.warn('No parser for grammar attribute "%s"' % (attr))
1115
getattr(self, attr_func)(grammar[attr])
1119
def parse_data(self, data):
1120
self.chart.data = data
919
1123
def get_possible_chart_types():
920
1124
possible_charts = []
921
for cls_name in globals():
1125
for cls_name in globals().keys():
922
1126
if not cls_name.endswith('Chart'):
924
1128
cls = globals()[cls_name]
925
1129
# Check if it is an abstract class
1131
a = cls(1, 1, auto_scale=False)
928
1133
except AbstractClassException:
930
1135
# Strip off "Class"
931
1136
possible_charts.append(cls_name[:-5])
932
1137
return possible_charts
934
def create_chart_instance(self):
1139
def create_chart_instance(self, grammar=None):
1141
grammar = self.grammar
1142
assert(isinstance(grammar, dict)) # grammar must be a dict
935
1143
assert('w' in grammar) # width is required
936
1144
assert('h' in grammar) # height is required
937
1145
assert('type' in grammar) # type is required
1146
chart_type = grammar['type']
1149
auto_scale = grammar.get('auto_scale', None)
1150
x_range = grammar.get('x_range', None)
1151
y_range = grammar.get('y_range', None)
938
1152
types = ChartGrammar.get_possible_chart_types()
939
if grammar['type'] not in types:
1153
if chart_type not in types:
940
1154
raise UnknownChartType('%s is an unknown chart type. Possible '
941
'chart types are %s' % (grammar['type'], ','.join(types)))
1155
'chart types are %s' % (chart_type, ','.join(types)))
1156
return globals()[chart_type + 'Chart'](w, h, auto_scale=auto_scale,
1157
x_range=x_range, y_range=y_range)
943
1159
def download(self):