source: indico/indico/MaKaC/review.py @ 7ba548

burotelhello-world-walkthroughipv6v0.98-seriesv0.98.2v0.98.3v0.98b1v0.98b2v0.99v1.0v1.1
Last change on this file since 7ba548 was 7ba548, checked in by Jose Benito <jose.benito.gonzalez@…>, 2 years ago

[FIX] Link of contribution with a removed abstract

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