# -*- coding: iso-8859-1 -*-
"""
MoinMoin - Sparkline Macro

This macro is used to generate and display a sparkline. The generated sparklines 
appear in the page attachments.
      
Requirements:
  MoinMoin (http://moinmoin.wikiwikiweb.de/)
  Python Imaging Library (http://www.pythonware.com/products/pil/)

Installation:
  Ensure you have PIL installed (try "import Image" in a python shell) then
  drop this file into your <wikidata>/data/plugin/macro folder.
      
Syntax:
  [[Sparkline(data [,h=height] [,n=min] [,x=max] [,nc=color] [,xc=color] 
    [,lc=color] [,s=step]]]

Parameters:
  data: Comma seperated list of values contained by quotes

Keyword Parameters:
  h: height in pixels 
  n: minimum value of y scale on sparkline 
  x: maximum value of y scale on sparkline 
  nc: color of minimum value (not highlighted if not specified) 
  xc: color of maximum value (not highlighted if not specified) 
  lc: color of last value 
  s: step size, 1 shows all values

Examples:
  Note: Don't put line breaks in the macro as MoinMoin will not recognize
        the text as a macro.  If you want the macro line to wrap in the text 
        area, just put a space after a comma.
        
  [[Sparkline(88,84,82,92,82,86,66,82,44,64,66,88,96,80,24,26,14,0,0,26,8,6,6,
  24,52,66,36,6,10,14,30)]]
  
  [[Sparkline(88,84,82,92,82,86,66,82,44,64,66,88,96,80,24,26,14,0,0,26,8,6,6,
  24,52,66,36,6,10,14,30, h=20, n=-10, x=150, nc=#00FF00, xc=#FF0000, 
  lc=#000000, s=1)]]

The majority of Sparkline macro functionality is provided by the Sparkline cgi 
written by Joe Gregorio (joe@bitworking.org).  Implementation as a MoinMoin 
macro was done by Dave Vieglais (vieglais@ku.edu)
            
@copyright: 2006 The University of Kansas Biodiversity Research Center
@license: GNU GPL, see COPYING for details.
"""

from MoinMoin import wikiutil, config
from MoinMoin.action import AttachFile
import re, string
import os, os.path
import Image, ImageDraw
import md5
import time

def plot_error(results, args, fdest):
  """
  Renders a box with a red x in it.
  """
  im = Image.new("RGB", (40, 15), 'white')
  draw = ImageDraw.Draw(im)
  draw.line((0, 0) + im.size, fill="red")
  draw.line((0, im.size[1], im.size[0], 0), fill="red")
  del draw                                                      
  im.save(fdest, "PNG")

def plot_sparkline_smooth(results, args, fdest):
  """
  Routine from Joe Gregorio's sparklines website 
  (http://bitworking.org/projects/sparklines/) with minor modifications by 
  Dave V.
  
  Parameters
    results: list of numeric values to plot
    args: arguments for controlling plot appearance
    fdest: full path name of PNG file to be generated
  """
  step = int(args.get('s', 1))
  height = int(args.get('h', 20))
  dmin = int(args.get('n', min(results)))
  dmax = int(args.get('x', max(results)))
   
  im = Image.new("RGB", ((len(results)-1)*step+4, height), 'white')
  draw = ImageDraw.Draw(im)
  coords = zip(range(1,len(results)*step+1, step), \
           [height - 3  - (y-dmin)/(float(dmax - dmin +1)/(height-4)) \
           for y in results])
  draw.line(coords, fill="#888888")
  has_min = True
  has_max = True
  has_last = True
  min_color = args.get('nc', '#0000FF')
  if min_color is None:
    has_min = False
  max_color = args.get('xc', '#00FF00')
  if max_color is None:
    has_max = False
  last_color = args.get('lc', '#FF0000')
  if last_color is None:
    has_last = False
  if has_min:
     min_pt = coords[results.index(min(results))]
     draw.rectangle([min_pt[0]-1, min_pt[1]-1, min_pt[0]+1, min_pt[1]+1], \
                    fill=min_color)
  if has_max:
     max_pt = coords[results.index(max(results))]
     draw.rectangle([max_pt[0]-1, max_pt[1]-1, max_pt[0]+1, max_pt[1]+1], \
                    fill=max_color)
  if has_last:
     end = coords[-1]
     draw.rectangle([end[0]-1, end[1]-1, end[0]+1, end[1]+1], fill=last_color)
  del draw 
  im.save(fdest, "PNG")

def deleteOldFiles(attdir, maxage=10):
  '''
  Deletes files more than maxage seconds old that have prefix "spkl_" and
  extension ".png".  This purges all the sparklines from attdir, but is only 
  called once per request so won't interfere with multiple sparklines on a page
  
  Parameters
    attdir: folder to examine for sparkline files
    maxage: maximum age of files in seconds (defaults to one day)
  '''
  filelist = os.listdir(attdir)
  test = re.compile("^spkl_.*[.]png", re.IGNORECASE)
  mmax = time.time() - maxage
  for f in filelist:
    if test.search(f):
      f = os.path.join(attdir ,f)
      mtime = os.path.getmtime(f)
      if mtime < mmax:
        try:
          os.remove(f)
        except:
          pass
        
def execute(macro, text):
  '''
  Implementation of MoinMoin Sparklines macro
  '''
  #hash text to use as the attachment name.  Then if text is
  #changed, the sparkline needs to be redone.
  hsh = md5.new(text)
  hfname = "spkl_"+hsh.hexdigest() + ".png"
  request = macro.request
  pagename= macro.formatter.page.page_name
  attach_dir=AttachFile.getAttachDir(request,pagename,create=1)
  
  #check to see if we have already purged files in this request
  if not hasattr(request,'__deletedOldSparkles'):
    deleteOldFiles(attach_dir)
  request.__deletedOldSparkles = True
  
  fname = os.path.join(attach_dir, hfname)
  url=AttachFile.getAttachUrl(pagename,hfname,request)
   
  if not os.path.exists(fname):
    #need to generate image and store it in the attachment folder
    kw = {'h': 20, 'nc': None, 'xc': None, 'lc': None, 's': 1} 
    if text:
      args = text.split(',')
    else:
      args = []
    data = []
    kwargs = []
    for a in args:
      a = a.strip()
      if a.find("=") < 0:
        try:
          data.append(float(a))
        except: 
          pass
      else:
        kwargs.append(a)
    argCount = len(kwargs)
    kwCount = 0
    for a in kwargs:
      if (a.find('=') > -1):
        kwCount = kwCount + 1
        key = a.split('=')
        kw[str(key[0])] = wikiutil.escape(string.join(key[1],''), quote=1)
    argCount = argCount - kwCount
    try:
      plot_sparkline_smooth(data, kw, fname)
    except:
      plot_error(data, kw, fname)
  
  #now generate the image link to the attached sparkline
  return macro.formatter.image(src=url)
  
