source: indico/indico/MaKaC/review.py @ e8695c

hello-world-walkthroughipv6v0.98-seriesv0.98.2v0.98.3v0.98b2v0.99v1.0v1.1
Last change on this file since e8695c was e8695c, checked in by Jose Benito <jose.benito.gonzalez@…>, 19 months ago

[FIX] update index when changing email

  • Property mode set to 100644
File size: 122.7 KB
Line 
1# -*- coding: utf-8 -*-
2##
3##
4## This file is part of CDS Indico.
5## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 CERN.
6##
7## CDS Indico is free software; you can redistribute it and/or
8## modify it under the terms of the GNU General Public License as
9## published by the Free Software Foundation; either version 2 of the
10## License, or (at your option) any later version.
11##
12## CDS Indico is distributed in the hope that it will be useful, but
13## WITHOUT ANY WARRANTY; without even the implied warranty of
14## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15## General Public License for more details.
16##
17## You should have received a copy of the GNU General Public License
18## along with CDS Indico; if not, write to the Free Software Foundation, Inc.,
19## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
20
21from copy import copy
22from pytz import timezone
23from indico.util.i18n import i18nformat
24import ZODB
25from persistent import Persistent
26from persistent.list import PersistentList
27from BTrees.OOBTree import OOBTree, intersection, union
28from BTrees.IOBTree import IOBTree
29import BTrees.OIBTree as OIBTree
30from datetime import datetime,timedelta
31import MaKaC
32from MaKaC.common.Counter import Counter
33from MaKaC.errors import MaKaCError
34from MaKaC.accessControl import AdminList
35from MaKaC.trashCan import TrashCanManager
36from MaKaC.common.timezoneUtils import nowutc
37from MaKaC.i18n import _
38from MaKaC.common import Config
39import tempfile
40
41
42class AbstractSorter:
43    pass
44
45class AbstractFilter:
46    pass
47
48
49class _AbstractParticipationIndex(Persistent):
50    """This class allows to index abstract participations (submitters)
51        for a single CFA process; this means that clients will be able to
52        efficiently perform queries of the type "give me all the abstracts
53        in which a certain registered user is implied".
54       For being able to perform this indexing, it is supposed that the Avatar
55        identifier is unique among other avatars and that it cannot change.
56       This index must be maintained by clients (i.e. the CFAMgr) as it doesn't
57        keep track of the changes on Participantons.
58       The key of the index is the Avatar and the values the different
59        Participations that user has within the current CFA process. For
60        performance reasons, the Avatar id will be used as index key (using the
61        whole Avatar object would make the index bigger and as the Avatar id
62        cannot change it's enough); the clients would have to keep the
63        integrity of the index.
64    """
65
66    def __init__( self ):
67        self._idx = OOBTree()
68
69    def index( self, participation ):
70        """Add a new participation to the index
71        """
72        #if the Participation is not linked to an Avatar there's no point to
73        #   index it
74        a = participation.getAvatar()
75        if not a:
76            return
77        #ToDo: if the Participation corresponds to an abstract which doesn't
78        #   correspond to the current CFAMgr, then an error must be raised
79
80        if not self._idx.has_key( a.getId() ):
81            self._idx[a.getId()] = PersistentList()
82        #if the participation is already in the index, no need for adding it
83        if participation in self._idx[a.getId()]:
84            return
85        self._idx[a.getId()].append( participation )
86
87    def unindex( self, participation ):
88        """Remove an existing participation from the index
89        """
90        #if the Participation is not linked to an Avatar there's no point to
91        #   unindex it
92        a = participation.getAvatar()
93
94        if not a:
95            return
96        #if the Avatar associated to the participation isn't in the index do
97        #   nothing
98        if not self._idx.has_key( a.getId() ):
99            return
100        #if the given participation is indexed remove it, otherwise do nothing
101        if participation in self._idx[a.getId()]:
102             self._idx[a.getId()].remove( participation )
103
104    def getParticipationList( self, av ):
105        try:
106            return self._idx[ av.getId() ]
107        except KeyError, e:
108            return []
109
110
111class AbstractParticipation(Persistent):
112
113    def __init__( self, abstract, **data ):
114        self._abstract = abstract
115        self._firstName = ""
116        self._surName = ""
117        self._email = ""
118        self._affilliation = ""
119        self._address = ""
120        self._telephone = ""
121        self._fax = ""
122        self._title = ""
123        self.setData( **data )
124
125    def setFromAvatar( self, av ):
126        data = {"title": av.getTitle(), \
127                "firstName": av.getName(), \
128                "surName": av.getSurName(), \
129                "email": av.getEmail(), \
130                "affiliation": av.getOrganisation(), \
131                "address": av.getAddress(), \
132                "telephone": av.getTelephone(), \
133                "fax": av.getFax() }
134        self.setData( **data )
135
136    def setFromAbstractParticipation(self,part):
137        data = {"title": part.getTitle(), \
138                "firstName": part.getFirstName(), \
139                "surName": part.getSurName(), \
140                "email": part.getEmail(), \
141                "affiliation": part.getAffiliation(), \
142                "address": part.getAddress(), \
143                "telephone": part.getTelephone(), \
144                "fax": part.getFax() }
145        self.setData( **data )
146
147    def setData( self, **data ):
148        if "firstName" in data:
149            self.setFirstName( data["firstName"] )
150        if "surName" in data:
151            self.setSurName( data["surName"] )
152        if "email" in data:
153            self.setEmail( data["email"] )
154        if "affiliation" in data:
155            self.setAffiliation( data["affiliation"] )
156        if "address" in data:
157            self.setAddress( data["address"] )
158        if "telephone" in data:
159            self.setTelephone( data["telephone"] )
160        if "fax" in data:
161            self.setFax( data["fax"] )
162        if "title" in data:
163            self.setTitle( data["title"] )
164    setValues = setData
165
166    def getData(self):
167        data = {}
168        data["firstName"] = self.getFirstName()
169        data["surName"] = self.getSurName()
170        data["email"] = self.getEmail()
171        data["affiliation"] = self.getAffiliation()
172        data["address"] = self.getAddress()
173        data["telephone"] = self.getTelephone()
174        data["fax"] = self.getFax()
175        data["title"] = self.getTitle()
176
177        return data
178    getValues = getData
179
180    def clone (self, abstract):
181        ap = AbstractParticipation(abstract,self.getData())
182        return ap
183
184    def _notifyModification(self):
185        self._abstract._notifyModification()
186
187    def _unindex(self):
188        abs=self.getAbstract()
189        if abs is not None:
190            mgr=abs.getOwner()
191            if mgr is not None:
192                mgr.unindexAuthor(self)
193
194    def _index(self):
195        abs=self.getAbstract()
196        if abs is not None:
197            mgr=abs.getOwner()
198            if mgr is not None:
199                mgr.indexAuthor(self)
200
201    def setFirstName( self, name ):
202        tmp=name.strip()
203        if tmp==self.getFirstName():
204            return
205        self._unindex()
206        self._firstName=tmp
207        self._index()
208        self._notifyModification()
209
210    def getFirstName( self ):
211        return self._firstName
212
213    def getName( self ):
214        return self._firstName
215
216    def setSurName( self, name ):
217        tmp=name.strip()
218        if tmp==self.getSurName():
219            return
220        self._unindex()
221        self._surName=tmp
222        self._index()
223        self._notifyModification()
224
225    def getSurName( self ):
226        return self._surName
227
228    def getFamilyName( self ):
229        return self._surName
230
231    def setEmail( self, email ):
232        email = email.strip().lower()
233        if email != self.getEmail():
234            self._unindex()
235            self._email = email
236            self._index()
237            self._notifyModification()
238
239    def getEmail( self ):
240        return self._email
241
242    def setAffiliation( self, af ):
243        self._affilliation = af.strip()
244        self._notifyModification()
245
246    setAffilliation=setAffiliation
247
248    def getAffiliation( self ):
249        return self._affilliation
250
251    def setAddress( self, address ):
252        self._address = address.strip()
253        self._notifyModification()
254
255    def getAddress( self ):
256        return self._address
257
258    def setTelephone( self, telf ):
259        self._telephone = telf.strip()
260        self._notifyModification()
261
262    def getTelephone( self ):
263        return self._telephone
264
265    def setFax( self, fax ):
266        self._fax = fax.strip()
267        self._notifyModification()
268
269    def getFax(self):
270        return self._fax
271
272    def setTitle( self, title ):
273        self._title = title.strip()
274        self._notifyModification()
275
276    def getTitle( self ):
277        return self._title
278
279    def getFullName( self ):
280        res = self.getSurName().decode('utf-8').upper().encode('utf-8')
281        tmp=[]
282        for name in self.getFirstName().lower().split(" "):
283            if name.strip() == "":
284                continue
285            name=name.strip()
286            tmp.append("%s%s"%(name[0].upper(),name[1:]))
287        firstName=" ".join(tmp)
288        if firstName != "":
289            res = "%s, %s"%( res, firstName )
290        if self.getTitle() != "":
291            res = "%s %s"%( self.getTitle(), res )
292        return res
293
294    def getStraightFullName(self):
295        name = ""
296        if self.getName() != "":
297            name = "%s "%self.getName()
298        return "%s%s"%(name, self.getSurName())
299
300    def getAbrName(self):
301        res = self.getSurName()
302        if self.getFirstName() != "":
303            if res != "":
304                res = "%s, "%res
305            res = "%s%s."%(res, self.getFirstName()[0].upper())
306        return res
307
308    def getAbstract( self ):
309        return self._abstract
310
311    def setAbstract(self, abs):
312        self._abstract = abs
313
314    def delete( self ):
315        self._abstract = None
316        TrashCanManager().add(self)
317
318    def recover(self):
319        TrashCanManager().remove(self)
320
321
322class Author( AbstractParticipation ):
323
324    def __init__( self, abstract, **data ):
325        AbstractParticipation.__init__( self, abstract, **data )
326        self._abstractId = ""
327
328    def getId( self ):
329        return self._id
330
331    def setId( self, newId ):
332        self._id = str( newId )
333
334    def clone(self, abstract):
335        auth = Author(abstract, self.getData())
336        return auth
337
338    def isSpeaker(self):
339        return self._abstract.isSpeaker(self)
340
341class Submitter( AbstractParticipation ):
342
343    def __init__( self, abstract, av ):
344        if av == None:
345            raise MaKaCError( _("abstract submitter cannot be None") )
346        AbstractParticipation.__init__( self, abstract )
347        self._user = None
348        self._setUser( av )
349        self.setFromAvatar( av )
350
351    def _setUser( self, av ):
352        if self.getUser() == av:
353            return
354        #if currently there's an association with a registered user, we notify
355        #   the unidexation of the participation
356        if self.getUser():
357            self.getAbstract().getOwner().unregisterParticipation( self )
358        self._user = av
359        #if the participation is associated to any avatar, we make the
360        #   association and index it
361        if self.getUser():
362            self.getAbstract().getOwner().registerParticipation( self )
363
364    def clone(self, abstract):
365        sub = Submitter(abstract,self.getAvatar())
366        sub.setData(self.getData())
367        return sub
368
369    def getUser( self ):
370        return self._user
371
372    def getAvatar( self ):
373        return self._user
374
375    def representsUser( self, av ):
376        return self.getUser() == av
377
378
379class _AuthIdx(Persistent):
380
381    def __init__(self,mgr):
382        self._mgr=mgr
383        self._idx=OOBTree()
384
385    def _getKey(self,auth):
386        return "%s %s"%(auth.getSurName().lower(),auth.getFirstName().lower())
387
388    def index(self,auth):
389        if auth.getAbstract() is None:
390            raise MaKaCError( _("cannot index an author of an abstract which is not included in a conference"))
391        if auth.getAbstract().getOwner()!=self._mgr:
392            raise MaKaCError( _("cannot index an author of an abstract which does not belong to this conference"))
393        key=self._getKey(auth)
394        abstractId=str(auth.getAbstract().getId())
395        if not self._idx.has_key(key):
396            self._idx[key]=OIBTree.OIBTree()
397        if not self._idx[key].has_key(abstractId):
398            self._idx[key][abstractId]=0
399        self._idx[key][abstractId]+=1
400
401    def unindex(self,auth):
402        if auth.getAbstract() is None:
403            raise MaKaCError( _("cannot unindex an author of an abstract which is not included in a conference"))
404        if auth.getAbstract().getOwner()!=self._mgr:
405            raise MaKaCError( _("cannot unindex an author of an abstract which does not belong to this conference"))
406        key=self._getKey(auth)
407        if not self._idx.has_key(key):
408            return
409        abstractId=str(auth.getAbstract().getId())
410        if abstractId not in self._idx[key]:
411            return
412        self._idx[key][abstractId]-=1
413        if self._idx[key][abstractId]<=0:
414            del self._idx[key][abstractId]
415        if len(self._idx[key])<=0:
416            del self._idx[key]
417
418    def match(self,query):
419        query=query.lower().strip()
420        res=OIBTree.OISet()
421        for k in self._idx.keys():
422            if k.find(query)!=-1:
423                res=OIBTree.union(res,self._idx[k])
424        return res
425
426
427class _PrimAuthIdx(_AuthIdx):
428
429    def __init__(self,mgr):
430        _AuthIdx.__init__(self,mgr)
431        for abs in self._mgr.getAbstractList():
432            for auth in abs.getPrimaryAuthorList():
433                self.index(auth)
434
435class _AuthEmailIdx(_AuthIdx):
436
437    def __init__(self, mgr):
438        _AuthIdx.__init__(self, mgr)
439        for abs in self._mgr.getAbstractList():
440            for auth in abs.getPrimaryAuthorList():
441                self.index(auth)
442            for auth in abs.getCoAuthorList():
443                self.index(auth)
444
445    def _getKey(self, auth):
446        return auth.getEmail().lower()
447
448class AbstractField(Persistent):
449    _fieldTypes = [ 'input', 'textarea' ]
450
451    def __init__(self, id, name, caption, maxlength=0, isMandatory=False, type="textarea", limitation="chars"):
452        self._id = id
453        self._name = name
454        self._caption = caption
455        self._active = True
456        self._maxLength = maxlength
457        self._isMandatory = isMandatory
458        self._type = type
459        self._limitation = limitation # possible values: chars, words
460
461    def clone(self):
462        af = AbstractField(self.getId(),self.getName(),self.getCaption(),self.getMaxLength(), self.isMandatory(), self.getType(), self.getLimitation())
463        return af
464
465    def _notifyModification(self):
466        self._p_changed = 1
467
468    def getType(self):
469        try:
470            return self._type
471        except:
472            self._type = "textarea"
473            return self._type
474
475    def setType(self, type):
476        self._type = type
477
478    def getLimitation(self):
479        try:
480            return self._limitation
481        except:
482            self._limitation = "chars"
483            return self._limitation
484
485    def setLimitation(self, limitation):
486        self._limitation = limitation
487
488    def isMandatory(self):
489        try:
490            return self._isMandatory
491        except:
492            self._isMandatory = False
493            return self._isMandatory
494
495    def setMandatory(self, isMandatory=False):
496        self._isMandatory = isMandatory
497        self._notifyModification()
498
499    def getMaxLength(self):
500        try:
501            return self._maxLength
502        except:
503            self._maxLength=0
504            return self._maxLength
505
506    def setValues(self, name, caption, maxlength, isMandatory, fieldType, fieldLimitation):
507        self._name = name
508        self._caption = caption
509        self._maxLength = maxlength
510        self._isMandatory = isMandatory
511        self._type = fieldType
512        self._limitation = fieldLimitation
513        self._notifyModification()
514
515    def setMaxLength(self, maxLength=0):
516        self._maxLength = maxLength
517        self._notifyModification()
518
519    def getId(self):
520        return self._id
521
522    def setId(self, id):
523        self._id=id
524        self._notifyModification()
525
526    def getName(self):
527        try:
528            if self._name:
529                pass
530        except AttributeError, e:
531            try:
532                if int(self._id):
533                    pass
534                self._name = ""
535            except ValueError, er:
536                self._name = self._id
537        return self._name
538
539    def setName(self, name):
540        self._name=name
541        self._notifyModification()
542
543    def getCaption(self):
544        return self._caption
545
546    def setCaption(self, caption):
547        self._caption=caption
548        self._notifyModification()
549
550    def isActive(self):
551        return self._active
552
553    def setActive(self, active):
554        self._active=active
555        self._notifyModification()
556
557class AbstractFieldsMgr(Persistent):
558
559    def __init__(self):
560        self._fields=self._initFields()
561        self.__fieldGenerator = Counter()
562
563    def clone(self):
564        afm = AbstractFieldsMgr()
565        for f in self.getFields():
566            afm._addField(f.clone())
567        return afm
568
569    def getFieldGenerator(self):
570        try:
571            if self.__fieldGenerator:
572                pass
573        except AttributeError,e:
574            self.__fieldGenerator = Counter()
575        return self.__fieldGenerator
576
577    def _notifyModification(self):
578        self._p_changed = 1
579
580    def _initFields(self):
581        d=[]
582        su=AbstractField("content", "Content", "Abstract content", 0, True)
583        d.append(su)
584        su=AbstractField("summary", "Summary", "Summary",0)
585        d.append(su)
586        return d
587
588    def hasField(self, id):
589        for f in self._fields:
590            if f.getId() == id:
591                return True
592        return False
593
594    def getFields(self):
595        if not self.hasField("content"):
596            ac=AbstractField("content","Content", _("Abstract content"),0,True)
597            self._fields.insert(0, ac)
598        return self._fields
599
600    def getActiveFields(self):
601        fl = []
602        for f in self.getFields():
603            if f.isActive():
604                fl.append(f)
605        return fl
606
607    def hasActiveField(self, id):
608        return self.hasField(id) and self.getFieldById(id).isActive()
609
610    def hasAnyActiveField( self ):
611        for f in self._fields:
612            if f.isActive():
613                return True
614        return False
615
616    def enableField(self, id):
617        if self.hasField(id):
618            self.getFieldById(id).setActive(True)
619            self._notifyModification()
620
621    def disableField(self, id):
622        if self.hasField(id):
623            self.getFieldById(id).setActive(False)
624            self._notifyModification()
625
626    def getFieldKeys(self):
627        keys = []
628        for f in self._fields:
629            keys.append(f.getId())
630        return keys
631
632    def getFieldById(self, id):
633        for f in self._fields:
634            if f.getId() == id:
635                return f
636        return None
637
638    def _addField(self, field):
639        self._fields.append(field)
640
641    def addField(self, id, name, caption, maxlength=0, isMandatory=False, fieldType="textarea", fieldLimitation="chars"):
642        if self.hasField(id):
643            self.getFieldById(id).setValues(name, caption, maxlength, isMandatory, fieldType, fieldLimitation)
644        else:
645            id=str(self.getFieldGenerator().newCount())
646            absf = AbstractField(id, name, caption, maxlength, isMandatory, fieldType, fieldLimitation)
647            self._fields.append(absf)
648        self._notifyModification()
649        return id
650
651    def removeField(self, id):
652        if self.hasField(id):
653            self._fields.remove(self.getFieldById(id))
654            self._notifyModification()
655
656    def moveAbsFieldUp(self, id):
657        if self.hasField(id):
658            f = self.getFieldById(id)
659            idx = self._fields.index(f)
660            self._fields.remove(f)
661            if idx == 0:
662                self._fields.append(f)
663            else:
664                self._fields.insert(idx-1,f)
665            self._notifyModification()
666
667    def moveAbsFieldDown(self, id):
668        if self.hasField(id):
669            f = self.getFieldById(id)
670            idx = self._fields.index(f)
671            self._fields.remove(f)
672            if idx == len(self._fields):
673                self._fields.insert(0,f)
674            else:
675                self._fields.insert(idx+1,f)
676            self._notifyModification()
677
678class AbstractMgr(Persistent):
679
680    def __init__(self, owner):
681        self._owner = owner
682        self._abstracts = OOBTree()
683        self._participationIdx = _AbstractParticipationIndex()
684        self.__abstractGenerator = Counter()
685        self._activated = False
686        self.setStartSubmissionDate( datetime.now() )
687        self.setEndSubmissionDate( datetime.now() )
688##        self._contribTypes = PersistentList()
689        self.setAnnouncement( "" )
690        self._notifTpls=IOBTree()
691        self._notifTplsOrder=PersistentList()
692        self.__notifTplsCounter=Counter()
693        self._authorizedSubmitter = PersistentList()
694        self._primAuthIdx =_PrimAuthIdx(self)
695        self._authEmailIdx =_AuthEmailIdx(self)
696        self._abstractFieldsMgr=AbstractFieldsMgr()
697        self._submissionNotification=SubmissionNotification()
698        self._multipleTracks = True
699        self._tracksMandatory = False
700        self._attachFiles = False
701
702    def getMultipleTracks( self ):
703        try:
704            return self._multipleTracks
705        except:
706            self.setMultipleTracks( True )
707            return self._multipleTracks
708
709    def setMultipleTracks( self, multipleTracks = True ):
710        self._multipleTracks = multipleTracks
711
712    def areTracksMandatory( self ):
713        try:
714            return self._tracksMandatory
715        except:
716            self.setTracksMandatory( False )
717            return self._tracksMandatory
718
719    def canAttachFiles(self):
720        try:
721            return self._attachFiles
722        except:
723            self.setAllowAttachFiles(False)
724            return self._attachFiles
725
726    def setAllowAttachFiles(self, attachedFiles):
727        self._attachFiles = attachedFiles
728
729    def setTracksMandatory( self, tracksMandatory = False ):
730        self._tracksMandatory = tracksMandatory
731
732    def getAbstractFieldsMgr(self):
733        try:
734            return self._abstractFieldsMgr
735        except:
736            self._abstractFieldsMgr=AbstractFieldsMgr()
737            return self._abstractFieldsMgr
738
739    def clone(self, conference):
740        amgr = AbstractMgr(conference)
741        amgr._abstractFieldsMgr = self.getAbstractFieldsMgr().clone()
742        amgr.setAnnouncement(self.getAnnouncement())
743
744        timeDifference = conference.getStartDate() - self.getOwner().getStartDate()
745        amgr.setStartSubmissionDate(self.getStartSubmissionDate() + timeDifference)
746        amgr.setEndSubmissionDate(self.getEndSubmissionDate() + timeDifference)
747
748        modifDeadline = self.getModificationDeadline()
749        if modifDeadline is not None :
750            amgr.setModificationDeadline(self.getModificationDeadline() + timeDifference)
751
752        amgr.setActive(self.isActive())
753        if self.getCFAStatus() :
754            amgr.activeCFA()
755        else :
756            amgr.desactiveCFA()
757
758        for a in self.getAbstractList() :
759            amgr.addAbstract(a.clone(conference, amgr._generateNewAbstractId()))
760
761        for tpl in self.getNotificationTplList() :
762            amgr.addNotificationTpl(tpl.clone())
763
764        # Cloning submission notification:
765        amgr.setSubmissionNotification(self.getSubmissionNotification().clone())
766
767        return amgr
768
769    def getOwner(self):
770        return self._owner
771    getConference = getOwner
772
773    def getTimezone(self):
774        return self.getConference().getTimezone()
775
776    def activeCFA(self):
777        self._activated = True
778
779    def desactiveCFA(self):
780        self._activated = False
781
782    def getAuthorizedSubmitterList(self):
783        try:
784            return self._authorizedSubmitter
785        except AttributeError:
786            self._authorizedSubmitter = PersistentList()
787            return self._authorizedSubmitter
788
789    def addAuthorizedSubmitter(self, av):
790        try:
791            if self._authorizedSubmitter:
792                pass
793        except AttributeError:
794            self._authorizedSubmitter = PersistentList()
795        if not av in self._authorizedSubmitter:
796            self._authorizedSubmitter.append(av)
797            av.linkTo(self, "abstractSubmitter")
798
799    def removeAuthorizedSubmitter(self, av):
800        try:
801            if self._authorizedSubmitter:
802                pass
803        except:
804            self._authorizedSubmitter = PersistentList()
805        if av in self._authorizedSubmitter:
806            self._authorizedSubmitter.remove(av)
807            av.unlinkTo(self, "abstractSubmitter")
808
809    def getCFAStatus(self):
810        return self._activated
811
812    def setActive(self, value):
813        if value:
814            self.activeCFA()
815        else:
816            self.desactiveCFA()
817
818    def isActive(self):
819        return self._activated
820
821    def setStartSubmissionDate(self, date):
822        self._submissionStartDate = datetime(date.year,date.month,date.day,0,0,0)
823
824    def getStartSubmissionDate(self):
825        return timezone(self.getTimezone()).localize(self._submissionStartDate)
826
827    def setEndSubmissionDate(self, date):
828        self._submissionEndDate = datetime(date.year,date.month,date.day,23,59,59)
829
830    def getEndSubmissionDate(self):
831        return timezone(self.getTimezone()).localize(self._submissionEndDate)
832
833    def inSubmissionPeriod( self, date = None ):
834        if date is None:
835            date=nowutc()
836        sd = self.getStartSubmissionDate()
837        ed = self.getEndSubmissionDate()
838        return date <= ed and date >= sd
839
840    def getModificationDeadline( self ):
841        """Returns the deadline for modifications on the submitted abstracts.
842        """
843        try:
844            if self._modifDeadline:
845                pass
846        except AttributeError, e:
847            self._modifDeadline = None
848        if self._modifDeadline != None:
849            return timezone(self.getTimezone()).localize(self._modifDeadline)
850        else:
851            return None
852
853    def setModificationDeadline( self, newDL ):
854        """Sets a new deadline for modifications on the submitted abstracts.
855        """
856        if newDL != None:
857            self._modifDeadline = datetime( newDL.year,newDL.month,newDL.day,23,59,59)
858        else:
859            self._modifDeadline = newDL
860
861    def inModificationPeriod( self, date=None ):
862        """Tells whether is possible to modify a submitted abstract in a
863            certain date.
864        """
865        if date is None:
866            date=nowutc()
867        if not self.getModificationDeadline():
868            return True
869        return date <= self.getModificationDeadline()
870
871    def getAnnouncement( self ):
872        #to be removed
873        try:
874            if self._announcement:
875                pass
876        except AttributeError, e:
877            self._announcement = ""
878
879        return self._announcement
880
881    def setAnnouncement( self, newAnnouncement ):
882        self._announcement = newAnnouncement.strip()
883
884##    def addContribType(self, type):
885##        type = type.strip()
886##        if type == "":
887##            raise MaKaCError("Cannot add an empty contribution type")
888##        self._contribTypes.append(type)
889##
890##    def removeContribType(self, type):
891##        if type in self._contribTypes:
892##            self._contribTypes.remove(type)
893##
894##    def getContribTypeList(self):
895##        return self._contribTypes
896
897    def _generateNewAbstractId( self ):
898        """Returns a new unique identifier for the current conference
899            contributions
900        """
901        #instead of having a own counter, the abstract manager will request
902        #   abstract ids to the conference which will ensure a unique id
903        #   which will persist afterwards when an abstract is accepted
904        return str(self.getConference().genNewAbstractId())
905
906    def _getOldAbstractCounter(self):
907        return self.__abstractGenerator._getCount()
908
909    def newAbstract( self, av, **data ):
910        """Creates a new abstract under this manager
911        """
912        id = self._generateNewAbstractId()
913        a = Abstract( self, id, av, **data )
914        self._abstracts[ id ] = a
915        for auth in a.getPrimaryAuthorList():
916            self.indexAuthor(auth)
917        return a
918
919    def addAbstract(self, abstract):
920        if abstract in self.getAbstractList():
921            return
922        if isinstance(abstract.getCurrentStatus(),AbstractStatusWithdrawn):
923            raise MaKaCError(  _("Cannot add an abstract which has been withdrawn"), ("Event"))
924        abstract._setOwner(self)
925        self._abstracts[abstract.getId()] = abstract
926        for auth in abstract.getPrimaryAuthorList():
927            self.indexAuthor(auth)
928
929    def removeAbstract( self, abstract ):
930        if self._abstracts.has_key( abstract.getId() ):
931            #for auth in abstract.getPrimaryAuthorList():
932            #    self.unindexAuthor(auth)
933            # * Remove dependencies with another abstracts:
934            #       - If it's an accepted abstract-->remove abstract from contribution
935            if isinstance(abstract.getCurrentStatus(), AbstractStatusAccepted):
936                raise MaKaCError( _("Cannot remove an accepted abstract before removing the contribution linked to it"))
937            # If it's a withdrawn abstract-->remove abstract from contribution
938            if isinstance(abstract.getCurrentStatus(), AbstractStatusWithdrawn) and abstract.getContribution():
939                raise MaKaCError( _("Cannot remove the abstract before removing the contribution linked to it"))
940            for abs in self._abstracts.values():
941                if abs != abstract:
942                    st = abs.getCurrentStatus()
943                    if isinstance(st, AbstractStatusDuplicated):
944                        #if the abstract to delete is the orginal in another "duplicated", change status to submitted
945                        if st.getOriginal() == abstract:
946                            abs.setCurrentStatus(AbstractStatusSubmitted(abs))
947                    elif isinstance(st, AbstractStatusMerged):
948                        #if the abstract to delete is the target one in another "merged", change status to submitted
949                        if st.getTargetAbstract() == abstract:
950                            abs.setCurrentStatus(AbstractStatusSubmitted(abs))
951            #unindex participations!!!
952            self.unregisterParticipation(abstract.getSubmitter())
953            del self._abstracts[ abstract.getId() ]
954            abstract.delete()
955
956    def recoverAbstract(self, abstract):
957        self.addAbstract(abstract)
958        abstract.recoverFromTrashCan()
959    def getAbstractList(self):
960        return self._abstracts.values()
961
962    def getAbstractById(self, id):
963        return self._abstracts.get(str(id),None)
964
965    def registerParticipation( self, p ):
966        self._participationIdx.index( p )
967
968    def unregisterParticipation( self, p ):
969        self._participationIdx.unindex( p )
970
971    def getAbstractListForAvatar( self, av ):
972        try:
973            if self._participationIdx:
974                pass
975        except AttributeError, e:
976            self._participationIdx = self._partipationIdx
977            self._partipationIdx = None
978        res = []
979        for participation in self._participationIdx.getParticipationList( av ):
980            abstract = participation.getAbstract()
981            if abstract is not None and abstract.isSubmitter( av ):
982                if abstract not in res:
983                    res.append( abstract )
984        return res
985
986    def getAbstractListForAuthorEmail(self, email):
987        """ Get list of abstracts where the email belongs to an author"""
988        return [self.getAbstractById(i) for i in self._getAuthEmailIndex().match(email)]
989
990    def getNotificationTplList(self):
991        try:
992            if self._notifTpls:
993                pass
994        except AttributeError:
995            self._notifTpls=IOBTree()
996        try:
997            if self._notifTplsOrder:
998                pass
999        except AttributeError:
1000            self._notifTplsOrder=PersistentList()
1001            for tpl in self._notifTpls.values():
1002                self._notifTplsOrder.append(tpl)
1003        return self._notifTplsOrder
1004
1005    def addNotificationTpl(self,tpl):
1006        try:
1007            if self._notifTpls:
1008                pass
1009        except AttributeError:
1010            self._notifTpls=IOBTree()
1011        try:
1012            if self._notifTplsOrder:
1013                pass
1014        except AttributeError:
1015            self._notifTplsOrder=PersistentList()
1016            for tpl in self._notifTpls.values():
1017                self._notifTplsOrder.append(tpl)
1018        try:
1019            if self._notifTplsCounter:
1020                pass
1021        except AttributeError:
1022            self._notifTplsCounter=Counter()
1023        if tpl.getOwner()==self and self._notifTpls.has_key(tpl.getId()):
1024            return
1025        id = tpl.getId()
1026        if id == "":
1027            id=self._notifTplsCounter.newCount()
1028        tpl.includeInOwner(self,id)
1029        self._notifTpls[int(id)]=tpl
1030        self._notifTplsOrder.append(tpl)
1031
1032    def removeNotificationTpl(self,tpl):
1033        try:
1034            if self._notifTpls:
1035                pass
1036        except AttributeError:
1037            self._notifTpls=IOBTree()
1038        try:
1039            if self._notifTplsOrder:
1040                pass
1041        except AttributeError:
1042            self._notifTplsOrder=PersistentList()
1043            for tpl in self._notifTpls.values():
1044                self._notifTplsOrder.append(tpl)
1045        if tpl.getOwner()!=self or not self._notifTpls.has_key(int(tpl.getId())):
1046            return
1047        del self._notifTpls[int(tpl.getId())]
1048        self._notifTplsOrder.remove(tpl)
1049        tpl.includeInOwner(None,tpl.getId()) # We don't change the id for
1050                                             # recovery purposes.
1051        tpl.delete()
1052
1053    def recoverNotificationTpl(self, tpl):
1054        self.addNotificationTpl(tpl)
1055        tpl.recover()
1056
1057    def getNotificationTplById(self,id):
1058        try:
1059            if self._notifTpls:
1060                pass
1061        except AttributeError:
1062            self._notifTpls=IOBTree()
1063        return self._notifTpls.get(int(id),None)
1064
1065    def getNotifTplForAbstract(self,abs):
1066        """
1067        """
1068        for tpl in self.getNotificationTplList():
1069            if tpl.satisfies(abs):
1070                return tpl
1071        return None
1072
1073    def moveUpNotifTpl(self,tpl):
1074        """
1075        """
1076        try:
1077            if self._notifTplsOrder:
1078                pass
1079        except AttributeError:
1080            self._notifTplsOrder=PersistentList()
1081            for tpl in self._notifTpls.values():
1082                self._notifTplsOrder.append(tpl)
1083        if tpl not in self._notifTplsOrder:
1084            return
1085        idx=self._notifTplsOrder.index(tpl)
1086        if idx==0:
1087            return
1088        self._notifTplsOrder.remove(tpl)
1089        self._notifTplsOrder.insert(idx-1,tpl)
1090
1091
1092    def moveDownNotifTpl(self,tpl):
1093        """
1094        """
1095        try:
1096            if self._notifTplsOrder:
1097                pass
1098        except AttributeError:
1099            self._notifTplsOrder=PersistentList()
1100            for tpl in self._notifTpls.values():
1101                self._notifTplsOrder.append(tpl)
1102        idx=self._notifTplsOrder.index(tpl)
1103        if idx==len(self._notifTplsOrder):
1104            return
1105        self._notifTplsOrder.remove(tpl)
1106        self._notifTplsOrder.insert(idx+1,tpl)
1107
1108    def indexAuthor(self,auth):
1109        a=auth.getAbstract()
1110        if a.isPrimaryAuthor(auth):
1111            self._getPrimAuthIndex().index(auth)
1112        self._getAuthEmailIndex().index(auth)
1113
1114    def unindexAuthor(self,auth):
1115        a=auth.getAbstract()
1116        if a.isPrimaryAuthor(auth):
1117            self._getPrimAuthIndex().unindex(auth)
1118        self._getAuthEmailIndex().unindex(auth)
1119
1120    def _getPrimAuthIndex(self):
1121        try:
1122            if self._primAuthIdx:
1123                pass
1124        except AttributeError:
1125            self._primAuthIdx=_PrimAuthIdx(self)
1126        return self._primAuthIdx
1127
1128    def _getAuthEmailIndex(self):
1129        if not hasattr(self, '_authEmailIdx'):
1130            self._authEmailIdx = _AuthEmailIdx(self)
1131        return self._authEmailIdx
1132
1133    def getAbstractsMatchingAuth(self,query,onlyPrimary=True):
1134        if str(query).strip()=="":
1135            return self.getAbstractList()
1136        res=self._getPrimAuthIndex().match(query)
1137        return [self.getAbstractById(id) for id in res]
1138
1139    def addAbstractField(self, id, name, caption, maxLength=0, isMandatory=False, fieldType="textarea", fieldLimitation="chars"):
1140        return self.getAbstractFieldsMgr().addField(id, name, caption, maxLength, isMandatory, fieldType, fieldLimitation)
1141
1142    def removeAbstractField(self, id):
1143        self.getAbstractFieldsMgr().removeField(id)
1144
1145    def hasAnyEnabledAbstractField( self ):
1146        return self.getAbstractFieldsMgr().hasAnyActiveField()
1147
1148    def hasEnabledAbstractField(self, key):
1149        return self.getAbstractFieldsMgr().hasActiveField(key)
1150
1151    def enableAbstractField(self, abstractField):
1152        self.getAbstractFieldsMgr().enableField(abstractField)
1153        self.notifyModification()
1154
1155    def disableAbstractField(self, abstractField):
1156        self.getAbstractFieldsMgr().disableField(abstractField)
1157        self.notifyModification()
1158
1159    def moveAbsFieldUp(self, id):
1160        self.getAbstractFieldsMgr().moveAbsFieldUp(id)
1161        self.notifyModification()
1162
1163    def moveAbsFieldDown(self, id):
1164        self.getAbstractFieldsMgr().moveAbsFieldDown(id)
1165        self.notifyModification()
1166
1167    def getSubmissionNotification(self):
1168        try:
1169            if self._submissionNotification:
1170                pass
1171        except AttributeError, e:
1172            self._submissionNotification=SubmissionNotification()
1173        return self._submissionNotification
1174
1175    def setSubmissionNotification(self, sn):
1176        self._submissionNotification=sn
1177
1178    def recalculateAbstractsRating(self, scaleLower, scaleHigher):
1179        ''' recalculate the values of the rating for all the abstracts in the conference '''
1180        for abs in self.getAbstractList():
1181            abs.updateRating((scaleLower, scaleHigher))
1182
1183    def removeAnswersOfQuestion(self, questionId):
1184        ''' Remove a question results for each abstract '''
1185        for abs in self.getAbstractList():
1186            abs.removeAnswersOfQuestion(questionId)
1187
1188    def notifyModification(self):
1189        self._p_changed=1
1190
1191class SubmissionNotification(Persistent):
1192
1193    def __init__(self):
1194        self._toList = PersistentList()
1195        self._ccList = PersistentList()
1196
1197    def hasDestination(self):
1198        return self._toList!=[] or self._toList!=[]
1199
1200    def getToList(self):
1201        return self._toList
1202
1203    def setToList(self, tl):
1204        self._toList = tl
1205
1206    def addToList(self, to):
1207        self._toList.append(to)
1208
1209    def clearToList(self):
1210        self._toList = PersistentList()
1211
1212    def getCCList(self):
1213        return self._ccList
1214
1215    def setCCList(self, cl):
1216        self._ccList = cl
1217
1218    def addCCList(self, cc):
1219        self._ccList.append(cc)
1220
1221    def clearCCList(self):
1222        self._ccList = PersistentList()
1223
1224    def clone(self):
1225        nsn=SubmissionNotification()
1226        for i in self.getToList():
1227            nsn.addToList(i)
1228        for i in self.getCCList():
1229            nsn.addCCList(i)
1230        return nsn
1231
1232class Comment(Persistent):
1233
1234    def __init__(self,res,content=""):
1235        self._abstract=None
1236        self._id=""
1237        self._responsible=res
1238        self._content=""
1239        self._creationDate=nowutc()
1240        self._modificationDate=nowutc()
1241
1242    def getLocator(self):
1243        loc=self._abstract.getLocator()
1244        loc["intCommentId"]=self._id
1245        return loc
1246
1247    def includeInAbstract(self,abstract,id):
1248        self._abstract=abstract
1249        self._id=id
1250
1251    def delete(self):
1252        self._abstract=None
1253        TrashCanManager().add(self)
1254
1255    def recover(self):
1256        TrashCanManager().remove(self)
1257
1258    def _notifyModification(self, dt=None):
1259        if dt:
1260            self._modificationDate = dt
1261        else:
1262            self._modificationDate=nowutc()
1263
1264    def getResponsible(self):
1265        return self._responsible
1266
1267    def getAbstract(self):
1268        return self._abstract
1269
1270    def getId(self):
1271        return self._id
1272
1273    def getContent(self):
1274        return self._content
1275
1276    def setContent(self, newContent):
1277        self._content=newContent
1278        self._notifyModification()
1279
1280    def getCreationDate(self):
1281        return self._creationDate
1282
1283    def getModificationDate(self):
1284        return self._modificationDate
1285
1286    def canModify(self,aw):
1287        return self.canUserModify(aw.getUser())
1288
1289    def canUserModify(self,user):
1290        abstract=self.getAbstract()
1291        conf=abstract.getConference()
1292        return self.getResponsible()==user and \
1293            (abstract.canUserModify(user) or \
1294            len(conf.getConference().getCoordinatedTracks(user))>0)
1295
1296class Abstract(Persistent):
1297
1298    def __init__(self, owner, id, submitter, **abstractData):
1299        self._setOwner( owner )
1300        self._setId( id )
1301        self._title = ""
1302        self._fields = {}
1303        self._authorGen = Counter()
1304        self._authors = OOBTree()
1305        self._primaryAuthors = PersistentList()
1306        self._coAuthors = PersistentList()
1307        self._speakers = PersistentList()
1308        self._tracks = OOBTree()
1309        self._contribTypes = PersistentList( [""] )
1310        self._setSubmissionDate( nowutc() )
1311        self._modificationDate = nowutc()
1312        self._currentStatus = AbstractStatusSubmitted( self )
1313        self._trackAcceptances = OOBTree()
1314        self._trackRejections = OOBTree()
1315        self._trackReallocations = OOBTree()
1316        self._trackJudgementsHistorical={}
1317        self._comments = ""
1318        self._contribution = None
1319        self._intCommentGen=Counter()
1320        self._intComments=PersistentList()
1321        self._mergeFromList = PersistentList()
1322        self._notifLog=NotificationLog(self)
1323        self._submitter=None
1324        self._setSubmitter( submitter )
1325        self._rating = None # It needs to be none to avoid the case of having the same value as the lowest value in the judgement
1326        self._attachments = {}
1327        self._attachmentsCounter = Counter()
1328
1329    def clone(self, conference, abstractId):
1330
1331        # abstractId - internal in abstract manager of the conference
1332        abs = Abstract(conference.getAbstractMgr(), abstractId, self.getSubmitter().getAvatar())
1333        abs.setTitle(self.getTitle())
1334        for key in self.getFields().keys():
1335            abs.setField(key,self.getField(key))
1336        abs.setComments(self.getComments())
1337
1338        abs._setSubmissionDate(self.getSubmissionDate())
1339        abs._modificationDate = self.getModificationDate()
1340
1341        # Cloning of primary- and coauthors
1342        # if an author is also a speaker, an appropriate object will be
1343        # appended also to the speaker list
1344        for pa in self.getPrimaryAuthorList() :
1345            npa = abs.newPrimaryAuthor(**(pa.getData()))
1346            if self.isSpeaker(pa) :
1347                abs.addSpeaker(npa)
1348        for ca in self.getCoAuthorList() :
1349            nca = abs.newCoAuthor(**(ca.getData()))
1350            if self.isSpeaker(ca) :
1351                abs.addSpeaker(nca)
1352
1353        # Cloning of speakers
1354        # only those, who are not authors :
1355            for sp in self.getSpeakerList() :
1356                if not self.isAuthor(sp) :
1357                    abs.addSpeaker(sp.clone())
1358
1359        abs.setSubmitter(self.getSubmitter().getAvatar())
1360
1361        if self.getContribType() is not None :
1362            for ct in conference.getContribTypeList() :
1363                if self.getContribType().getName() == ct.getName() :
1364                    abs.setContribType(ct)
1365                    break
1366        else :
1367            abs.setContribType(None)
1368
1369        # the track, to which the abstract belongs to
1370        # legacy list implementation
1371        for tr in self.getTrackList() :
1372            for newtrack in conference.getTrackList():
1373                if newtrack.getTitle() == tr.getTitle() :
1374                    abs.addTrack(newtrack)
1375
1376        # overall abstract status (accepted / rejected)
1377        abs._currentStatus = self._currentStatus.clone(abs)
1378
1379        for ta in self.getTrackAcceptanceList() :
1380            for newtrack in conference.getTrackList():
1381                if newtrack.getTitle() == ta.getTrack().getTitle() :
1382                    newta = ta.clone(newtrack)
1383                    abs._addTrackAcceptance(newta)
1384                    abs._addTrackJudgementToHistorical(newta)
1385
1386        for trj in self._trackRejections.values() :
1387            for newtrack in conference.getTrackList():
1388                if newtrack.getTitle() == trj.getTrack().getTitle() :
1389                    newtrj = trj.clone(newtrack)
1390                    abs._addTrackRejection(newtrj)
1391                    abs._addTrackJudgementToHistorical(newtrj)
1392
1393        for trl in self._trackReallocations.values() :
1394            for newtrack in conference.getTrackList():
1395                if newtrack.getTitle() == trl.getTrack().getTitle() :
1396                    newtrl = trl.clone(newtrack)
1397                    abs._addTrackReallocation(newtrl)
1398                    abs._addTrackJudgementToHistorical(newtrl)
1399
1400        # Cloning materials
1401        for f in self.getAttachments().values():
1402            newFile = f.clone(abs, protection=False)
1403            abs.__addFile(newFile)
1404
1405        return abs
1406
1407    def getUniqueId( self ):
1408        """returns (string) the unique identifier of the item"""
1409        """used only in the web session access key table"""
1410        """it is the same as the conference since only the conf can"""
1411        """be protected with an access key"""
1412        return self.getConference().getUniqueId()
1413
1414    def getMergeFromList(self):
1415        try:
1416            return self._mergeFromList
1417        except AttributeError:
1418            self._mergeFromList = PersistentList()
1419            return self._mergeFromList
1420
1421    def addMergeFromAbstract(self, abstract):
1422        try:
1423            if self._mergeFromList:
1424                pass
1425        except AttributeError:
1426            self._mergeFromList = PersistentList()
1427        self._mergeFromList.append(abstract)
1428
1429    def removeMergeFromAbstract(self, abstract):
1430        try:
1431            if self._mergeFromList:
1432                pass
1433        except AttributeError:
1434            self._mergeFromList = PersistentList()
1435
1436        if abstract in self._mergeFromList:
1437            self._mergeFromList.remove(abstract)
1438
1439    def getComments(self):
1440        try:
1441            return self._comments
1442        except AttributeError:
1443            self._comments = ""
1444            return self._comments
1445
1446    def setComments(self, comments):
1447        self._comments = comments
1448
1449    def __addFile(self, file):
1450        file.archive(self.getConference()._getRepository())
1451        self.getAttachments()[file.getId()] = file
1452        self._notifyModification()
1453
1454
1455    def saveFiles(self, files):
1456        cfg = Config.getInstance()
1457        from MaKaC.conference import LocalFile
1458        for fileUploaded in files:
1459            # create a temp file
1460            tempPath = cfg.getUploadedFilesTempDir()
1461            tempFileName = tempfile.mkstemp(suffix="IndicoAbstract.tmp", dir=tempPath)[1]
1462            f = open(tempFileName, "wb")
1463            f.write(fileUploaded.file.read() )
1464            f.close()
1465            file = LocalFile()
1466            file.setFileName(fileUploaded.filename)
1467            file.setFilePath(tempFileName)
1468            file.setOwner(self)
1469            file.setId(self._getAttachmentsCounter())
1470            self.__addFile(file)
1471
1472    def deleteFilesNotInList(self, keys):
1473        """This method is used in order to delete all the files that are not present (by id) in the
1474        parameter "keys".
1475        This is useful when files are deleted from the abstract form using Javascript, and so it is
1476        the only way to know that they are deleted.
1477        """
1478        existingKeys = self.getAttachments().keys()
1479        for key in existingKeys:
1480            if not key in keys:
1481                self._deleteFile(key)
1482
1483    def _deleteFile(self, key):
1484        file = self.getAttachments()[key]
1485        file.delete()
1486        del self.getAttachments()[key]
1487        self._notifyModification()
1488
1489    def removeResource(self, res):
1490        """Necessary because LocalFile.delete (see _deleteFile) is calling this method.
1491        In our case, nothing to do.
1492        """
1493        pass
1494
1495    def _setOwner( self, owner ):
1496        self._owner = owner
1497
1498    def getOwner( self ):
1499        return self._owner
1500
1501    def _setId( self, id ):
1502        self._id = str( id )
1503
1504    def getId(self):
1505        return self._id
1506
1507    def _setSubmissionDate( self, newDate ):
1508        self._submissionDate = newDate
1509
1510    def setModificationDate(self, dt = None):
1511        if dt:
1512            self._modificationDate = dt
1513        else:
1514            self._modificationDate = nowutc()
1515
1516    def _notifyModification( self, dt=None ):
1517        self.setModificationDate(dt)
1518        self._p_changed = 1
1519
1520    def getModificationDate( self ):
1521        return self._modificationDate
1522
1523    def _setSubmitter( self, av ):
1524        if not av:
1525            raise MaKaCError( _("An abstract must have a submitter"))
1526        if self._submitter:
1527            self.getOwner().unregisterParticipation( self._submitter )
1528            self._submitter.getUser().unlinkTo(self, "submitter")
1529            self._submitter.delete()
1530        self._submitter=Submitter( self, av )
1531        av.linkTo(self, "submitter")
1532        self.getOwner().registerParticipation( self._submitter )
1533        self._notifyModification()
1534
1535    def recoverSubmitter(self, subm):
1536        if not subm:
1537            raise MaKaCError( _("An abstract must have a submitter"))
1538        if self._submitter:
1539            self.getOwner().unregisterParticipation( self._submitter )
1540            self._submitter.delete()
1541        self._submitter = subm
1542        self._submitter.setAbstract(self)
1543        self.getOwner().registerParticipation( self._submitter )
1544        subm.recover()
1545        self._notifyModification()
1546
1547    def setSubmitter( self, av ):
1548        self._setSubmitter(av)
1549
1550    def getSubmitter( self ):
1551        return self._submitter
1552
1553    def isSubmitter( self, av ):
1554        return self.getSubmitter().representsUser( av )
1555
1556    def setTitle(self, title):
1557        self._title = title.strip()
1558        self._notifyModification()
1559
1560    def getTitle(self):
1561        return self._title
1562
1563    def getFields( self ):
1564        try:
1565            return self._fields
1566        except:
1567            self._fields = {}
1568            try:
1569                if self._content != "":
1570                    self._fields["content"] = self._content
1571                del self._content
1572            except:
1573                pass
1574            try:
1575                if self._summary != "":
1576                    self._fields["summary"] = self._summary
1577                del self._summary
1578            except:
1579                pass
1580            return self._fields
1581
1582    def removeField(self, field):
1583        if self.getFields().has_key(field):
1584            del self.getFields()[field]
1585            self._notifyModification()
1586
1587    def setField( self, field, value ):
1588        try:
1589            self.getFields()[field] = value
1590            self._notifyModification()
1591        except:
1592            pass
1593
1594    def getField( self, field):
1595        try:
1596            if self._content != "":
1597                self._fields["content"] = self._content
1598            del self._content
1599        except:
1600            pass
1601        try:
1602            if self._summary != "":
1603                self._fields["summary"] = self._summary
1604            del self._summary
1605        except:
1606            pass
1607        if self.getFields().has_key(field):
1608            return self.getFields()[field]
1609        else:
1610            return ""
1611
1612    def getSubmissionDate( self ):
1613        try:
1614            if self._submissionDate:
1615                pass
1616        except AttributeError:
1617            self._submissionDate=nowutc()
1618        return self._submissionDate
1619
1620    def getConference( self ):
1621        return self.getOwner().getOwner()
1622
1623    def _newAuthor( self, **data ):
1624        author = Author( self, **data )
1625        author.setId( self._authorGen.newCount() )
1626        self._authors[ author.getId() ] = author
1627        return author
1628
1629    def _removeAuthor(self,part):
1630        if not self.isAuthor(part):
1631            return
1632        part.delete()
1633        del self._authors[part.getId()]
1634
1635    def isAuthor( self, part ):
1636        return self._authors.has_key( part.getId() )
1637
1638    def getAuthorList( self ):
1639        return self._authors.values()
1640
1641    def getAuthorById(self, id):
1642        return self._authors.get(str(id), None)
1643
1644    def clearAuthors( self ):
1645        self.clearPrimaryAuthors()
1646        self.clearCoAuthors()
1647        self._notifyModification()
1648
1649    def newPrimaryAuthor(self,**data):
1650        auth=self._newAuthor(**data)
1651        self._addPrimaryAuthor(auth)
1652        self._notifyModification()
1653        return auth
1654
1655    def isPrimaryAuthor( self, part ):
1656        return part in self._primaryAuthors
1657
1658    def getPrimaryAuthorList( self ):
1659        return self._primaryAuthors
1660    #XXX: I keep it for compatibility but it should be removed
1661    getPrimaryAuthorsList = getPrimaryAuthorList
1662
1663    def getPrimaryAuthorEmailList(self, lower=False):
1664        emailList = []
1665        for pAuthor in self.getPrimaryAuthorList():
1666            emailList.append(pAuthor.getEmail().lower() if lower else pAuthor.getEmail())
1667        return emailList
1668
1669    def clearPrimaryAuthors(self):
1670        while len(self._primaryAuthors)>0:
1671            self._removePrimaryAuthor(self._primaryAuthors[0])
1672        self._notifyModification()
1673
1674    def _addPrimaryAuthor( self, part ):
1675        if not self.isAuthor( part ):
1676            raise MaKaCError( _("The participation you want to set as primary author is not an author of the abstract"))
1677        if part in self._primaryAuthors:
1678            return
1679        self._primaryAuthors.append( part )
1680        self.getOwner().indexAuthor(part)
1681
1682    def _removePrimaryAuthor(self,part):
1683        if not self.isPrimaryAuthor(part):
1684            return
1685        if self.isSpeaker(part):
1686            self.removeSpeaker(part)
1687        self.getOwner().unindexAuthor(part)
1688        self._primaryAuthors.remove(part)
1689        self._removeAuthor(part)
1690
1691    def recoverPrimaryAuthor(self, auth):
1692        self._authors[ auth.getId() ] = auth
1693        auth.setAbstract(self)
1694        self._addPrimaryAuthor(auth)
1695        auth.recover()
1696        self._notifyModification()
1697
1698    def newCoAuthor(self,**data):
1699        auth=self._newAuthor(**data)
1700        self._addCoAuthor(auth)
1701        self._notifyModification()
1702        return auth
1703
1704    def _comp_CoAuthors(self):
1705        try:
1706            if self._coAuthors!=None:
1707                return
1708        except AttributeError:
1709            self._coAuthors=PersistentList()
1710        for auth in self._authors.values():
1711            if not self.isPrimaryAuthor(auth):
1712                self._addCoAuthor(auth)
1713
1714    def isCoAuthor( self, part ):
1715        self._comp_CoAuthors()
1716        return part in self._coAuthors
1717
1718    def getCoAuthorList( self ):
1719        self._comp_CoAuthors()
1720        return self._coAuthors
1721
1722    def getCoAuthorEmailList(self, lower=False):
1723        emailList = []
1724        for coAuthor in self.getCoAuthorList():
1725            emailList.append(coAuthor.getEmail().lower() if lower else coAuthor.getEmail())
1726        return emailList
1727
1728    def clearCoAuthors(self):
1729        while len(self._coAuthors)>0:
1730            self._removeCoAuthor(self._coAuthors[0])
1731        self._notifyModification()
1732
1733    def _addCoAuthor( self, part ):
1734        self._comp_CoAuthors()
1735        if not self.isAuthor( part ):
1736            raise MaKaCError( _("The participation you want to set as primary author is not an author of the abstract"))
1737        if part in self._coAuthors:
1738            return
1739        self._coAuthors.append( part )
1740
1741    def _removeCoAuthor(self,part):
1742        if not self.isCoAuthor(part):
1743            return
1744        if self.isSpeaker(part):
1745            self.removeSpeaker(part)
1746        self._coAuthors.remove(part)
1747        self._removeAuthor(part)
1748
1749    def recoverCoAuthor(self, auth):
1750        self._authors[ auth.getId() ] = auth
1751        auth.setAbstract(self)
1752        self._addCoAuthor(auth)
1753        auth.recover()
1754        self._notifyModification()
1755
1756    def addSpeaker( self, part ):
1757        if not self.isAuthor( part ):
1758            raise MaKaCError( _("The participation you want to set as speaker is not an author of the abstract"))
1759        if part in self._speakers:
1760            return
1761        self._speakers.append( part )
1762        self._notifyModification()
1763
1764    def removeSpeaker(self,part):
1765        if part not in self._speakers:
1766            return
1767        self._speakers.remove(part)
1768
1769    def clearSpeakers( self ):
1770        while len(self.getSpeakerList()) > 0:
1771            self.removeSpeaker(self.getSpeakerList()[0])
1772        self._speakers = PersistentList()
1773
1774    def getSpeakerList( self ):
1775        return self._speakers
1776
1777    def isSpeaker( self, part ):
1778        return part in self._speakers
1779
1780    def setContribType( self, contribType ):
1781        self._contribTypes[0] = contribType
1782        self._notifyModification()
1783
1784    def getContribType( self ):
1785        return self._contribTypes[0]
1786
1787
1788    def _addTrack( self, track ):
1789        """Adds the specified track to the suggested track list. Any
1790            verification must be done by the caller.
1791        """
1792        self._tracks[ track.getId() ] = track
1793        track.addAbstract( self )
1794        self._notifyModification()
1795
1796    def addTrack( self, track ):
1797        self._changeTracksImpl()
1798        if not self._tracks.has_key( track.getId() ):
1799            self._addTrack( track )
1800            self.getCurrentStatus().update()
1801
1802    def _removeTrack( self, track ):
1803        """Removes the specified track from the track list. Any verification
1804            must be done by the caller.
1805        """
1806        del self._tracks[ track.getId() ]
1807        track.removeAbstract( self )
1808        self._notifyModification()
1809
1810    def removeTrack( self, track ):
1811        if self._tracks.has_key( track.getId() ):
1812            self._removeTrack( track )
1813            self.getCurrentStatus().update()
1814        if isinstance(self.getCurrentStatus(), AbstractStatusAccepted):
1815            self.getCurrentStatus()._setTrack(None)
1816
1817    def _changeTracksImpl( self ):
1818        if self._tracks.__class__ != OOBTree:
1819            oldTrackList = self._tracks
1820            self._tracks = OOBTree()
1821            for track in oldTrackList:
1822                self._addTrack( track )
1823            self.getCurrentStatus().update()
1824
1825    def getTrackList( self ):
1826        self._changeTracksImpl()
1827
1828        return self._tracks.values()
1829
1830    def hasTrack( self, track ):
1831        self._changeTracksImpl()
1832
1833        return self._tracks.has_key( track.getId() )
1834
1835    def getTrackListSorted( self ):
1836        self._changeTracksImpl()
1837        return self.getConference().sortTrackList( self._tracks.values() )
1838
1839    def clearTracks( self ):
1840        self._changeTracksImpl()
1841
1842        while len(self.getTrackList())>0:
1843            track = self.getTrackList()[0]
1844            self._removeTrack( track )
1845        self.getCurrentStatus().update()
1846
1847    def setTracks( self, trackList ):
1848        """Set the suggested track classification of the current abstract to
1849            the specified list
1850        """
1851        #We need to do it in 2 steps otherwise the list over which we are
1852        #   iterating gets modified
1853        toBeRemoved = []
1854        toBeAdded = copy( trackList )
1855        for track in self.getTrackList():
1856            if track not in trackList:
1857                toBeRemoved.append( track )
1858            else:
1859                toBeAdded.remove( track )
1860        for track in toBeRemoved:
1861            self._removeTrack( track )
1862        for track in toBeAdded:
1863            self._addTrack( track )
1864        self.getCurrentStatus().update()
1865
1866    def isProposedForTrack( self, track ):
1867        return self._tracks.has_key( track.getId() )
1868
1869    def getNumTracks( self ):
1870        return len( self._tracks )
1871
1872    def getLocator(self):
1873        loc = self.getConference().getLocator()
1874        loc["abstractId"] = self.getId()
1875        return loc
1876
1877    def isAllowedToCoordinate(self,av):
1878        """Tells whether or not the specified user can coordinate any of the
1879            tracks of this abstract
1880        """
1881        for track in self.getTrackList():
1882            if track.canUserCoordinate(av):
1883                return True
1884        return False
1885
1886    def canAuthorAccess(self, user):
1887        if user is None:
1888            return False
1889        el=self.getCoAuthorEmailList(True)+self.getPrimaryAuthorEmailList(True)
1890        for e in user.getEmails():
1891            if e.lower() in el:
1892                return True
1893        return False
1894
1895    def isAllowedToAccess( self, av ):
1896        """Tells whether or not an avatar can access an abstract independently
1897            of the protection
1898        """
1899        #any author is allowed to access
1900        #CFA managers are allowed to access
1901        #any user being able to modify is also allowed to access
1902        #any TC is allowed to access
1903        if self.canAuthorAccess(av):
1904            return True
1905        if self.isAllowedToCoordinate(av):
1906            return True
1907        return self.canUserModify(av)
1908
1909    def canAccess(self,aw):
1910        #if the conference is protected, then only allowed AW can access
1911        return self.isAllowedToAccess( aw.getUser() )
1912
1913    def canView(self, aw):
1914        #in the future it would be possible to add an access control
1915        #only those users allowed to access are allowed to view
1916        return self.isAllowedToAccess( aw.getUser() )
1917
1918    def canModify( self, aw ):
1919        return self.canUserModify( aw.getUser() )
1920
1921    def canUserModify( self, av ):
1922        #the submitter can modify
1923        if self.isSubmitter( av ):
1924            return True
1925        #??? any CFA manager can modify
1926        #??? any user granted with modification privileges can modify
1927        #conference managers can modify
1928        conf = self.getConference()
1929        return conf.canUserModify( av )
1930
1931    def getModifKey( self ):
1932        return ""
1933
1934    def getAccessKey( self ):
1935        return ""
1936
1937    def getAccessController(self):
1938        return self.getConference().getAccessController()
1939
1940    def isProtected(self):
1941        return self.getConference().isProtected()
1942
1943    def delete( self ):
1944        if self._owner:
1945            self.getSubmitter().delete()
1946            self._submitter = None
1947            self.clearAuthors()
1948            self.clearSpeakers()
1949            self.clearTracks()
1950            owner = self._owner
1951            self._owner = None
1952            owner.removeAbstract( self )
1953            self.setCurrentStatus(AbstractStatusNone(self))
1954            TrashCanManager().add(self)
1955
1956    def recoverFromTrashCan(self):
1957        TrashCanManager().remove(self)
1958
1959    def getCurrentStatus( self ):
1960        try:
1961            if self._currentStatus:
1962                pass
1963        except AttributeError, e:
1964            self._currentStatus = AbstractStatusSubmitted( self )
1965        return self._currentStatus
1966
1967    def setCurrentStatus( self, newStatus ):
1968        self._currentStatus = newStatus
1969        #If we want to keep a history of status changes we should add here
1970        #   the old status to a list
1971
1972    def accept(self,responsible,destTrack,type,comments="",session=None):
1973        """
1974        """
1975        self.getCurrentStatus().accept(responsible,destTrack,type,comments )
1976        #add the abstract to the track for which it has been accepted so it
1977        #   is visible for it.
1978        if destTrack is not None:
1979            destTrack.addAbstract( self )
1980        #once the abstract is accepted a new contribution under the destination
1981        #   track must be created
1982        # ATTENTION: This import is placed here explicitely for solving
1983        #   problems with circular imports
1984        from MaKaC.conference import AcceptedContribution
1985        contrib=AcceptedContribution(self)
1986        if session != None:
1987            contrib.setSession(session)
1988        self.getCurrentStatus().setContribution( contrib )
1989        self._setContribution( contrib )
1990
1991    def reject( self, responsible, comments="" ):
1992        """
1993        """
1994        self.getCurrentStatus().reject( responsible, comments )
1995
1996    def _cmpByDate(self, tj1, tj2):
1997        return cmp(tj1.getDate(), tj2.getDate())
1998
1999    def getTrackJudgementsHistorical(self):
2000        try:
2001            if self._trackJudgementsHistorical:
2002                pass
2003            if type(self._trackJudgementsHistorical) == tuple:
2004                self._trackJudgementsHistorical={}
2005        except AttributeError, e:
2006            self._trackJudgementsHistorical={}
2007            for track in self.getTrackList():
2008                if self.getTrackJudgement(track) is not None:
2009                    self._trackJudgementsHistorical[track.getId()]= [self.getTrackJudgement(track)]
2010            self._notifyModification()
2011        return self._trackJudgementsHistorical
2012
2013    def getJudgementHistoryByTrack(self, track):
2014        id = "notrack"
2015        if track is not None:
2016            id = track.getId()
2017        if self.getTrackJudgementsHistorical().has_key(id):
2018            return self.getTrackJudgementsHistorical()[id]
2019        return []
2020
2021    def _addTrackJudgementToHistorical(self, tj):
2022        id = "notrack"
2023        if tj.getTrack() is not None:
2024            id = tj.getTrack().getId()
2025        if self.getTrackJudgementsHistorical().has_key(id):
2026            if tj not in self.getTrackJudgementsHistorical()[id]:
2027                self.getTrackJudgementsHistorical()[id].insert(0, tj)
2028        else:
2029            self.getTrackJudgementsHistorical()[id] = [tj]
2030        self._notifyModification()
2031
2032    def _removeTrackAcceptance( self, track ):
2033        """
2034        """
2035        try:
2036            if self._trackAcceptances:
2037                pass
2038        except AttributeError, e:
2039            self._trackAcceptances = OOBTree()
2040
2041        if self._trackAcceptances.has_key( track.getId() ):
2042            del self._trackAcceptances[ track.getId() ]
2043
2044    def _addTrackAcceptance( self, judgement ):
2045        """
2046        """
2047        try:
2048            if self._trackAcceptances:
2049                pass
2050        except AttributeError, e:
2051            self._trackAcceptances = OOBTree()
2052
2053        self._removeTrackRejection( judgement.getTrack() )
2054        self._removeTrackReallocation( judgement.getTrack() )
2055        self._trackAcceptances[ judgement.getTrack().getId() ] = judgement
2056        self._addTrackJudgementToHistorical(judgement)
2057
2058    def _removeTrackRejection( self, track ):
2059        """
2060        """
2061        try:
2062            if self._trackRejections:
2063                pass
2064        except AttributeError, e:
2065            self._trackRejections = OOBTree()
2066
2067        if self._trackRejections.has_key( track.getId() ):
2068            del self._trackRejections[ track.getId() ]
2069
2070    def _addTrackRejection( self, judgement ):
2071        """
2072        """
2073        try:
2074            if self._trackRejections:
2075                pass
2076        except AttributeError, e:
2077            self._trackRejections = OOBTree()
2078
2079        self._removeTrackAcceptance( judgement.getTrack() )
2080        self._removeTrackReallocation( judgement.getTrack() )
2081        self._trackRejections[ judgement.getTrack().getId() ] = judgement
2082        self._addTrackJudgementToHistorical(judgement)
2083
2084    def _removeTrackReallocation( self, track ):
2085        """
2086        """
2087        try:
2088            if self._trackReallocations:
2089                pass
2090        except AttributeError, e:
2091            self._trackReallocations = OOBTree()
2092
2093        if self._trackReallocations.has_key( track.getId() ):
2094            del self._trackReallocations[ track.getId() ]
2095
2096    def _addTrackReallocation( self, judgement ):
2097        """
2098        """
2099        try:
2100            if self._trackReallocations:
2101                pass
2102        except AttributeError, e:
2103            self._trackReallocations = OOBTree()
2104
2105        self._removeTrackAcceptance( judgement.getTrack() )
2106        self._removeTrackRejection( judgement.getTrack() )
2107        self._trackReallocations[ judgement.getTrack().getId() ] = judgement
2108        self._addTrackJudgementToHistorical(judgement)
2109
2110    def _clearTrackRejections( self ):
2111        try:
2112            if self._trackRejections:
2113                pass
2114        except AttributeError, e:
2115            self._trackRejections = OOBTree()
2116        while len(self._trackRejections.values())>0:
2117            t = self._trackRejections.values()[0].getTrack()
2118            self._removeTrackRejection( t )
2119
2120    def _clearTrackAcceptances( self ):
2121        try:
2122            if self._trackAcceptances:
2123                pass
2124        except AttributeError, e:
2125            self._trackAcceptances = OOBTree()
2126        while len(self._trackAcceptances.values())>0:
2127            t = self._trackAcceptances.values()[0].getTrack()
2128            self._removeTrackAcceptance( t )
2129
2130    def _clearTrackReallocations( self ):
2131        try:
2132            if self._trackReallocations:
2133                pass
2134        except AttributeError, e:
2135            self._trackReallocations = OOBTree()
2136        while len(self._trackReallocations.values())>0:
2137            t = self._trackReallocations.values()[0].getTrack()
2138            self._removeTrackReallocation(t)
2139
2140    def _removePreviousJud(self, responsible, track):
2141        ''' Check if there is a previous judgement and remove it '''
2142        toDelete = [] # list of judgements to delete
2143        for jud in self.getJudgementHistoryByTrack(track):
2144            if jud.getResponsible() == responsible:
2145                toDelete.append(jud)
2146
2147        for x in toDelete:
2148            self.getTrackJudgementsHistorical()[track.getId()].remove(x)
2149
2150
2151    def proposeToAccept( self, responsible, track, contribType, comment="", answers=[] ):
2152        """
2153        """
2154        # the proposal has to be done for a track
2155        if track is None:
2156            raise MaKaCError( _("You have to choose a track in order to do the proposal. If there are not tracks to select, please change the track assignment of the abstract"))
2157        #We check the track for which the abstract is proposed to be accepted
2158        #   is in the current abstract
2159        if not self.isProposedForTrack( track ):
2160            raise MaKaCError( _("Cannot propose to accept an abstract which is not proposed for the specified track"))
2161        # check if there is a previous judgement of this author in for this abstract in this track
2162        self._removePreviousJud(responsible, track)
2163        # Create the new judgement
2164        jud = AbstractAcceptance( track, responsible, contribType, answers )
2165        jud.setComment( comment )
2166        self._addTrackAcceptance( jud )
2167        # Update the rating of the abstract
2168        self.updateRating()
2169        #We trigger the state transition
2170        self.getCurrentStatus().proposeToAccept()
2171
2172    def proposeToReject( self, responsible, track, comment="", answers=[] ):
2173        """
2174        """
2175        # the proposal has to be done for a track
2176        if track is None:
2177            raise MaKaCError( _("You have to choose a track in order to do the proposal. If there are not tracks to select, please change the track assignment of the abstract"))
2178        #We check the track for which the abstract is proposed to be accepted
2179        #   is in the current abstract
2180        if not self.isProposedForTrack( track ):
2181            raise MaKaCError( _("Cannot propose to reject an abstract which is not proposed for the specified track"))
2182        # check if there is a previous judgement of this author in for this abstract in this track
2183        self._removePreviousJud(responsible, track)
2184        # Create the new judgement
2185        jud = AbstractRejection( track, responsible, answers )
2186        jud.setComment( comment )
2187        self._addTrackRejection( jud )
2188        # Update the rating of the abstract
2189        self.updateRating()
2190        #We trigger the state transition
2191        self.getCurrentStatus().proposeToReject()
2192
2193    def proposeForOtherTracks( self, responsible, track, comment, propTracks, answers=[] ):
2194        """
2195        """
2196        #We check the track which proposes to allocate the abstract is in the
2197        #   current abstract
2198        if not self.isProposedForTrack( track ):
2199            raise MaKaCError( _("Cannot propose to reallocate an abstract which is not proposed for the specified track"))
2200        # check if there is a previous judgement of this author in for this abstract in this track
2201        self._removePreviousJud(responsible, track)
2202        #We keep the track judgement
2203        jud = AbstractReallocation( track, responsible, propTracks, answers )
2204        jud.setComment( comment )
2205        self._addTrackReallocation( jud )
2206        #We add the proposed tracks to the abstract
2207        for track in propTracks:
2208            self._addTrack( track )
2209        #We trigger the state transition
2210        self.getCurrentStatus().proposeToReallocate()
2211        # Update the rating of the abstract
2212        self.updateRating()
2213
2214    def withdraw(self,resp,comment=""):
2215        """
2216        """
2217        self.getCurrentStatus().withdraw(resp,comment)
2218
2219    def recover( self ):
2220        """Puts a withdrawn abstract back in the list of submitted abstracts.
2221           HAS NOTHING TO DO WITH THE RECOVERY PROCESS...
2222        """
2223        #we must clear any track judgement
2224        #self._clearTrackAcceptances()
2225        #self._clearTrackRejections()
2226        #self._clearTrackReallocations()
2227        self.getCurrentStatus().recover() #status change
2228        #if succeeded we must reset the submission date
2229        self._setSubmissionDate( nowutc() )
2230        self._notifyModification()
2231
2232    def getTrackJudgement( self, track ):
2233        """
2234        """
2235        try:
2236            if self._trackAcceptances:
2237                pass
2238        except AttributeError, e:
2239            self._trackAcceptances = OOBTree()
2240        try:
2241            if self._trackRejections:
2242                pass
2243        except AttributeError, e:
2244            self._trackRejections = OOBTree()
2245        try:
2246            if self._trackReallocations:
2247                pass
2248        except AttributeError, e:
2249            self._trackReallocations = OOBTree()
2250
2251        if self._trackAcceptances.has_key( track.getId() ):
2252            return self._trackAcceptances[ track.getId() ]
2253        elif self._trackRejections.has_key( track.getId() ):
2254            return self._trackRejections[ track.getId() ]
2255        elif self._trackReallocations.has_key( track.getId() ):
2256            return self._trackReallocations[ track.getId() ]
2257        return None
2258
2259    def getTrackAcceptanceList( self ):
2260        try:
2261            if self._trackAcceptances:
2262                pass
2263        except AttributeError, e:
2264            self._trackAcceptances = OOBTree()
2265        res = []
2266        for trackId in intersection( self._tracks, self._trackAcceptances ):
2267            res.append( self._trackAcceptances[ trackId ] )
2268        return res
2269
2270    def getNumProposedToAccept( self ):
2271        try:
2272            if self._trackAcceptances:
2273                pass
2274        except AttributeError, e:
2275            self._trackAcceptances = OOBTree()
2276        return len( intersection( self._tracks, self._trackAcceptances ) )
2277
2278    def getNumProposedToReject( self ):
2279        try:
2280            if self._trackRejections:
2281                pass
2282        except AttributeError, e:
2283            self._trackRejections = OOBTree()
2284        return len( intersection( self._tracks, self._trackRejections ) )
2285
2286    def getNumJudgements( self ):
2287        """
2288        """
2289        try:
2290            if self._trackAcceptances:
2291                pass
2292        except AttributeError, e:
2293            self._trackAcceptances = OOBTree()
2294        try:
2295            if self._trackRejections:
2296                pass
2297        except AttributeError, e:
2298            self._trackRejections = OOBTree()
2299        try:
2300            if self._trackReallocations:
2301                pass
2302        except AttributeError, e:
2303            self._trackReallocations = OOBTree()
2304        tmp1 = union( self._trackAcceptances, self._trackRejections )
2305        judgements = union( tmp1, self._trackReallocations )
2306        return len( intersection( self._tracks, judgements ) )
2307
2308    def getReallocationTargetedList( self, track ):
2309        try:
2310            if self._trackReallocations:
2311                pass
2312        except AttributeError, e:
2313            self._trackReallocations = OOBTree()
2314        #XXX: not optimal
2315        res = []
2316        for r in self._trackReallocations.values():
2317            if track in r.getProposedTrackList():
2318                res.append( r )
2319        return res
2320
2321    def getContribution( self ):
2322        try:
2323            if self._contribution:
2324                pass
2325        except AttributeError:
2326            self._contribution = None
2327        status = self.getCurrentStatus()
2328        if isinstance(status,AbstractStatusAccepted) and \
2329                                            self._contribution is None:
2330            self._contribution=status.getContribution()
2331        return self._contribution
2332
2333    def _setContribution(self,contrib):
2334        self._contribution = contrib
2335
2336    def getIntCommentList(self):
2337        try:
2338            if self._intComments:
2339                pass
2340        except AttributeError:
2341            self._intComments=PersistentList()
2342        return self._intComments
2343
2344    def addIntComment(self,newComment):
2345        try:
2346            if self._intComments:
2347                pass
2348        except AttributeError:
2349            self._intComments=PersistentList()
2350        try:
2351            if self._intCommentsGen:
2352                pass
2353        except AttributeError:
2354            self._intCommentsGen=Counter()
2355        if newComment in self._intComments:
2356            return
2357        id = newComment.getId()
2358        if id == "":
2359            id = self._authorGen.newCount()
2360        newComment.includeInAbstract(self, id)
2361        self._intComments.append(newComment)
2362
2363    def getIntCommentById(self,id):
2364        try:
2365            if self._intComments:
2366                pass
2367        except AttributeError:
2368            self._intComments=PersistentList()
2369        for comment in self._intComments:
2370            if id.strip()==comment.getId():
2371                return comment
2372        return None
2373
2374    def clearIntCommentList(self):
2375        while len(self.getIntCommentList()) > 0:
2376            self.removeIntComment(self.getIntCommentList()[0])
2377
2378    def removeIntComment(self,comment):
2379        try:
2380            if self._intComments:
2381                pass
2382        except AttributeError:
2383            self._intComments=PersistentList()
2384        if comment not in self._intComments:
2385            return
2386        self._intComments.remove(comment)
2387        comment.delete()
2388
2389    def recoverIntComment(self, comment):
2390        self.addIntComment(comment)
2391        comment.recover()
2392
2393    def markAsDuplicated(self,responsible,originalAbstract,comments="", track=None, answers=[]):
2394        """
2395        """
2396        self.getCurrentStatus().markAsDuplicated(responsible,originalAbstract,comments)
2397        # check if there is a previous judgement of this author in for this abstract in this track
2398        self._removePreviousJud(responsible, track)
2399
2400        if track is not None:
2401            jud = AbstractMarkedAsDuplicated( track, responsible, originalAbstract, answers )
2402            jud.setComment( comments )
2403            self._addTrackJudgementToHistorical(jud)
2404        else:
2405            for t in self.getTrackList():
2406                jud = AbstractMarkedAsDuplicated( t, responsible, originalAbstract, answers )
2407                jud.setComment( comments )
2408                self._addTrackJudgementToHistorical(jud)
2409        # Update the rating of the abstract
2410        self.updateRating()
2411
2412    def unMarkAsDuplicated(self,responsible,comments="", track=None, answers=[]):
2413        """
2414        """
2415
2416        #we must clear any track judgement
2417        self._clearTrackAcceptances()
2418        self._clearTrackRejections()
2419        self._clearTrackReallocations()
2420        #self.getCurrentStatus().recover() #status change
2421        self.getCurrentStatus().unMarkAsDuplicated(responsible,comments)
2422
2423        # check if there is a previous judgement of this author in for this abstract in this track
2424        self._removePreviousJud(responsible, track)
2425
2426        if track is not None:
2427            jud = AbstractUnMarkedAsDuplicated(track, responsible, answers )
2428            jud.setComment( comments )
2429            self._addTrackJudgementToHistorical(jud)
2430        else:
2431            for t in self.getTrackList():
2432                jud = AbstractUnMarkedAsDuplicated( t, responsible, answers )
2433                jud.setComment( comments )
2434                self._addTrackJudgementToHistorical(jud)
2435        # Update the rating of the abstract
2436        self.updateRating()
2437        self._notifyModification()
2438
2439    def mergeInto(self,responsible,targetAbs,mergeAuthors=False,comments=""):
2440        """
2441        """
2442        self.getCurrentStatus().mergeInto(responsible,targetAbs,comments)
2443        targetAbs.addMergeFromAbstract(self)
2444        if mergeAuthors:
2445            #for auth in self.getAuthorList():
2446            #    newAuth=targetAbs.newAuthor()
2447            #    newAuth.setFromAbstractParticipation(auth)
2448            #    if self.isPrimaryAuthor(auth):
2449            #        targetAbs.addPrimaryAuthor(newAuth)
2450            for auth in self.getPrimaryAuthorList():
2451                newAuth=targetAbs.newPrimaryAuthor()
2452                newAuth.setFromAbstractParticipation(auth)
2453            for auth in self.getCoAuthorList():
2454                newAuth=targetAbs.newCoAuthor()
2455                newAuth.setFromAbstractParticipation(auth)
2456
2457    def notify(self,notificator,responsible):
2458        """notifies the abstract responsibles with a matching template
2459        """
2460        tpl=self.getOwner().getNotifTplForAbstract(self)
2461        if not tpl:
2462            return
2463        notificator.notify(self,tpl)
2464        self.getNotificationLog().addEntry(NotifLogEntry(responsible,tpl))
2465
2466    def unMerge(self,responsible,comments=""):
2467        #we must clear any track judgement
2468        self._clearTrackAcceptances()
2469        self._clearTrackRejections()
2470        self._clearTrackReallocations()
2471        self.getCurrentStatus().getTargetAbstract().removeMergeFromAbstract(self)
2472        self.getCurrentStatus().unMerge(responsible,comments)
2473        self._notifyModification()
2474
2475    def getNotificationLog(self):
2476        try:
2477            if self._notifLog:
2478                pass
2479        except AttributeError:
2480            self._notifLog=NotificationLog(self)
2481        return self._notifLog
2482
2483    # Rating methods
2484    def getRating(self):
2485        """ Get the average rating of the abstract """
2486        try:
2487            if self._rating:
2488                pass
2489        except AttributeError:
2490            self._rating = None
2491        return self._rating
2492
2493    def updateRating(self, scale = None):
2494        """
2495            Update the average rating of the abstract which is calculated with the average of each judgement.
2496            If the scale (tuple with lower,higher) is passed, the judgement are re-adjusted to the new scale.
2497        """
2498        self._rating = None
2499        # calculate the total valoration
2500        judNum = 0
2501        ratingSum = 0
2502        for track in self.getTrackListSorted():
2503            for jud in self.getJudgementHistoryByTrack(track):
2504                if scale:
2505                    # calculate the new values for each judgement
2506                    scaleLower, scaleHigher = scale
2507                    jud.recalculateJudgementValues(scaleLower, scaleHigher)
2508                if jud.getJudValue() != None: # it means there is a numeric value for the judgement
2509                    ratingSum += jud.getJudValue()
2510                    judNum += 1
2511        # Calculate the average
2512        if judNum != 0:
2513            self._rating = float(ratingSum) / judNum
2514
2515    def getQuestionsAverage(self):
2516        '''Get the list of questions answered in the reviews for an abstract '''
2517        dTotals = {} # {idQ1: total_value, idQ2: total_value ...}
2518        dTimes = {} # {idQ1: times_answered, idQ2: times_answered}
2519        for track in self.getTrackListSorted():
2520            for jud in self.getJudgementHistoryByTrack(track):
2521                for answer in jud.getAnswers():
2522                    # check if the question is in d and sum the answers value or insert in d the new question
2523                    if dTotals.has_key(answer.getQuestion().getText()):
2524                        dTotals[answer.getQuestion().getText()] += answer.getValue()
2525                        dTimes[answer.getQuestion().getText()] += 1
2526                    else: # first time
2527                        dTotals[answer.getQuestion().getText()] = answer.getValue()
2528                        dTimes[answer.getQuestion().getText()] = 1
2529        # get the questions average
2530        questionsAverage = {}
2531        for q, v in dTotals.iteritems():
2532            # insert the element and calculate the average for the value
2533            questionsAverage[q] = float(v)/dTimes[q]
2534        return questionsAverage
2535
2536    def removeAnswersOfQuestion(self, questionId):
2537        ''' Remove the answers of the question with questionId value '''
2538        for track in self.getTrackListSorted():
2539            for jud in self.getJudgementHistoryByTrack(track):
2540                jud.removeAnswer(questionId)
2541
2542    def getRatingPerReviewer(self, user, track):
2543        """
2544            Get the rating of the user for the abstract in the track given.
2545        """
2546        for jud in self.getJudgementHistoryByTrack(track):
2547            if (jud.getResponsible() == user):
2548                return jud.getJudValue()
2549
2550    def _getAttachmentsCounter(self):
2551        try:
2552            if self._attachmentsCounter:
2553                pass
2554        except AttributeError:
2555            self._attachmentsCounter = Counter()
2556        return self._attachmentsCounter.newCount()
2557
2558    def setAttachments(self, attachments):
2559        self._attachments = attachments
2560
2561    def getAttachments(self):
2562        try:
2563            if self._attachments:
2564                pass
2565        except AttributeError:
2566            self._attachments = {}
2567        return self._attachments
2568
2569    def getAttachmentById(self, id):
2570        return self.getAttachments().get(id, None)
2571
2572
2573class AbstractJudgement( Persistent ):
2574    """This class represents each of the judgements made by a track about a
2575        certain abstract. Each track for which an abstract is proposed can
2576        make a judgement proposing the abstract to be accepted or rejected.
2577        Different track judgements must be kept so the referees who have to
2578        take the final decission can overview different opinions from the
2579        track coordinators.
2580      Together with the judgement some useful information like the date when
2581        it was done and the user who did it will be kept.
2582    """
2583
2584    def __init__( self, track, responsible, answers ):
2585        self._track = track
2586        self._setResponsible( responsible )
2587        self._date = nowutc()
2588        self._comment = ""
2589        self._answers = answers
2590        self._judValue = self.calculateJudgementAverage() # judgement average value
2591        self._totalJudValue = self.calculateAnswersTotalValue()
2592
2593
2594    def _setResponsible( self, newRes ):
2595        self._responsible = newRes
2596
2597    def getResponsible( self ):
2598        return self._responsible
2599
2600    def getDate( self ):
2601        return self._date
2602
2603    def setDate(self, date):
2604        self._date = date
2605
2606    def getTrack( self ):
2607        return self._track
2608
2609    def setComment( self, newComment ):
2610        self._comment = newComment.strip()
2611
2612    def getComment( self ):
2613        return self._comment
2614
2615    def getAnswers(self):
2616        try:
2617            if self._answers:
2618                pass
2619        except AttributeError:
2620            self._answers = []
2621        return self._answers
2622
2623    def calculateJudgementAverage(self):
2624        '''Calculate the average value of the given answers'''
2625        result = 0
2626        if (len(self.getAnswers()) != 0):
2627            # convert the values into float types
2628            floatList = [ans.getValue() for ans in self._answers]
2629            result = sum(floatList) / float(len(floatList)) # calculate the average
2630        else:
2631            # there are no questions
2632            result = None
2633        return result
2634
2635    def getJudValue(self):
2636        try:
2637            if self._judValue:
2638                pass
2639        except AttributeError:
2640            self._judValue = self.calculateJudgementAverage() # judgement average value
2641        return self._judValue
2642
2643    def getTotalJudValue(self):
2644        try:
2645            if self._totalJudValue:
2646                pass
2647        except AttributeError:
2648            self._totalJudValue = self.calculateAnswersTotalValue()
2649        return self._totalJudValue
2650
2651    def calculateAnswersTotalValue(self):
2652        ''' Calculate the sum of all the ratings '''
2653        result = 0
2654        for ans in self.getAnswers():
2655            result += ans.getValue()
2656        return result
2657
2658    def recalculateJudgementValues(self, scaleLower, scaleHigher):
2659        ''' Update the values of the judgement. This function is called when the scale is changed.'''
2660        for ans in self.getAnswers():
2661            ans.calculateRatingValue(scaleLower, scaleHigher)
2662        self._judValue = self.calculateJudgementAverage()
2663        self._totalJudValue = self.calculateAnswersTotalValue()
2664
2665    def removeAnswer(self, questionId):
2666        ''' Remove the current answers of the questionId '''
2667        for ans in self.getAnswers():
2668            if ans.getQuestion().getId() == questionId:
2669                self._answers.remove(ans)
2670                self._notifyModification()
2671
2672    def _notifyModification(self):
2673        self._p_changed = 1
2674
2675
2676class AbstractAcceptance( AbstractJudgement ):
2677
2678    def __init__( self, track, responsible, contribType, answers ):
2679        AbstractJudgement.__init__( self, track, responsible, answers )
2680        self._contribType = contribType
2681
2682    def clone(self,track):
2683        aa = AbstractAcceptance(track,self.getResponsible(), self.getContribType(), self.getAnswers())
2684        return aa
2685
2686    def getContribType( self ):
2687        try:
2688            if self._contribType:
2689                pass
2690        except AttributeError, e:
2691            self._contribType = None
2692        return self._contribType
2693
2694
2695class AbstractRejection( AbstractJudgement ):
2696
2697    def clone(self, track):
2698        arj = AbstractRejection(track,self.getResponsible(), self.getAnswers())
2699        return arj
2700
2701class AbstractReallocation( AbstractJudgement ):
2702
2703    def __init__( self, track, responsible, propTracks, answers ):
2704        AbstractJudgement.__init__( self, track, responsible, answers )
2705        self._proposedTracks = PersistentList( propTracks )
2706
2707    def clone(self, track):
2708        arl = AbstractReallocation(track, self.getResponsible(), self.getProposedTrackList(), self.getAnswers())
2709        return arl
2710
2711    def getProposedTrackList( self ):
2712        return self._proposedTracks
2713
2714class AbstractMarkedAsDuplicated( AbstractJudgement ):
2715
2716    def __init__( self, track, responsible, originalAbst, answers ):
2717        AbstractJudgement.__init__( self, track, responsible, answers )
2718        self._originalAbst=originalAbst
2719
2720    def clone(self,track):
2721        amad = AbstractMarkedAsDuplicated(track,self.getResponsible(), self.getOriginalAbstract(), self.getAnswers())
2722        return amad
2723
2724    def getOriginalAbstract(self):
2725        return self._originalAbst
2726
2727
2728class AbstractUnMarkedAsDuplicated( AbstractJudgement ):
2729
2730    def clone(self,track):
2731        auad = AbstractUnMarkedAsDuplicated(track,self.getResponsible())
2732        return auad
2733
2734
2735class AbstractStatus( Persistent ):
2736    """This class represents any of the status in which an abstract can be.
2737        From the moment they are submitted (and therefore created), abstracts
2738        can go throuugh different status each having a different meaning.
2739       As there can be many status, the transitions between them are quite
2740        complex and as the system evolves we could require to add or delete
2741        new status the "Status" pattern is applied. This is the base class.
2742       Apart from giving information about the status of an abstract, this
2743        class is responsible to store information about how the status was
2744        reached (who provoke the transition, when, ...).
2745    """
2746    _name = ""
2747
2748    def __init__( self, abstract ):
2749        self._setAbstract( abstract )
2750        self._setDate( nowutc() )
2751
2752    def getName(self):
2753        return self._name
2754
2755    def _setAbstract( self, abs ):
2756        self._abstract = abs
2757
2758    def getAbstract( self ):
2759        return self._abstract
2760
2761    def _setDate( self, date ):
2762        self._date = date
2763
2764    def getDate( self ):
2765        return self._date
2766
2767    def accept(self,responsible,destTrack,type,comments=""):
2768        """
2769        """
2770        s = AbstractStatusAccepted(self.getAbstract(),responsible,destTrack,type,comments)
2771        self.getAbstract().setCurrentStatus( s )
2772
2773    def reject( self, responsible, comments = "" ):
2774        """
2775        """
2776        s = AbstractStatusRejected( self.getAbstract(), responsible, comments )
2777        self.getAbstract().setCurrentStatus( s )
2778
2779    def _getStatusClass( self ):
2780        """
2781        """
2782        numAccepts = self._abstract.getNumProposedToAccept()
2783        numJudgements = self._abstract.getNumJudgements()
2784        if numJudgements>0:
2785            numTracks = self._abstract.getNumTracks()
2786            if numTracks == numJudgements:
2787                if numAccepts == 1:
2788                    s = AbstractStatusProposedToAccept
2789                elif numAccepts == 0:
2790                    s = AbstractStatusProposedToReject
2791                else:
2792                    s = AbstractStatusInConflict
2793            else:
2794                s=AbstractStatusUnderReview
2795        else:
2796            s=AbstractStatusSubmitted
2797        return s
2798
2799
2800    def update( self ):
2801        """
2802        """
2803        newStatusClass = self._getStatusClass()
2804        if self.__class__ != newStatusClass:
2805            self.getAbstract().setCurrentStatus( newStatusClass( self._abstract ) )
2806
2807    def proposeToAccept( self ):
2808        """
2809        """
2810        s = self._getStatusClass()( self._abstract )
2811        self.getAbstract().setCurrentStatus( s )
2812
2813    def proposeToReject( self ):
2814        """
2815        """
2816        s = self._getStatusClass()( self._abstract )
2817        self.getAbstract().setCurrentStatus( s )
2818
2819    def proposeToReallocate( self ):
2820        """
2821        """
2822        s = self._getStatusClass()( self._abstract )
2823        self.getAbstract().setCurrentStatus( s )
2824
2825    def withdraw(self,resp,comments=""):
2826        """
2827        """
2828        s=AbstractStatusWithdrawn(self.getAbstract(), resp, self, comments)
2829        self.getAbstract().setCurrentStatus(s)
2830
2831    def recover( self ):
2832        """
2833        """
2834        raise MaKaCError( _("only withdrawn abstracts can be recovered"))
2835
2836    def markAsDuplicated(self,responsible,originalAbs,comments=""):
2837        """
2838        """
2839        if self.getAbstract()==originalAbs:
2840            raise MaKaCError( _("the original abstract is the same as the duplicated one"))
2841        if isinstance(originalAbs.getCurrentStatus(),AbstractStatusDuplicated):
2842            raise MaKaCError( _("cannot set as original abstract one which is already marked as duplicated"))
2843        s=AbstractStatusDuplicated(self.getAbstract(),responsible,originalAbs,comments)
2844        self.getAbstract().setCurrentStatus(s)
2845
2846    def unMarkAsDuplicated(self,responsible,comments=""):
2847        """
2848        """
2849        raise MaKaCError( _("Only duplicated abstract can be unmark as duplicated"))
2850
2851    def mergeInto(self,responsible,targetAbs,comments=""):
2852        """
2853        """
2854        if self.getAbstract()==targetAbs:
2855            raise MaKaCError( _("An abstract cannot be merged into itself"))
2856        if targetAbs.getCurrentStatus().__class__ not in [AbstractStatusSubmitted,AbstractStatusUnderReview,AbstractStatusProposedToAccept,AbstractStatusProposedToReject,AbstractStatusInConflict]:
2857            raise MaKaCError(_("Target abstract is in a status which cannot receive mergings"))
2858        s=AbstractStatusMerged(self.getAbstract(),responsible,targetAbs,comments)
2859        self.getAbstract().setCurrentStatus(s)
2860
2861    def unMerge(self,responsible,comments=""):
2862        """
2863        """
2864        raise MaKaCError( _("Only merged abstracts can be unmerged"))
2865
2866
2867
2868class AbstractStatusSubmitted( AbstractStatus ):
2869    """
2870    """
2871
2872    def clone(self,abstract):
2873        ass = AbstractStatusSubmitted(abstract)
2874        return ass
2875
2876    def update( self ):
2877        #if an abstract that has been submitted has no judgement it
2878        #   must remain in the submitted status
2879        if self._abstract.getNumJudgements() == 0:
2880            return
2881        AbstractStatus.update( self )
2882
2883
2884class AbstractStatusAccepted( AbstractStatus ):
2885    """
2886    """
2887    def __init__(self,abstract,responsible,destTrack,type,comments=""):
2888        AbstractStatus.__init__( self, abstract )
2889        self._setResponsible( responsible )
2890        self._setTrack( destTrack )
2891        self._setComments( comments )
2892        self._setType( type )
2893        self._contrib = None
2894
2895    def clone(self,abstract):
2896        asa = AbstractStatusAccepted(abstract,self.getResponsible(), self.getTrack(), self.getType(), self.getComments())
2897        return asa
2898
2899    def _setResponsible( self, res ):
2900        self._responsible = res
2901
2902    def getResponsible( self ):
2903        return self._responsible
2904
2905    def _setComments( self, comments ):
2906        self._comments = str( comments ).strip()
2907
2908    def getComments( self ):
2909        try:
2910            if self._comments:
2911                pass
2912        except AttributeError:
2913            self._comments = ""
2914        return self._comments
2915
2916    def _setTrack( self, track ):
2917        self._track = track
2918
2919    def getTrack( self ):
2920        try:
2921            if self._track:
2922                pass
2923        except AttributeError:
2924            self._track = None
2925        return self._track
2926
2927    def _setType( self, type ):
2928        self._contribType = type
2929
2930    def getType( self ):
2931        try:
2932            if self._contribType:
2933                pass
2934        except AttributeError:
2935            self._contribType = None
2936        return self._contribType
2937
2938    def setContribution( self, newContrib ):
2939        self._contrib = newContrib
2940
2941    def getContribution( self ):
2942        try:
2943            if self._contrib:
2944                pass
2945        except AttributeError:
2946            self._contrib = None
2947        return self._contrib
2948
2949    def update( self ):
2950        return
2951
2952    def accept(self,responsible,destTrack,type,comments="" ):
2953        raise MaKaCError( _("Cannot accept an abstract which is already accepted"))
2954
2955    def reject( self, responsible, comments="" ):
2956        raise MaKaCError( _("Cannot reject an abstract which is already accepted"))
2957
2958    def proposeToAccept( self ):
2959        raise MaKaCError( _("Cannot propose for acceptance an abstract which is already accepted"))
2960
2961    def proposeToReject( self ):
2962        raise MaKaCError( _("Cannot propose for rejection an abstract which is already accepted"))
2963
2964    def proposeToReallocate( self ):
2965        raise MaKaCError( _("Cannot propose for reallocation an abstract which is already accepted"))
2966
2967    #def withdraw( self, comments="" ):
2968    #    raise MaKaCError( "Cannot withdraw an ACCEPTED abstract" )
2969
2970    def markAsDuplicated(self,responsible,originalAbs,comments=""):
2971        raise MaKaCError( _("Cannot mark as duplicated an abstract which is accepted"))
2972
2973    def unMarkAsDuplicated(self,responsible,comments=""):
2974        """
2975        """
2976        raise MaKaCError( _("Only duplicated abstract can be unmark as duplicated"))
2977
2978    def mergeInto(self,responsible,targetAbs,comments=""):
2979        raise MaKaCError( _("Cannot merge an abstract which is already accepted"))
2980
2981    def withdraw(self,resp,comments=""):
2982        """
2983        """
2984        contrib=self.getContribution()
2985        #this import is made here and not at the top of the file in order to
2986        #   avoid recursive import troubles
2987        from MaKaC.conference import ContribStatusWithdrawn
2988        if contrib is not None and \
2989                not isinstance(contrib.getCurrentStatus(),ContribStatusWithdrawn):
2990            contrib.withdraw(resp, i18nformat(""" _("abstract withdrawn"): %s""")%comments)
2991        AbstractStatus.withdraw(self,resp,comments)
2992
2993
2994class AbstractStatusRejected( AbstractStatus ):
2995    """
2996    """
2997    def __init__( self, abstract, responsible, comments = "" ):
2998        AbstractStatus.__init__( self, abstract )
2999        self._setResponsible( responsible )
3000        self._setComments( comments )
3001
3002    def clone(self,abstract):
3003        asr = AbstractStatusRejected(abstract, self.getResponsible(), self.getComments())
3004        return asr
3005
3006    def _setResponsible( self, res ):
3007        self._responsible = res
3008
3009    def getResponsible( self ):
3010        return self._responsible
3011
3012    def _setComments( self, comments ):
3013        self._comments = str( comments ).strip()
3014
3015    def getComments( self ):
3016        try:
3017            if self._comments:
3018                pass
3019        except AttributeError:
3020            self._comments = ""
3021        return self._comments
3022
3023    def update( self ):
3024        return
3025
3026    def reject( self, responsible, comments="" ):
3027        raise MaKaCError(  _("Cannot reject an abstract which is already rejected"))
3028
3029    def proposeToAccept( self ):
3030        raise MaKaCError(  _("Cannot propose for acceptance an abstract which is already rejected"))
3031
3032    def proposeToReject( self ):
3033        raise MaKaCError(  _("Cannot propose for rejection an abstract which is already rejected"))
3034
3035    def proposeToReallocate( self ):
3036        raise MaKaCError(  _("Cannot propose for reallocation an abstract which is already rejected"))
3037
3038    def withdraw(self,resp,comments=""):
3039        raise MaKaCError(  _("Cannot withdraw a REJECTED abstract"))
3040
3041    def markAsDuplicated(self,responsible,originalAbs,comments=""):
3042        raise MaKaCError( _("Cannot mark as duplicated an abstract which is rejected"))
3043
3044    def unMarkAsDuplicated(self,responsible,comments=""):
3045        """
3046        """
3047        raise MaKaCError( _("Only duplicated abstract can be unmark as duplicated"))
3048
3049    def mergeInto(self,responsible,targetAbs,comments=""):
3050        raise MaKaCError( _("Cannot merge an abstract which is rejected"))
3051
3052
3053class AbstractStatusUnderReview( AbstractStatus ):
3054    """
3055    """
3056    def clone(self,abstract):
3057        asur = AbstractStatusUnderReview(abstract)
3058        return asur
3059
3060class AbstractStatusProposedToAccept( AbstractStatus ):
3061    """
3062    """
3063    def clone(self, abstract):
3064        aspta = AbstractStatusProposedToAccept(abstract)
3065        return aspta
3066
3067    def getTrack(self):
3068        jud=self.getAbstract().getTrackAcceptanceList()[0]
3069        return jud.getTrack()
3070
3071    def getType(self):
3072        jud=self.getAbstract().getTrackAcceptanceList()[0]
3073        return jud.getContribType()
3074
3075
3076class AbstractStatusProposedToReject( AbstractStatus ):
3077    """
3078    """
3079    def clone(self, abstract):
3080        asptr = AbstractStatusProposedToReject(abstract)
3081        return asptr
3082
3083class AbstractStatusInConflict( AbstractStatus ):
3084    """
3085    """
3086    def clone(self,abstract):
3087        asic = AbstractStatusInConflict(abstract)
3088        return asic
3089
3090class AbstractStatusWithdrawn(AbstractStatus):
3091    """
3092    """
3093    def __init__(self,abstract,responsible, prevStatus,comments=""):
3094        AbstractStatus.__init__(self,abstract)
3095        self._setComments(comments)
3096        self._setResponsible(responsible)
3097        self._prevStatus=prevStatus
3098
3099    def clone(self,abstract):
3100        asw = AbstractStatusWithdrawn(abstract,self.getResponsible(),self.getComments())
3101        return asw
3102
3103    def _setResponsible(self,newResp):
3104        self._responsible=newResp
3105
3106    def getResponsible(self):
3107        try:
3108            if self._responsible:
3109                pass
3110        except AttributeError,e:
3111            self._responsible=self._abstract.getSubmitter().getAvatar()
3112        return self._responsible
3113
3114    def getPrevStatus(self):
3115        try:
3116            if self._prevStatus:
3117                pass
3118        except AttributeError,e:
3119            self._prevStatus=None
3120        return self._prevStatus
3121
3122    def _setComments( self, comments ):
3123        self._comments = str( comments ).strip()
3124
3125    def getComments( self ):
3126        return self._comments
3127
3128    def update( self ):
3129        return
3130
3131    def accept(self,responsible,destTrack,type,comments=""):
3132        raise MaKaCError( _("Cannot accept an abstract wich is withdrawn"))
3133
3134    def reject( self, responsible, comments="" ):
3135        raise MaKaCError(  _("Cannot reject an abstract which is withdrawn"))
3136
3137    def proposeToAccept( self ):
3138        raise MaKaCError(  _("Cannot propose for acceptance an abstract which withdrawn"))
3139
3140    def proposeToReject( self ):
3141        raise MaKaCError(  _("Cannot propose for rejection an abstract which is withdrawn"))
3142
3143    def recover( self ):
3144        if self.getPrevStatus() is None:
3145            # reset all the judgments
3146            self._clearTrackAcceptances()
3147            self._clearTrackRejections()
3148            self._clearTrackReallocations()
3149            # setting the status
3150            contrib=self.getAbstract().getContribution()
3151            if contrib is None:
3152                s = AbstractStatusSubmitted( self.getAbstract() )
3153            else:
3154                s = AbstractStatusAccepted(self.getAbstract(),self.getResponsible(),contrib.getTrack(),contrib.getType(),"")
3155        else:
3156            contrib=self.getAbstract().getContribution()
3157            if contrib is not None and not isinstance(self.getPrevStatus(), AbstractStatusAccepted):
3158                s = AbstractStatusAccepted(self.getAbstract(),self.getResponsible(),contrib.getTrack(),contrib.getType(),"")
3159            else:
3160                s=self.getPrevStatus()
3161        self.getAbstract().setCurrentStatus( s )
3162
3163    def markAsDuplicated(self,responsible,originalAbs,comments=""):
3164        raise MaKaCError( _("Cannot mark as duplicated an abstract which is withdrawn"))
3165
3166    def unMarkAsDuplicated(self,responsible,comments=""):
3167        """
3168        """
3169        raise MaKaCError( _("Only duplicated abstract can be unmark as duplicated"))
3170
3171    def mergeInto(self,responsible,targetAbs,comments=""):
3172        raise MaKaCError( _("Cannot merge an abstract which is withdrawn"))
3173
3174    def withdraw(self,resp,comments=""):
3175        raise MaKaCError( _("This abstract is already withdrawn"))
3176
3177
3178
3179class AbstractStatusDuplicated(AbstractStatus):
3180    """
3181    """
3182    def __init__( self,abstract,responsible,originalAbstract,comments=""):
3183        AbstractStatus.__init__(self,abstract)
3184        self._setResponsible(responsible)
3185        self._setComments(comments)
3186        self._setOriginalAbstract(originalAbstract)
3187
3188    def clone(self, abstract):
3189        asd = AbstractStatusDuplicated(abstract,self.getResponsible(),self.getOriginal(),self.getComments())
3190        return asd
3191
3192    def _setResponsible( self, res ):
3193        self._responsible = res
3194
3195    def getResponsible(self):
3196        return self._responsible
3197
3198    def _setComments( self, comments ):
3199        self._comments = str( comments ).strip()
3200
3201    def getComments( self ):
3202        return self._comments
3203
3204    def _setOriginalAbstract(self,abs):
3205        self._original=abs
3206
3207    def getOriginal(self):
3208        return self._original
3209
3210    def update( self ):
3211        return
3212
3213    def reject( self, responsible, comments="" ):
3214        raise MaKaCError( _("Cannot reject an abstract which is duplicated"))
3215
3216    def proposeToAccept( self ):
3217        raise MaKaCError( _("Cannot propose for acceptance an abstract which is duplicated"))
3218
3219    def proposeToReject( self ):
3220        raise MaKaCError( _("Cannot propose for rejection an abstract which is duplicated"))
3221
3222    def proposeToReallocate( self ):
3223        raise MaKaCError( _("Cannot propose for reallocation an abstract which is duplicated"))
3224
3225    def withdraw(self,resp,comments=""):
3226        raise MaKaCError( _("Cannot withdraw a duplicated abstract"))
3227
3228    def markAsDuplicated(self,responsible,originalAbs,comments=""):
3229        raise MaKaCError( _("This abstract is already duplicated"))
3230
3231    def unMarkAsDuplicated(self,responsible,comments=""):
3232        s = AbstractStatusSubmitted( self.getAbstract() )
3233        self.getAbstract().setCurrentStatus( s )
3234
3235    def mergeInto(self,responsible,targetAbs,comments=""):
3236        raise MaKaCError( _("Cannot merge an abstract which is marked as a duplicate"))
3237
3238
3239class AbstractStatusMerged(AbstractStatus):
3240    """
3241    """
3242
3243    def __init__(self,abstract,responsible,targetAbstract,comments=""):
3244        AbstractStatus.__init__(self,abstract)
3245        self._setResponsible(responsible)
3246        self._setComments(comments)
3247        self._setTargetAbstract(targetAbstract)
3248
3249    def clone(self,abstract):
3250        asm = AbstractStatusMerged(abstract,self.getResponsible(),self.getTargetAbstract(),self.getComments())
3251        return asm
3252
3253    def _setResponsible( self, res ):
3254        self._responsible = res
3255
3256    def getResponsible( self ):
3257        return self._responsible
3258
3259    def _setComments( self, comments ):
3260        self._comments = str( comments ).strip()
3261
3262    def getComments( self ):
3263        return self._comments
3264
3265    def _setTargetAbstract(self,abstract):
3266        self._target=abstract
3267
3268    def getTargetAbstract(self):
3269        return self._target
3270
3271    def update( self ):
3272        return
3273
3274    def reject( self, responsible, comments="" ):
3275        raise MaKaCError( _("Cannot reject an abstract which is merged into another one"))
3276
3277    def proposeToAccept( self ):
3278        raise MaKaCError( _("Cannot propose for acceptance an abstract which is merged into another one"))
3279
3280    def proposeToReject( self ):
3281        raise MaKaCError( _("Cannot propose for rejection an abstract which is merged into another one"))
3282
3283    def proposeToReallocate( self ):
3284        raise MaKaCError( _("Cannot propose for reallocation an abstract which is merged into another one"))
3285
3286    def withdraw(self,resp,comments=""):
3287        raise MaKaCError( _("Cannot withdraw an abstract which is merged into another one"))
3288
3289    def markAsDuplicated(self,responsible,originalAbs,comments=""):
3290        raise MaKaCError( _("Cannot mark as duplicated an abstract which is merged into another one"))
3291
3292    def unMarkAsDuplicated(self,responsible,comments=""):
3293        """
3294        """
3295        raise MaKaCError( _("Only duplicated abstract can be unmark as duplicated"))
3296
3297    def mergeInto(self,responsible,target,comments=""):
3298        raise MaKaCError( _("This abstract is already merged into another one"))
3299
3300    def unMerge(self,responsible,comments=""):
3301        s = AbstractStatusSubmitted( self.getAbstract() )
3302        self.getAbstract().setCurrentStatus( s )
3303
3304class AbstractStatusNone(AbstractStatus):
3305# This is a special status we assign to abstracts that are put in the trash can.
3306
3307    def __init__(self,abstract):
3308        AbstractStatus.__init__(self,abstract)
3309
3310    def clone(self,abstract):
3311        asn = AbstractStatusNone(abstract)
3312        return asn
3313
3314class NotificationTemplate(Persistent):
3315
3316    def __init__(self):
3317        self._owner=None
3318        self._id=""
3319        self._name=""
3320        self._description=""
3321        self._tplSubject=""
3322        self._tplBody=""
3323        self._fromAddr = ""
3324        self._CAasCCAddr = False
3325        self._ccAddrList=PersistentList()
3326        self._toAddrs = PersistentList()
3327        self._conditions=PersistentList()
3328        self._toAddrGenerator=Counter()
3329        self._condGenerator=Counter()
3330
3331    def clone(self):
3332        tpl = NotificationTemplate()
3333        tpl.setName(self.getName())
3334        tpl.setDescription(self.getDescription())
3335        tpl.setTplSubject(self.getTplSubject())
3336        tpl.setTplBody(self.getTplBody())
3337        tpl.setFromAddr(self.getFromAddr())
3338        tpl.setCAasCCAddr(self.getCAasCCAddr())
3339
3340        for cc in self.getCCAddrList() :
3341            tpl.addCCAddr(cc)
3342        for to in self.getToAddrList() :
3343            tpl.addToAddr(to)
3344
3345        for con in self.getConditionList() :
3346            tpl.addCondition(con.clone(tpl))
3347
3348        # kto to jest OWNER..??
3349        # bo nie konferencja..!!
3350
3351        return tpl
3352
3353    def delete(self):
3354        self.clearToAddrs()
3355        self.clearCCAddrList()
3356        self.clearConditionList()
3357        TrashCanManager().add(self)
3358
3359    def recover(self):
3360        TrashCanManager().remove(self)
3361
3362##    def getResponsible( self ):
3363##        return self._responsible
3364##
3365##    def _setComments( self, comments ):
3366##        self._comments = str( comments ).strip()
3367##
3368##    def getComments( self ):
3369##        return self._comments
3370##
3371##    def _setOriginalAbstract(self,abstract):
3372##        self._original=abstract
3373
3374    def canModify(self, aw):
3375        return self.getConference().canModify(aw)
3376
3377    def getLocator(self):
3378        loc = self.getOwner().getConference().getLocator()
3379        loc["notifTplId"] = self._id
3380        return loc
3381
3382    def getConference(self):
3383        return self._owner.getConference()
3384
3385    def includeInOwner(self,owner,id):
3386        self._owner=owner
3387        self._id=id
3388
3389    def getOwner(self):
3390        return self._owner
3391
3392    def getId(self):
3393        return self._id
3394
3395    def setName(self,newName):
3396        self._name=newName.strip()
3397
3398    def getName(self):
3399        return self._name
3400
3401    def setDescription(self,newDesc):
3402        self._description=newDesc.strip()
3403
3404    def getDescription(self):
3405        return self._description
3406
3407    def setTplSubject(self,newSubject, varList):
3408        self._tplSubject=self.parseTplContent(newSubject, varList).strip()
3409
3410    def getTplSubject(self):
3411        return self._tplSubject
3412
3413    def getTplSubjectShow(self, varList):
3414        return self.parseTplContentUndo(self._tplSubject, varList)
3415
3416    def setTplBody(self,newBody, varList):
3417        self._tplBody=self.parseTplContent(newBody, varList).strip()
3418
3419    def getTplBody(self):
3420        return self._tplBody
3421
3422    def getTplBodyShow(self, varList):
3423        return self.parseTplContentUndo(self._tplBody, varList)
3424
3425    def getCCAddrList(self):
3426        try:
3427            if self._ccAddrList:
3428                pass
3429        except AttributeError:
3430            self._ccAddrList=PersistentList()
3431        return self._ccAddrList
3432
3433    def addCCAddr(self,newAddr):
3434        try:
3435            if self._ccAddrList:
3436                pass
3437        except AttributeError:
3438            self._ccAddrList=PersistentList()
3439        ccAddr=newAddr.strip()
3440        if ccAddr!="" and ccAddr not in self._ccAddrList:
3441            self._ccAddrList.append(ccAddr)
3442
3443    def setCCAddrList(self,l):
3444        self.clearCCAddrList()
3445        for addr in l:
3446            self.addCCAddr(addr)
3447
3448    def setCAasCCAddr(self, CAasCCAddr):
3449        self._CAasCCAddr = CAasCCAddr
3450
3451    def getCAasCCAddr(self):
3452        try:
3453            if self._CAasCCAddr:
3454                pass
3455        except AttributeError:
3456            self._CAasCCAddr = False
3457        return self._CAasCCAddr
3458
3459    def clearCCAddrList(self):
3460        self._ccAddrList=PersistentList()
3461
3462    def getFromAddr(self):
3463        try:
3464            return self._fromAddr
3465        except AttributeError:
3466            self._fromAddr = self._owner.getConference().getSupportEmail()
3467            return self._fromAddr
3468
3469    def setFromAddr(self, addr):
3470        self._fromAddr = addr
3471
3472    def addToAddr(self,toAddr):
3473        """
3474        """
3475        if self.hasToAddr(toAddr.__class__):
3476            return
3477        try:
3478            if self._toAddrGenerator:
3479                pass
3480        except AttributeError, e:
3481            self._toAddrGenerator =  Counter()
3482        id = toAddr.getId()
3483        if id == -1:
3484            id = int(self._toAddrGenerator.newCount())
3485        toAddr.includeInTpl(self,id)
3486        self.getToAddrList().append(toAddr)
3487
3488    def removeToAddr(self,toAddr):
3489        """
3490        """
3491        if not self.hasToAddr(toAddr.__class__):
3492            return
3493        self.getToAddrList().remove(toAddr)
3494        toAddr.includeInTpl(None,toAddr.getId())
3495        toAddr.delete()
3496
3497    def recoverToAddr(self, toAddr):
3498        self.addToAddr(toAddr)
3499        toAddr.recover()
3500
3501    def getToAddrs(self, abs):
3502        users = []
3503        for toAddr in self.getToAddrList():
3504            users += toAddr.getToAddrList(abs)
3505        return users
3506
3507    def getToAddrList(self):
3508        """
3509        """
3510        try:
3511            if self._toAddrs:
3512                pass
3513        except AttributeError, e:
3514            self._toAddrs = PersistentList()
3515        return self._toAddrs
3516
3517    def getToAddrById(self,id):
3518        """
3519        """
3520        for toAddr in self.getToAddrList():
3521            if toAddr.getId()==int(id):
3522                return toAddr
3523        return None
3524
3525    def hasToAddr(self,toAddrKlass):
3526        """Returns True if the TPL contains a "toAddr" which class is "toAddrKlass"
3527        """
3528        for toAddr in self.getToAddrList():
3529            if toAddr.__class__ == toAddrKlass:
3530                return True
3531        return False
3532
3533    def clearToAddrs(self):
3534        while(len(self.getToAddrList())>0):
3535            self.removeToAddr(self.getToAddrList()[0])
3536
3537    def addCondition(self,cond):
3538        """
3539        """
3540        if cond in self._conditions:
3541            return
3542        id = cond.getId()
3543        if id == -1:
3544            id = int(self._condGenerator.newCount())
3545        cond.includeInTpl(self, id)
3546        self._conditions.append(cond)
3547
3548    def removeCondition(self,cond):
3549        """
3550        """
3551        if cond not in self._conditions:
3552            return
3553        self._conditions.remove(cond)
3554        cond.delete()
3555
3556    def recoverCondition(self, cond):
3557        self.addCondition(cond)
3558        cond.recover()
3559
3560    def getConditionList(self):
3561        """
3562        """
3563        return self._conditions
3564
3565    def getConditionById(self,id):
3566        """
3567        """
3568        for cond in self._conditions:
3569            if cond.getId()==int(id):
3570                return cond
3571        return None
3572
3573    def clearConditionList(self):
3574        while(len(self.getConditionList())>0):
3575            self.removeCondition(self.getConditionList()[0])
3576
3577    def satisfies(self,abs):
3578        """
3579        """
3580        for cond in self._conditions:
3581            if cond.satisfies(abs):
3582                return True
3583        return False
3584
3585    def parseTplContent(self, content, varList):
3586        # replace the % in order to avoid exceptions
3587        result = content.replace("%", "%%")
3588        # find the vars and make the expressions, it is necessary to do in reverse in order to find the longest tags first
3589        for var in varList:
3590            result = result.replace("{"+var.getName()+"}", "%("+var.getName()+")s")
3591        return result
3592
3593    def parseTplContentUndo(self, content, varList):
3594        # The body content is shown without "%()" and with "%" in instead of "%%" but it is not modified
3595        result = content
3596        for var in varList:
3597            result = result.replace("%("+var.getName()+")s", "{"+var.getName()+"}")
3598        # replace the %% by %
3599        result = result.replace("%%", "%")
3600        return result
3601
3602    def getModifKey( self ):
3603        return self.getConference().getModifKey()
3604
3605
3606
3607class NotifTplToAddr(Persistent):
3608    """
3609    """
3610
3611    def __init__(self):
3612        self._tpl=None
3613        self._id=-1
3614
3615    def clone(self):
3616        ntta = NotifTplToAddr()
3617        return ntta
3618
3619    def delete(self):
3620        TrashCanManager().add(self)
3621
3622    def recover(self):
3623        TrashCanManager().remove(self)
3624
3625    def includeInTpl(self,newTpl,newId):
3626        self._tpl=newTpl
3627        self._id=newId
3628
3629    def getTpl(self):
3630        return self._tpl
3631
3632    def getId(self):
3633        return self._id
3634
3635    def getToAddrList(self,absList):
3636        """
3637        Return a list with all the emails for a group.
3638        """
3639        return []
3640
3641
3642class NotifTplToAddrSubmitter(NotifTplToAddr):
3643
3644    def getToAddrList(self,abs):
3645        l = []
3646        l.append(abs.getSubmitter())
3647        return l
3648
3649    def clone(self):
3650        nttas = NotifTplToAddrSubmitter()
3651        return nttas
3652
3653class NotifTplToAddrPrimaryAuthors(NotifTplToAddr):
3654
3655    def getToAddrList(self,abs):
3656        l = []
3657        for pa in abs.getPrimaryAuthorList():
3658            l.append(pa)
3659        return l
3660
3661    def clone(self):
3662        nttapa = NotifTplToAddrPrimaryAuthors()
3663        return nttapa
3664
3665class NotifTplCondition(Persistent):
3666    """
3667    """
3668
3669    def __init__(self):
3670        self._tpl=None
3671        self._id=-1
3672
3673    def clone(self, template):
3674        con = NotifyCondition()
3675        con.includeInTpl(template)
3676        return con
3677
3678    def delete(self):
3679        TrashCanManager().add(self)
3680
3681    def recover(self):
3682        TrashCanManager().remove(self)
3683
3684    def includeInTpl(self,newTpl,newId):
3685        self._tpl=newTpl
3686        self._id=newId
3687
3688    def getTpl(self):
3689        return self._tpl
3690
3691    def getId(self):
3692        return self._id
3693
3694    def satisfies(self,abs):
3695        return True
3696
3697
3698class NotifTplCondAccepted(NotifTplCondition):
3699
3700    def __init__(self,track="--any--",contribType="--any--"):
3701        NotifTplCondition.__init__(self)
3702        self._track=track
3703        self._contribType=contribType
3704
3705    def clone(self, conference, template):
3706        ntca = NotifTplCondAccepted()
3707        for newtrack in conference.getTrackList() :
3708            if newtrack.getTitle() == self.getTrack().getTitle() :
3709                ntca.setTrack(newtrack)
3710        for newtype in conference.getContribTypeList() :
3711             if newtype.getName() == self.getContribType() :
3712                ntca.setContribType(newtype)
3713
3714        return ntca
3715
3716    def setContribType(self, ct="--any--"):
3717        self._contribType = ct
3718
3719    def getContribType(self):
3720        return self._contribType
3721
3722    def setTrack(self, tr="--any--"):
3723        self._track = tr
3724
3725    def getTrack(self):
3726        try:
3727            if self._track:
3728                pass
3729        except AttributeError:
3730            self._track="--any--"
3731        return self._track
3732
3733    def _satifiesContribType(self,abs):
3734        status=abs.getCurrentStatus()
3735        if self._contribType=="--any--":
3736            return True
3737        else:
3738            if self._contribType=="" or self._contribType==None or \
3739                                            self._contribType=="--none--":
3740                return status.getType()=="" or status.getType()==None
3741            return status.getType()==self._contribType
3742        return False
3743
3744    def _satifiesTrack(self,abs):
3745        status=abs.getCurrentStatus()
3746        if self.getTrack()=="--any--":
3747            return True
3748        else:
3749            if self.getTrack()=="" or self.getTrack() is None or \
3750                                            self.getTrack()=="--none--":
3751                return status.getTrack()=="" or status.getTrack()==None
3752            return status.getTrack()==self.getTrack()
3753        return False
3754
3755    def satisfies(self,abs):
3756        if not isinstance(abs.getCurrentStatus(),AbstractStatusAccepted):
3757            return False
3758        else:
3759            return self._satifiesContribType(abs) and self._satifiesTrack(abs)
3760
3761
3762class NotifTplCondRejected(NotifTplCondition):
3763
3764    def satisfies(self,abs):
3765        return isinstance(abs.getCurrentStatus(),AbstractStatusRejected)
3766
3767    def clone(self, conference, template):
3768        ntcr = NotifTplCondRejected()
3769        ntcr.includeInTpl(template)
3770        return ntcr
3771
3772class NotifTplCondMerged(NotifTplCondition):
3773
3774    def satisfies(self,abs):
3775        return isinstance(abs.getCurrentStatus(),AbstractStatusMerged)
3776
3777    def clone(self, conference, template):
3778        ntcm = NotifTplCondMerged()
3779        ntcm.includeInTpl(newTpl, newId)
3780
3781class NotificationLog(Persistent):
3782
3783    def __init__(self,abstract):
3784        self._abstract=abstract
3785        self._entries=PersistentList()
3786
3787    def getAbstract(self):
3788        return self._abstract
3789
3790    def addEntry(self,newEntry):
3791        if newEntry!=None and newEntry not in self._entries:
3792            self._entries.append(newEntry)
3793
3794    def getEntryList(self):
3795        return self._entries
3796
3797    # The 3 following metods are used only for recovery purposes:
3798
3799    def removeEntry(self, entry):
3800        if entry!=None and entry in self._entries:
3801            self._entries.remove(entry)
3802            entry.delete()
3803
3804    def recoverEntry(self, entry):
3805        self.addEntry(entry)
3806        entry.recover()
3807
3808    def clearEntryList(self):
3809        while len(self.getEntryList()) > 0:
3810            self.removeEntry(self.getEntryList()[0])
3811
3812    # -----------------------------------------------------------
3813
3814class NotifLogEntry(Persistent):
3815
3816    def __init__(self,responsible,tpl):
3817        self._setDate(nowutc())
3818        self._setResponsible(responsible)
3819        self._setTpl(tpl)
3820
3821    def _setDate(self,newDate):
3822        self._date=newDate
3823
3824    def getDate(self):
3825        return self._date
3826
3827    def _setResponsible(self,newResp):
3828        self._responsible=newResp
3829
3830    def getResponsible(self):
3831        return self._responsible
3832
3833    def _setTpl(self,newTpl):
3834        self._tpl=newTpl
3835
3836    def getTpl(self):
3837        return self._tpl
3838
3839    def delete(self):
3840        TrashCanManager().add(self)
3841
3842    def recover(self):
3843        TrashCanManager().remove(self)
Note: See TracBrowser for help on using the repository browser.