Attachment 'Sparkline.py'
Download 1 # -*- coding: iso-8859-1 -*-
2 """
3 MoinMoin - Sparkline Macro
4
5 This macro is used to generate and display a sparkline. The generated sparklines
6 appear in the page attachments.
7
8 Requirements:
9 MoinMoin (http://moinmoin.wikiwikiweb.de/)
10 Python Imaging Library (http://www.pythonware.com/products/pil/)
11
12 Installation:
13 Ensure you have PIL installed (try "import Image" in a python shell) then
14 drop this file into your <wikidata>/data/plugin/macro folder.
15
16 Syntax:
17 [[Sparkline(data [,h=height] [,n=min] [,x=max] [,nc=color] [,xc=color]
18 [,lc=color] [,s=step]]]
19
20 Parameters:
21 data: Comma seperated list of values contained by quotes
22
23 Keyword Parameters:
24 h: height in pixels
25 n: minimum value of y scale on sparkline
26 x: maximum value of y scale on sparkline
27 nc: color of minimum value (not highlighted if not specified)
28 xc: color of maximum value (not highlighted if not specified)
29 lc: color of last value
30 s: step size, 1 shows all values
31
32 Examples:
33 Note: Don't put line breaks in the macro as MoinMoin will not recognize
34 the text as a macro. If you want the macro line to wrap in the text
35 area, just put a space after a comma.
36
37 [[Sparkline(88,84,82,92,82,86,66,82,44,64,66,88,96,80,24,26,14,0,0,26,8,6,6,
38 24,52,66,36,6,10,14,30)]]
39
40 [[Sparkline(88,84,82,92,82,86,66,82,44,64,66,88,96,80,24,26,14,0,0,26,8,6,6,
41 24,52,66,36,6,10,14,30, h=20, n=-10, x=150, nc=#00FF00, xc=#FF0000,
42 lc=#000000, s=1)]]
43
44 The majority of Sparkline macro functionality is provided by the Sparkline cgi
45 written by Joe Gregorio (joe@bitworking.org). Implementation as a MoinMoin
46 macro was done by Dave Vieglais (vieglais@ku.edu)
47
48 @copyright: 2006 The University of Kansas Biodiversity Research Center
49 @license: GNU GPL, see COPYING for details.
50 """
51
52 from MoinMoin import wikiutil, config
53 from MoinMoin.action import AttachFile
54 import re, string
55 import os, os.path
56 import Image, ImageDraw
57 import md5
58 import time
59
60 def plot_error(results, args, fdest):
61 """
62 Renders a box with a red x in it.
63 """
64 im = Image.new("RGB", (40, 15), 'white')
65 draw = ImageDraw.Draw(im)
66 draw.line((0, 0) + im.size, fill="red")
67 draw.line((0, im.size[1], im.size[0], 0), fill="red")
68 del draw
69 im.save(fdest, "PNG")
70
71 def plot_sparkline_smooth(results, args, fdest):
72 """
73 Routine from Joe Gregorio's sparklines website
74 (http://bitworking.org/projects/sparklines/) with minor modifications by
75 Dave V.
76
77 Parameters
78 results: list of numeric values to plot
79 args: arguments for controlling plot appearance
80 fdest: full path name of PNG file to be generated
81 """
82 step = int(args.get('s', 1))
83 height = int(args.get('h', 20))
84 dmin = int(args.get('n', min(results)))
85 dmax = int(args.get('x', max(results)))
86
87 im = Image.new("RGB", ((len(results)-1)*step+4, height), 'white')
88 draw = ImageDraw.Draw(im)
89 coords = zip(range(1,len(results)*step+1, step), \
90 [height - 3 - (y-dmin)/(float(dmax - dmin +1)/(height-4)) \
91 for y in results])
92 draw.line(coords, fill="#888888")
93 has_min = True
94 has_max = True
95 has_last = True
96 min_color = args.get('nc', '#0000FF')
97 if min_color is None:
98 has_min = False
99 max_color = args.get('xc', '#00FF00')
100 if max_color is None:
101 has_max = False
102 last_color = args.get('lc', '#FF0000')
103 if last_color is None:
104 has_last = False
105 if has_min:
106 min_pt = coords[results.index(min(results))]
107 draw.rectangle([min_pt[0]-1, min_pt[1]-1, min_pt[0]+1, min_pt[1]+1], \
108 fill=min_color)
109 if has_max:
110 max_pt = coords[results.index(max(results))]
111 draw.rectangle([max_pt[0]-1, max_pt[1]-1, max_pt[0]+1, max_pt[1]+1], \
112 fill=max_color)
113 if has_last:
114 end = coords[-1]
115 draw.rectangle([end[0]-1, end[1]-1, end[0]+1, end[1]+1], fill=last_color)
116 del draw
117 im.save(fdest, "PNG")
118
119 def deleteOldFiles(attdir, maxage=10):
120 '''
121 Deletes files more than maxage seconds old that have prefix "spkl_" and
122 extension ".png". This purges all the sparklines from attdir, but is only
123 called once per request so won't interfere with multiple sparklines on a page
124
125 Parameters
126 attdir: folder to examine for sparkline files
127 maxage: maximum age of files in seconds (defaults to one day)
128 '''
129 filelist = os.listdir(attdir)
130 test = re.compile("^spkl_.*[.]png", re.IGNORECASE)
131 mmax = time.time() - maxage
132 for f in filelist:
133 if test.search(f):
134 f = os.path.join(attdir ,f)
135 mtime = os.path.getmtime(f)
136 if mtime < mmax:
137 try:
138 os.remove(f)
139 except:
140 pass
141
142 def execute(macro, text):
143 '''
144 Implementation of MoinMoin Sparklines macro
145 '''
146 #hash text to use as the attachment name. Then if text is
147 #changed, the sparkline needs to be redone.
148 hsh = md5.new(text)
149 hfname = "spkl_"+hsh.hexdigest() + ".png"
150 request = macro.request
151 pagename= macro.formatter.page.page_name
152 attach_dir=AttachFile.getAttachDir(request,pagename,create=1)
153
154 #check to see if we have already purged files in this request
155 if not hasattr(request,'__deletedOldSparkles'):
156 deleteOldFiles(attach_dir)
157 request.__deletedOldSparkles = True
158
159 fname = os.path.join(attach_dir, hfname)
160 url=AttachFile.getAttachUrl(pagename,hfname,request)
161
162 if not os.path.exists(fname):
163 #need to generate image and store it in the attachment folder
164 kw = {'h': 20, 'nc': None, 'xc': None, 'lc': None, 's': 1}
165 if text:
166 args = text.split(',')
167 else:
168 args = []
169 data = []
170 kwargs = []
171 for a in args:
172 a = a.strip()
173 if a.find("=") < 0:
174 try:
175 data.append(float(a))
176 except:
177 pass
178 else:
179 kwargs.append(a)
180 argCount = len(kwargs)
181 kwCount = 0
182 for a in kwargs:
183 if (a.find('=') > -1):
184 kwCount = kwCount + 1
185 key = a.split('=')
186 kw[str(key[0])] = wikiutil.escape(string.join(key[1],''), quote=1)
187 argCount = argCount - kwCount
188 try:
189 plot_sparkline_smooth(data, kw, fname)
190 except:
191 plot_error(data, kw, fname)
192
193 #now generate the image link to the attached sparkline
194 return macro.formatter.image(src=url)
195
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.