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.
  • [get | view] (2006-09-21 18:47:51, 6.1 KB) [[attachment:Sparkline.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.