"""Auto-update mechanism for django-dataplot images.
Use:
from dataplot.cache import PlotModel
from django.db import models
class YourModel(PlotModel,models.Model):
"""
from django.db.models.base import ModelBase
from django.db.models import *
from django.utils.functional import curry
from copy import copy
import dataplot
import os,re
import pdb
class DataplotImproperlyConfigured(dataplot.PlotError): pass
UNSAFE_FILE_CHARS=re.compile(r'[^a-zA-Z0-9]')
class Dataplot(object):
"""Parsing logic for dataplot autoconfig tuples.
This is just used for DRY convenience here and should not be used
outside this module.
"""
def get_method(self):
"""Construct a data-returning method from the tuple.
"""
return curry(get_plot_args,plot_dict=self.kwargs['plot_dict'],
default_args_map=getattr(self.plot,'default_args_map',{}))
def __init__(self,plot):
"""Initialize a dataplot using sensible defaults.
This can be a
dataplot (subclass of dataplot.GenericPlot, i.e. R.Scatter) in
this case, we assume basename of scatter and a plot method
called get_scatter_args.
tuple (dataplot,dict) where dict is a dictionary of kwargs
used to initialize the dataplot. So you can use this form if
you want to override the default basename, get_plot_args
method, or other plot parameters.
"""
if type(plot)==tuple:
plot,kwargs=plot
else:
kwargs={}
self.plot=plot
self.kwargs=kwargs
# Parse plotargs
plotname=kwargs.setdefault('plotname',plot.__name__)
kwargs.setdefault('attribute',plotname)
get_plot_args=kwargs.get('get_plot_args',plotname)
if type(get_plot_args)==dict:
plot_dict=get_plot_args
methodname=kwargs['attribute']
elif type(get_plot_args)==str:
plot_dict=None
methodname=get_plot_args
else:
raise DataplotImproperlyConfigured(
"get_plot_args must be unassigned, dict, or str")
kwargs['plot_dict']=plot_dict
kwargs['methodname']='%s_args'%methodname
def get_plot_args(self,plot_dict,default_args_map):
new_plot_dict=dict(plot_dict)
qs=getattr(self,'plotable',self.all)()
for k,v in new_plot_dict.iteritems():
try: # to get value list from fields
vals=[getattr(row,v) for row in qs]
new=[callable(val) and val() or val for val in vals]
except: # field not specified
try: # to get a named attribute
attr=getattr(self,v,v)
new=callable(attr) and attr() or attr
except: # not a named attribute, just keep the value
new=v
new_plot_dict[k]=new
for k,v in default_args_map.iteritems(): # set default args if specified
new_plot_dict.setdefault(k,plot_dict[v])
return new_plot_dict
class ModelBase(ModelBase):
"""Extend ModelBase to initialize dataplots.
"""
def __init__(self,*posargs,**kwargs):
"""automatic dataplot construction based on DATAPLOTS syntax
"""
super(ModelBase,self).__init__(*posargs,**kwargs)
print self,self.objects.model._meta.db_table,self._meta.db_table
print self.objects,self.ChangeManipulator.manager,self._default_manager
del self.ChangeManipulator.manager
self.ChangeManipulator.manager=copy(self.objects)
#pdb.set_trace()
self.ChangeManipulator.manager.model=self
# need to change model._meta.db_table
for plotarg in getattr(self,'MANAGER_DATAPLOTS',[]):
dp=Dataplot(plotarg)
setattr(
self.objects.__class__,
dp.kwargs['methodname'],
dp.get_method())
# construct basename default
nameitems=(
dp.kwargs['plotname'],
dp.kwargs['attribute'],
)
name='_'.join([str(x) for x in nameitems if x])
subdir=getattr(
self,'DATAPLOT_SUBDIR',
os.path.join(self.__module__.split('.')[-2],self.__name__))
basename=subdir!=None and os.path.join(subdir,name) or name
inst=dp.plot(
basename,
getattr(self.objects,dp.kwargs['methodname']))
setattr(self.objects,dp.kwargs['attribute'],inst)
class Model(Model):
"""Enables figure autosave with Django-dataplot.
Specify which plots you want to remake by defining an init_plots
method of your models subclass. Also make sure your subclass
inherits from this class first, i.e.
class Location(PlotModel,models.Model):
"""
__metaclass__=ModelBase
def save(self):
"""Save method which allows for maximum configurability.
On a model with no custom save method, we will call django's
save first, then try to make plots for this object.
On a model with a custom save method, you should call
make_plots and Model.save yourself, depending on when it is
appropriate in terms of your data processing.
"""
super(Model,self).save()
self.make_plots()
def make_plots(self):
"""Try to remake plots related to this model.
This includes plots which are attributes of this model, and
model-level plots which are attributes of this model's
manager.
"""
for x in self,self.__class__.objects:
cancel_attr='DO_NOT_CACHE_%s_PLOTS'%(
x.__class__.__bases__[-1].__name__.upper())
if not getattr(self,cancel_attr,None):
for at in x.__dict__.values():
if at and isinstance(at,dataplot.GenericPlot):
at.makefiles()