pygalcharts.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. """
  2. pygal Tag
  3. ---------
  4. This implements a Liquid-style pygal tag for Pelican. JSON is used for the data,
  5. and you can pass a bunch of pygal's 'config' items through as-is
  6. [1] http://www.pygal.org/
  7. Syntax
  8. ------
  9. {% pygal
  10. {
  11. <graph data>
  12. }
  13. %}
  14. Examples
  15. --------
  16. {%
  17. pygal {
  18. "type": "bar",
  19. "title": "Test Chart",
  20. "x-labels" : {"from": 2002, "to": 2013},
  21. "data" : [
  22. {"title": "Firefox",
  23. "values": [null, null, 0, 16.6, 25, 31, 36.4, 45.5, 46.3, 42.8, 37.1]},
  24. {"title": "Chrome",
  25. "values": [null, null, null, null, null, null, 0, 3.9, 10.8, 23.8, 35.3]},
  26. {"title": "IE",
  27. "values": [85.8, 84.6, 84.7, 74.5, 66, 58.6, 54.7, 44.8, 36.2, 26.6, 20.1]},
  28. {"title": "Others",
  29. "values": [14.2, 15.4, 15.3, 8.9, 9, 10.4, 8.9, 5.8, 6.7, 6.8, 7.5]}
  30. ]
  31. }
  32. %}
  33. {%
  34. pygal {
  35. "type": "pie",
  36. "half_pie": true,
  37. "title": "Browser usage in February 2012 (in %)",
  38. "data" : [
  39. {"title": "IE",
  40. "values": 19.5},
  41. {"title": "Firefox",
  42. "values": 36.6},
  43. {"title": "Chrome",
  44. "values": 36.3},
  45. {"title": "Safari",
  46. "values": 4.5},
  47. {"title": "Opera",
  48. "values": 2.3}
  49. ]
  50. }
  51. %}
  52. {%
  53. pygal {
  54. "type": "pie",
  55. "config": {
  56. "show_legend": false,
  57. "print_values": true,
  58. "show_y_labels": true
  59. },
  60. "title": "Browser usage in February 2012 (in %)",
  61. "data" : [
  62. {"title": "IE",
  63. "values": 19.5},
  64. {"title": "Firefox",
  65. "values": 36.6},
  66. {"title": "Chrome",
  67. "values": 36.3},
  68. {"title": "Safari",
  69. "values": 4.5},
  70. {"title": "Opera",
  71. "values": 2.3}
  72. ]
  73. }
  74. %}
  75. ...
  76. Output
  77. ------
  78. <<div class="pygal" style="text-align: center;"><embed type="image/svg+xml" src=SVG_MARKUP_EMBEDDED style="max-width:1000px"/></div>
  79. """
  80. import base64
  81. import re
  82. from json import loads
  83. from .mdx_liquid_tags import LiquidTags
  84. SYNTAX = '{% pygal (data) %}'
  85. DOT_BLOCK_RE = re.compile(r'^\s*\{\s*(?P<code>.*\})\s*\}$', re.MULTILINE | re.DOTALL)
  86. def run_pygal(data, options=[], format='svg'):
  87. """ Runs pygal programs and returns image data
  88. """
  89. import pygal
  90. chart_title = data.get('title', None)
  91. chart_type = data.get('type', '').lower()
  92. # Config options are pretty much proxied straight through from the JSON dict into the object
  93. config = pygal.Config()
  94. config_dict = data.get('config', {})
  95. for key in config_dict.keys():
  96. setattr(config, key, config_dict[key])
  97. if chart_type == 'bar':
  98. chart = pygal.HorizontalBar(config) if data.get('horizontal', False) else pygal.Bar(config)
  99. elif chart_type == 'line':
  100. chart = pygal.Line(config)
  101. elif chart_type == 'pie':
  102. ir=data.get('inner_radius', 0.0)
  103. hp=data.get('half_pie', False)
  104. chart = pygal.Pie(config, inner_radius=ir, half_pie=hp)
  105. else:
  106. print('undefined or unknown chart type')
  107. if chart is not None:
  108. chart.title = data.get('title', None)
  109. # Do labels (if present)
  110. label_data = data.get('x-labels', None)
  111. if isinstance(label_data, list):
  112. # use list
  113. chart.x_labels = label_data
  114. elif isinstance(label_data, dict):
  115. # use a range
  116. range_from = label_data.get('from', 0)
  117. range_to = label_data.get('to', 0)
  118. chart.x_labels = map(str, range(range_from, range_to))
  119. # insert data
  120. for data_set in data.get('data', []):
  121. title = data_set.get('title', None)
  122. values = data_set.get('values', None)
  123. chart.add(title, values)
  124. # now render
  125. result = chart.render_data_uri()
  126. else:
  127. result = None
  128. return result
  129. @LiquidTags.register('pygal')
  130. def pygal_parser(preprocessor, tag, markup):
  131. """ Simple pygal parser """
  132. # Find JSON payload
  133. data = loads(markup)
  134. if tag == 'pygal' and data is not None:
  135. # Run generation of chart
  136. output = run_pygal(data)
  137. # Return embedded SVG image
  138. return '<div class="pygal" style="text-align: center;"><embed type="image/svg+xml" src=%s style="max-width:1000px"/></div>' % output
  139. else:
  140. raise ValueError('Error processing input. \nExpected syntax: {0}'.format(SYNTAX))
  141. #----------------------------------------------------------------------
  142. # This import allows image tag to be a Pelican plugin
  143. from .liquid_tags import register