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

hello-world-walkthroughipv6v0.98-seriesv0.98.2v0.98.3v0.99v1.0v1.1
Last change on this file since b7f6b6 was b7f6b6, checked in by Alberto Resco Perez <alberto.resco.perez@…>, 15 months ago

[FIX] Abstract submission file empty

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