PageRenderTime 51ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/prewikka-1.0.0/prewikka/Chart.py

#
Python | 385 lines | 357 code | 9 blank | 19 comment | 1 complexity | ae925031737430cd039a65d32a3f45cb MD5 | raw file
  1. # Copyright (C) 2005-2009 PreludeIDS Technologies. All Rights Reserved.
  2. # Author: Nicolas Delon <nicolas.delon@prelude-ids.com>
  3. # Author: Yoann Vandoorselaere <yoann.v@prelude-ids.com>
  4. #
  5. # This file is part of the Prewikka program.
  6. #
  7. # This program is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation; either version 2, or (at your option)
  10. # any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program; see the file COPYING. If not, write to
  19. # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  20. import stat
  21. import time
  22. import base64
  23. import urllib
  24. import os, os.path
  25. import glob, tempfile
  26. from prewikka import utils, siteconfig, cairoplot
  27. from preludedb import PreludeDBError
  28. from xml.dom.minidom import parse, parseString
  29. RED_STD = "c1292e"
  30. ORANGE_STD = "F29324"
  31. YELLOW_STD = "f8e930"
  32. GREEN_STD = "7ab41d"
  33. BLUE_STD = "528fc8"
  34. COLOR_MAP = "528fc8", "7ab41d", "f8e930", "F29324", "c1292e", "874b94", "212483", \
  35. "487118", "ea752c", "8C0A14", "5F3269", "196d38"
  36. def userToHex(user):
  37. if not user:
  38. return ""
  39. hval = ""
  40. for i in user:
  41. hval += hex(ord(i)).replace("0x", "")
  42. return hval
  43. class ChartCommon:
  44. def __init__(self, user, width=800, height=450):
  45. self._user = user
  46. self._filename = None
  47. self._values = [ ]
  48. self._labels = [ ]
  49. self._has_title = False
  50. self._support_link = False
  51. self._width = width
  52. self._height = height
  53. self._color_map = COLOR_MAP
  54. self._names_map = {}
  55. self._color_map_idx = 0
  56. def hex2rgb(self, color):
  57. r = int(color[0:2], 16)
  58. g = int(color[2:4], 16)
  59. b = int(color[4:6], 16)
  60. return (r/255.0, g/255.0, b/255.0)
  61. def getWidth(self):
  62. return self._width
  63. def getHeight(self):
  64. return self._height
  65. def _colorMap2str(self):
  66. if type(self._color_map) not in (list, tuple):
  67. return None
  68. str = ""
  69. for color in self._color_map:
  70. if len(str):
  71. str += ","
  72. str += color
  73. return str
  74. def getItemColor(self, str):
  75. if isinstance(self._color_map, dict):
  76. return self._color_map[str]
  77. color = self._color_map[self._color_map_idx % len(self._color_map)]
  78. self._color_map_idx += 1
  79. return color
  80. def setColorMap(self, plist):
  81. self._color_map = plist
  82. def getFilename(self):
  83. return self._filename
  84. def getHref(self):
  85. return self._href
  86. def setTitle(self, title):
  87. self.addTitle(title, "", 14)
  88. self._has_title = True
  89. def setValues(self, values):
  90. self._values = values
  91. def setLabels(self, labels):
  92. self._labels = labels
  93. def addLabelValuePair(self, label, value, link=None):
  94. self._labels.append(label)
  95. if self._support_link:
  96. self._values.append((value, utils.escape_html_string(link)))
  97. else:
  98. self._values.append(value)
  99. def _remove_old_chart_files(self, pathname, expire):
  100. directory = pathname + "_*"
  101. files = glob.glob(directory)
  102. used = None
  103. prev = None
  104. for f in files:
  105. mtime = os.stat(f)[stat.ST_MTIME]
  106. now = time.time()
  107. if not expire or (now - mtime) > (2 * expire):
  108. os.remove(f)
  109. def _getFilename(self, name, expire = None, uid=None, gid=None, suffix=".png"):
  110. old_mask = os.umask(0)
  111. basename = base64.urlsafe_b64encode(name.encode("utf-8"))
  112. pathname = os.path.join(siteconfig.htdocs_dir, "generated_images")
  113. user = base64.urlsafe_b64encode(self._user.login.encode("utf-8"))
  114. pathname = os.path.normpath(os.path.join(pathname, user))
  115. try:
  116. os.mkdir(pathname, 0755)
  117. except: pass
  118. if uid != None and gid != None:
  119. os.lchown(pathname, uid, gid)
  120. self._remove_old_chart_files(os.path.join(pathname, basename), expire)
  121. fd, self._filename = tempfile.mkstemp(prefix = basename + "_", suffix = suffix, dir = pathname)
  122. if uid != None and gid != None:
  123. os.lchown(self._filename, uid, gid)
  124. os.chmod(self._filename, 0644)
  125. self._href = urllib.quote("prewikka/generated_images/%s" % (user or "") + "/" + os.path.basename(self._filename))
  126. os.umask(old_mask)
  127. return self._filename
  128. class TimelineChartCommon(ChartCommon):
  129. def getType(self):
  130. return "None"
  131. def __init__(self, user, width, height):
  132. ChartCommon.__init__(self, user, width, height)
  133. self._got_value = False
  134. self._color_map_idx = 0
  135. self._assigned_colors = {}
  136. self._multiple_values = False
  137. self._total = []
  138. def enableMultipleValues(self, names_and_colors={}):
  139. self._multiple_values = True
  140. self._names_and_colors = names_and_colors
  141. self._values = utils.OrderedDict()
  142. for name in self._names_and_colors.keys():
  143. self._values[name] = []
  144. def getItemColor(self, name):
  145. if not self._multiple_values:
  146. return ChartCommon.getItemColor(self, name)
  147. if self._names_and_colors.has_key(name):
  148. return self._names_and_colors[name]
  149. if self._assigned_colors.has_key(name):
  150. return self._assigned_colors[name]
  151. color = self._assigned_colors[name] = ChartCommon.getItemColor(self, name)
  152. return color
  153. def _itemFromValue(self, value):
  154. if isinstance(value, tuple):
  155. return value
  156. return value, None
  157. if self._support_link:
  158. return value[0], utils.escape_html_string(value[1])
  159. else:
  160. return value[0]
  161. def addLabelValuesPair(self, label, values, total_link):
  162. empty = True
  163. for i in values.values():
  164. if i != 0:
  165. empty = False
  166. break
  167. if not self._got_value and empty:
  168. # do not add 0 only values at the beginning of the chart
  169. return
  170. if self._support_link and total_link:
  171. total_link = utils.escape_html_string(total_link)
  172. self._labels.append(label)
  173. clen = 0
  174. if self._values:
  175. clen = len(self._values.values()[0])
  176. total = 0
  177. for name in values.keys():
  178. if not self._values.has_key(name):
  179. if clen > 0:
  180. self._values[name] = [(0, None) for i in range(0, clen)]
  181. else:
  182. self._values[name] = []
  183. value = self._itemFromValue(values[name])
  184. self._values[name].append(value)
  185. total += value[0]
  186. self._total.append((total, total_link))
  187. for name in self._values.keys():
  188. if not values.has_key(name):
  189. self._values[name].append(self._itemFromValue(0))
  190. self._got_value = True
  191. def addLabelValuePair(self, label, values, link=None):
  192. if self._multiple_values or isinstance(values, dict):
  193. if not isinstance(self._values, dict):
  194. self._values = utils.OrderedDict()
  195. self.addLabelValuesPair(label, values, link)
  196. else:
  197. ChartCommon.addLabelValuePair(self, label, values, link)
  198. class CairoDistributionChart(ChartCommon):
  199. def getType(self):
  200. return "None"
  201. def render(self, name, expire=None, suffix=".png", uid=None, gid=None):
  202. fname = self._getFilename(name, expire, uid, gid);
  203. color = []
  204. data = utils.OrderedDict()
  205. total = 0
  206. lv = zip(self._labels, self._values)
  207. lv.sort(lambda x, y: int(x[1] - y[1]))
  208. for l, v in lv:
  209. total += v
  210. if total:
  211. share = 100.0 / total
  212. for l, v in lv:
  213. l = str(l)
  214. data["%s (%d, %.2f%%)" % (l, v, share * v)] = v
  215. color.append(self.hex2rgb(self.getItemColor(l)))
  216. cairoplot.pie_plot(fname, data, self._width, self._height, gradient = True, shadow = True, colors=color)
  217. class CairoTimelineChart(TimelineChartCommon):
  218. def render(self, name, expire=None, suffix=".png", uid=None, gid=None):
  219. fname = self._getFilename(name, expire, uid, gid);
  220. colors = []
  221. values = utils.OrderedDict()
  222. for name in self._values.keys():
  223. nname = name[0:min(len(name), 25)]
  224. if not values.has_key(nname):
  225. values[nname] = []
  226. for item in self._values[name]:
  227. values[nname].append(item[0])
  228. colors.append(self.hex2rgb(self.getItemColor(name)))
  229. cairoplot.dot_line_plot(fname, values, self._width, self._height, border=0, axis=True, grid=True,
  230. x_labels = self._labels, series_legend=True, series_colors=colors)
  231. class CairoStackedTimelineChart(TimelineChartCommon):
  232. def render(self, name, expire=None, suffix=".png", uid=None, gid=None):
  233. fname = self._getFilename(name, expire, uid, gid);
  234. colors = []
  235. legend = []
  236. labels = []
  237. data = []
  238. minval = 0
  239. maxval = 0
  240. values_items = self._values.items()
  241. for i in xrange(0, len(self._labels)):
  242. l = []
  243. total = 0
  244. for name, values in values_items:
  245. l.append(values[i])
  246. total += values[i]
  247. minval = min(minval, total)
  248. maxval = max(maxval, total)
  249. data.append(l)
  250. l = minval
  251. increment = maxval / 20.0
  252. for i in xrange(0, 20+1):
  253. labels.append("%.1f" % l)
  254. l += increment
  255. idx = 0
  256. for name, color in self._names_and_colors.values():
  257. if self._values.has_key(name):
  258. if color:
  259. colors.append(self.hex2rgb(color))
  260. else:
  261. colors.append(self.hex2rgb(COLOR_MAP[idx % len(COLOR_MAP)]))
  262. idx += 1
  263. legend.append(name)
  264. cairoplot.vertical_bar_plot(fname, data, self._width, self._height, border=0, series_labels=legend, display_values=True, grid=True, rounded_corners=False, stack=True,
  265. three_dimension=False, y_labels=labels, x_labels = self._labels, colors=colors)
  266. class CairoWorldChart(CairoDistributionChart):
  267. def needCountryCode(self):
  268. return False
  269. class TimelineChart(object):
  270. def __new__(cls, user, width, height):
  271. o = CairoTimelineChart(user, width, height)
  272. o.isFlash = False
  273. return o
  274. class StackedTimelineChart(object):
  275. def __new__(cls, user, width, height):
  276. o = CairoStackedTimelineChart(user, width, height)
  277. o.isFlash = False
  278. return o
  279. class WorldChart(object):
  280. def __new__(cls, user, width, height):
  281. o = CairoWorldChart(user, width, height)
  282. o.isFlash = False
  283. return o
  284. class DistributionChart(object):
  285. def __new__(cls, user, width, height):
  286. o = CairoDistributionChart(user, width, height)
  287. o.isFlash = True
  288. return o