"""dynamically generated image views:organization: Logilab:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr"""__docformat__="restructuredtext en"importosfromtempfileimportmktempfromitertoolsimportcyclefromlogilab.common.graphimportescape,GraphGenerator,DotBackendfromyamsimportschema2dotass2dfromcubicweb.common.viewimportEntityView,StartupViewclassRestrictedSchemaDotPropsHandler(s2d.SchemaDotPropsHandler):def__init__(self,req):# FIXME: colors are arbitraryself.nextcolor=cycle(('#aa0000','#00aa00','#0000aa','#000000','#888888')).nextself.req=reqdefdisplay_attr(self,rschema):returnnotrschema.metaand(rschema.has_local_role('read')orrschema.has_perm(self.req,'read'))# XXX remove this method once yams > 0.20 is outdefnode_properties(self,eschema):"""return default DOT drawing options for an entity schema"""label=['{',eschema.type,'|']label.append(r'\l'.join(rel.typeforrelineschema.subject_relations()ifrel.finalandself.display_attr(rel)))label.append(r'\l}')# trailing \l ensure alignement of the last onereturn{'label':''.join(label),'shape':"record",'fontname':"Courier",'style':"filled"}defedge_properties(self,rschema,subjnode,objnode):kwargs=super(RestrictedSchemaDotPropsHandler,self).edge_properties(rschema,subjnode,objnode)# symetric rels are handled differently, let yams decide what's bestifnotrschema.symetric:kwargs['color']=self.nextcolor()kwargs['fontcolor']=kwargs['color']# dot label decoration is just awful (1 line underlining the label# + 1 line going to the closest edge spline point)kwargs['decorate']='false'returnkwargsclassRestrictedSchemaVisitorMiIn:def__init__(self,req,*args,**kwargs):# hack hack hackassertlen(self.__class__.__bases__)==2self.__parent=self.__class__.__bases__[1]self.__parent.__init__(self,*args,**kwargs)self.req=reqdefnodes(self):foretype,eschemainself.__parent.nodes(self):ifeschema.has_local_role('read')oreschema.has_perm(self.req,'read'):yieldeschema.type,eschemadefedges(self):forsetype,oetype,rschemainself.__parent.edges(self):ifrschema.has_local_role('read')orrschema.has_perm(self.req,'read'):yieldsetype,oetype,rschemaclassFullSchemaVisitor(RestrictedSchemaVisitorMiIn,s2d.FullSchemaVisitor):passclassOneHopESchemaVisitor(RestrictedSchemaVisitorMiIn,s2d.OneHopESchemaVisitor):passclassOneHopRSchemaVisitor(RestrictedSchemaVisitorMiIn,s2d.OneHopRSchemaVisitor):passclassTmpFileViewMixin(object):binary=Truecontent_type='application/octet-stream'cache_max_age=60*60*2# stay in http cache for 2 hours by default defcall(self):self.cell_call()defcell_call(self,row=0,col=0):self.row,self.col=row,col# in case one need ittmpfile=mktemp('.png')try:self._generate(tmpfile)self.w(open(tmpfile).read())finally:os.unlink(tmpfile)classSchemaImageView(TmpFileViewMixin,StartupView):id='schemagraph'content_type='image/png'skip_rels=('owned_by','created_by','identity','is','is_instance_of')def_generate(self,tmpfile):"""display global schema information"""skipmeta=notint(self.req.form.get('withmeta',0))visitor=FullSchemaVisitor(self.req,self.schema,skiprels=self.skip_rels,skipmeta=skipmeta)s2d.schema2dot(outputfile=tmpfile,visitor=visitor,prophdlr=RestrictedSchemaDotPropsHandler(self.req))classEETypeSchemaImageView(TmpFileViewMixin,EntityView):id='eschemagraph'content_type='image/png'accepts=('EEType',)skip_rels=('owned_by','created_by','identity','is','is_instance_of')def_generate(self,tmpfile):"""display schema information for an entity"""entity=self.entity(self.row,self.col)eschema=self.vreg.schema.eschema(entity.name)visitor=OneHopESchemaVisitor(self.req,eschema,skiprels=self.skip_rels)s2d.schema2dot(outputfile=tmpfile,visitor=visitor,prophdlr=RestrictedSchemaDotPropsHandler(self.req))classERTypeSchemaImageView(EETypeSchemaImageView):accepts=('ERType',)def_generate(self,tmpfile):"""display schema information for an entity"""entity=self.entity(self.row,self.col)rschema=self.vreg.schema.rschema(entity.name)visitor=OneHopRSchemaVisitor(self.req,rschema)s2d.schema2dot(outputfile=tmpfile,visitor=visitor,prophdlr=RestrictedSchemaDotPropsHandler(self.req))classWorkflowDotPropsHandler(object):def__init__(self,req):self._=req._defnode_properties(self,stateortransition):"""return default DOT drawing options for a state or transition"""props={'label':stateortransition.name,'fontname':'Courier'}ifhasattr(stateortransition,'state_of'):props['shape']='box'props['style']='filled'ifstateortransition.reverse_initial_state:props['color']='#88CC88'else:props['shape']='ellipse'descr=[]tr=stateortransitioniftr.require_group:descr.append('%s%s'%(self._('groups:'),','.join(g.nameforgintr.require_group)))iftr.condition:descr.append('%s%s'%(self._('condition:'),tr.condition))ifdescr:props['label']+=escape('\n'.join(descr))returnpropsdefedge_properties(self,transition,fromstate,tostate):return{'label':'','dir':'forward','color':'black','style':'filled'}classWorkflowVisitor:def__init__(self,entity):self.entity=entitydefnodes(self):forstateinself.entity.reverse_state_of:state.complete()yieldstate.eid,statefortransitioninself.entity.reverse_transition_of:transition.complete()yieldtransition.eid,transitiondefedges(self):fortransitioninself.entity.reverse_transition_of:forincomingstateintransition.reverse_allowed_transition:yieldincomingstate.eid,transition.eid,transitionyieldtransition.eid,transition.destination().eid,transitionclassEETypeWorkflowImageView(TmpFileViewMixin,EntityView):id='ewfgraph'content_type='image/png'accepts=('EEType',)def_generate(self,tmpfile):"""display schema information for an entity"""entity=self.entity(self.row,self.col)visitor=WorkflowVisitor(entity)prophdlr=WorkflowDotPropsHandler(self.req)generator=GraphGenerator(DotBackend('workflow','LR',ratio='compress',size='30,12'))returngenerator.generate(visitor,prophdlr,tmpfile)