"""some hooks and views to handle supervising of any data changes:organization: Logilab:copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses"""__docformat__="restructuredtext en"fromcubicwebimportUnknownEidfromcubicweb.selectorsimportnone_rsetfromcubicweb.schemaimportdisplay_namefromcubicweb.viewimportComponentfromcubicweb.mailimportformat_mailfromcubicweb.server.hookimportSendMailOpdeffilter_changes(changes):""" * when an entity has been deleted: * don't show deletion of its relations * don't show related TrInfo deletion if any * when an entity has been added don't show owned_by relation addition * don't show new TrInfo entities if any """# first build an index of changesindex={}added,deleted=set(),set()forchangeinchanges[:]:event,changedescr=changeifevent=='add_entity':entity=changedescr.entityadded.add(entity.eid)ifentity.e_schema=='TrInfo':changes.remove(change)event='change_state'change=(event,(entity.wf_info_for[0],entity.from_state[0],entity.to_state[0]))changes.append(change)elifevent=='delete_entity':deleted.add(changedescr[0])index.setdefault(event,set()).add(change)forkeyin('delete_relation','add_relation'):forchangeinindex.get(key,{}).copy():ifchange[1].rtype=='in_state':index[key].remove(change)# filter changesforeidinadded:try:forchangeinindex['add_relation'].copy():changedescr=change[1]# skip meta-relations which are set automatically# XXX generate list below using rtags (category = 'generated')ifchangedescr.rtypein('created_by','owned_by','is','is_instance_of','from_state','to_state','by_transition','wf_info_for') \andchangedescr.eidfrom==eid:index['add_relation'].remove(change)exceptKeyError:breakforeidindeleted:try:forchangeinindex['delete_relation'].copy():ifchange[1].eidfrom==eid:index['delete_relation'].remove(change)elifchange[1].eidto==eid:index['delete_relation'].remove(change)ifchange[1].rtype=='wf_info_for':forchange_inindex['delete_entity'].copy():ifchange_[1].eidfrom==change[1].eidfrom:index['delete_entity'].remove(change_)exceptKeyError:breakforchangeinchanges:event,changedescr=changeifchangeinindex[event]:yieldchangeclassSupervisionEmailView(Component):"""view implementing the email API for data changes supervision notification """__regid__='supervision_notif'__select__=none_rset()defrecipients(self):returnself._cw.vreg.config['supervising-addrs']defsubject(self):returnself._cw._('[%s supervision] changes summary')%self._cw.vreg.config.appiddefcall(self,changes):user=self._cw.actual_session().userself.w(self._cw._('user %s has made the following change(s):\n\n')%user.login)forevent,changedescrinfilter_changes(changes):self.w(u'* ')getattr(self,event)(changedescr)self.w(u'\n\n')def_entity_context(self,entity):return{'eid':entity.eid,'etype':entity.dc_type().lower(),'title':entity.dc_title()}defadd_entity(self,changedescr):msg=self._cw._('added %(etype)s #%(eid)s (%(title)s)')self.w(u'%s\n'%(msg%self._entity_context(changedescr.entity)))self.w(u' %s'%changedescr.entity.absolute_url())defupdate_entity(self,changedescr):msg=self._cw._('updated %(etype)s #%(eid)s (%(title)s)')self.w(u'%s\n'%(msg%self._entity_context(changedescr.entity)))# XXX print changesself.w(u' %s'%changedescr.entity.absolute_url())defdelete_entity(self,(eid,etype,title)):msg=self._cw._('deleted %(etype)s #%(eid)s (%(title)s)')etype=display_name(self._cw,etype).lower()self.w(msg%locals())defchange_state(self,(entity,fromstate,tostate)):msg=self._cw._('changed state of %(etype)s #%(eid)s (%(title)s)')self.w(u'%s\n'%(msg%self._entity_context(entity)))self.w(_(' from state %(fromstate)s to state %(tostate)s\n'%{'fromstate':_(fromstate.name),'tostate':_(tostate.name)}))self.w(u' %s'%entity.absolute_url())def_relation_context(self,changedescr):_=self._cw._session=self._cw.actual_session()defdescribe(eid):try:return_(session.describe(eid)[0]).lower()exceptUnknownEid:# may occurs when an entity has been deleted from an external# source and we're cleaning its relationreturn_('unknown external entity')eidfrom,rtype,eidto=changedescr.eidfrom,changedescr.rtype,changedescr.eidtoreturn{'rtype':_(rtype),'eidfrom':eidfrom,'frometype':describe(eidfrom),'eidto':eidto,'toetype':describe(eidto)}defadd_relation(self,changedescr):msg=self._cw._('added relation %(rtype)s from %(frometype)s #%(eidfrom)s to %(toetype)s #%(eidto)s')self.w(msg%self._relation_context(changedescr))defdelete_relation(self,changedescr):msg=self._cw._('deleted relation %(rtype)s from %(frometype)s #%(eidfrom)s to %(toetype)s #%(eidto)s')self.w(msg%self._relation_context(changedescr))classSupervisionMailOp(SendMailOp):"""special send email operation which should be done only once for a bunch of changes """def_get_view(self):returnself.session.vreg['components'].select('supervision_notif',self.session)def_prepare_email(self):session=self.sessionconfig=session.vreg.configuinfo={'email':config['sender-addr'],'name':config['sender-name']}view=self._get_view()content=view.render(changes=session.transaction_data.get('pendingchanges'))recipients=view.recipients()msg=format_mail(uinfo,recipients,content,view.subject(),config=config)self.to_send=[(msg,recipients)]defcommit_event(self):self._prepare_email()SendMailOp.commit_event(self)