source: indico/indico/MaKaC/reviewing.py @ 2e99d4

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

[BUG FIX] - User competences bug fix

  • there was a tricky bug - if a user has more than 1 role, and then we delete him from some of the reviewers lists - he was not showing in the Competences list at all. To be fixed there are new conditions while deleting a user from a list - if he has other role, then not no delete his competences.
  • Property mode set to 100644
File size: 44.6 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 MaKaC.webinterface.mail import GenericNotification
22from MaKaC.common.info import HelperMaKaCInfo
23from MaKaC.webinterface import urlHandlers
24from MaKaC.common.mail import GenericMailer
25from MaKaC.common.timezoneUtils import getAdjustedDate, nowutc
26from datetime import datetime
27from persistent import Persistent
28from MaKaC.errors import MaKaCError
29import tempfile
30from MaKaC.common import Config
31import conference
32from MaKaC.common.Counter import Counter
33
34###############################################
35# Conference-wide classes
36###############################################
37
38
39class ConferenceReview(Persistent):
40    """
41    This class manages the parameters of the conference reviewing.
42    """
43    reviewingModes = ["", "No reviewing", "Content reviewing", "Layout reviewing", "Content and layout reviewing"]
44    predefinedStates = ["Accept", "To be corrected", "Reject"]
45    reviewingQuestionsAnswers = ["Strongly Disagree", "Disagree", "Weakly Disagree", "Borderline", "Weakly Agree", "Agree", "Strongly Agree"]
46    reviewingQuestionsLabels = ["-3", "", "", "0", "", "", "+3"]
47    initialSelectedAnswer = 3
48
49    def __init__( self, conference):
50        """ Constructor.
51            conference must be a Conference object (not an id).
52        """
53
54        self._conference = conference
55
56        #lists of users with reviewing roles
57        self._reviewersList = []
58        self._editorsList = []
59        self._refereesList = []
60        self._paperReviewManagersList = []
61
62        self._abstractManagerList = []
63        self._abstractReviewersList = []
64
65        self._refereeContribution = {} #key: user, value: list of contributions where user is referee
66        self._editorContribution = {} #key: user, value: list of contributions where user is editor
67        self._reviewerContribution = {} #key: user, value: list of contributions where user is reviewer
68        self._reviewerAsbtract = {} #key: user, value: list of abstracts where user is abstract reviewer
69
70        self.setChoice(1) #initial reviewing mode: no reviewing
71
72        #default dates
73        self._startSubmissionDate = None
74        self._endSubmissionDate = None
75        self._defaultRefereeDueDate = None
76        self._defaultEditorDueDate = None
77        self._defaultReviwerDueDate = None
78        self._defaultAbstractReviwerDueDate = None
79
80        self._reviewableMaterials = ["paper"]
81        from MaKaC.webinterface.materialFactories import MaterialFactoryRegistry
82
83        self._nonReviewableMaterials = MaterialFactoryRegistry._allowedMaterials['conference'] + MaterialFactoryRegistry._allowedMaterials['category']
84
85        self._nonReviewableMaterials.remove("paper")
86
87        self._states = [] # list of content reviewing and final judgement non-default states
88        self._reviewingQuestions = [] #list of content reviewing and final judgement questions
89        self._formCriteriaList = [] #list of layout editing criteria
90        self._templates = {} #dictionary with layout templates. key: id, value: Template object
91        self._templateCounter = Counter(1) #counter to make new id's for the templates
92        self._userCompetences = {} #dictionary with the competences of each user. key: user, value: list of competences
93        self._userCompetencesByTag = {} #dictionary with the users for each competence. key: competence, value: list of users
94
95        self.notifyModification()
96
97
98    def getConference(self):
99        """ Returns the parent conference of the ConferenceReview object
100        """
101        return self._conference
102
103    def setStartSubmissionDate(self, startSubmissionDate):
104        self._startSubmissionDate= datetime(startSubmissionDate.year,startSubmissionDate.month,startSubmissionDate.day,23,59,59)
105
106    def getStartSubmissionDate(self):
107        return self._startSubmissionDate
108
109    def setEndSubmissionDate(self, endSubmissionDate):
110        self._endSubmissionDate = datetime(endSubmissionDate.year,endSubmissionDate.month,endSubmissionDate.day,23,59,59)
111
112    def getEndSubmissionDate(self):
113        return self._endSubmissionDate
114
115
116
117    def setDefaultRefereeDueDate(self, date):
118        self._defaultRefereeDueDate = date
119
120    def getDefaultRefereeDueDate(self):
121        return self._defaultRefereeDueDate
122
123    def getAdjustedDefaultRefereeDueDate(self):
124        if self.getDefaultRefereeDueDate() is None:
125            return None
126        else:
127            return getAdjustedDate(self._defaultRefereeDueDate, self.getConference())
128
129    def setDefaultEditorDueDate(self, date):
130        self._defaultEditorDueDate = date
131
132    def getDefaultEditorDueDate(self):
133        return self._defaultEditorDueDate
134
135    def getAdjustedDefaultEditorDueDate(self):
136        if self.getDefaultEditorDueDate() is None:
137            return None
138        else:
139            return getAdjustedDate(self._defaultEditorDueDate, self.getConference())
140
141    def setDefaultReviewerDueDate(self, date):
142        self._defaultReviwerDueDate = date
143
144    def getDefaultReviewerDueDate(self):
145        return self._defaultReviwerDueDate
146
147    def getAdjustedDefaultReviewerDueDate(self):
148        if self.getDefaultReviewerDueDate() is None:
149            return None
150        else:
151            return getAdjustedDate(self._defaultReviwerDueDate, self.getConference())
152
153    def setDefaultAbstractReviewerDueDate(self, date):
154        self._defaultAbstractReviwerDueDate = date
155
156    def getDefaultAbstractReviewerDueDate(self):
157        if not hasattr(self, '_defaultAbstractReviwerDueDate'):
158            self._defaultAbstractReviwerDueDate = None
159        return self._defaultAbstractReviwerDueDate
160
161    def getAdjustedDefaultAbstractReviewerDueDate(self):
162        if self.getDefaultAbstractReviewerDueDate() is None:
163            return None
164        else:
165            return getAdjustedDate(self._defaultAbstractReviwerDueDate, self.getConference())
166
167    #Reviewing mode methods
168    def setChoice(self, choice):
169        """ Sets the reviewing mode for the conference, as a number and as a string
170            1: "No reviewing"
171            2: "Content reviewing"
172            3: "Layout reviewing"
173            4: "Content and layout reviewing"
174        """
175        self._choice = choice
176        self._reviewing = ConferenceReview.reviewingModes[choice]
177
178    def getChoice(self):
179        """ Returns the reviewing mode for the conference, as a number
180            1: "No reviewing"
181            2: "Content reviewing"
182            3: "Layout reviewing"
183            4: "Content and layout reviewing"
184        """
185        return self._choice
186
187    def getReviewingMode(self):
188        """ Returns the reviewing mode for the conference, as a string
189        """
190        return self._reviewing
191
192    def setReviewingMode(self, mode):
193        """ Sets the reviewing mode of the conference, as a string.
194            The string has to be one of those in ConferenceReview.reviewingModes
195        """
196        if mode in ConferenceReview.reviewingModes:
197            self._reviewing = mode
198            self._choice = ConferenceReview.reviewingModes.index(mode)
199
200    def hasReviewing(self):
201        """ Convenience method that returns if the Conference has a reviewing mode active or not.
202        """
203        return self._choice > 1
204
205    def hasPaperReviewing(self):
206        """ Convenience method that returns if the Conference has a reviewing mode that allows paper reviewing.
207            (modes 2 and 4)
208        """
209        return self._choice == 2 or self._choice == 4
210
211    def hasPaperEditing(self):
212        """ Convenience method that returns if the Conference has a reviewing mode that allows paper editing.
213            (modes 3 and 4)
214        """
215        return self._choice == 3 or self._choice == 4
216
217    #Choice of reviewable materials methods
218    def getReviewableMaterials(self):
219        """ Returns the list of reviewable materials.
220            Reviewable materials are the only ones subject to versioning and they will be "blocked"
221            when the author submits them, so that they cannot be changed during the reviewing process.
222        """
223        return self._reviewableMaterials
224
225    def getNonReviewableMaterials(self):
226        """ Returns the list of non-reviewable materials
227        """
228        return self._nonReviewableMaterials
229
230    def addReviewableMaterials(self, materials):
231        """ Adds a list of materials to the list of reviewable materials
232        """
233        self._reviewableMaterials.extend(materials)
234        for m in materials:
235            self._nonReviewableMaterials.remove(m)
236        self.notifyModification()
237
238    def removeReviewableMaterials(self, materials):
239        """ Removes a list of materials from the list of reviewable materials
240        """
241        self._nonReviewableMaterials.extend(materials)
242        for m in materials:
243            self._reviewableMaterials.remove(m)
244        self.notifyModification()
245
246    #Content reviewing and final judgement new states methods
247    def addState(self, state):
248        """ Adds a state to the list of non-default states
249        """
250        self._states.append(state)
251        self.notifyModification()
252
253    def getStates(self):
254        """ Returns list of non-default states
255        """
256        return self._states
257
258    def setStates(self, states):
259        self._states = states
260
261    def getAllStates(self):
262        """ Returns list of default and non-default states
263        """
264        return ConferenceReview.predefinedStates + self._states
265
266    def removeState(self, state):
267        """ Remoevs a state
268        """
269        if state in self._states:
270            self._states.remove(state)
271            self.notifyModification()
272        else:
273            raise MaKaCError("Cannot remove a state which doesn't exist")
274
275
276
277    def inModificationPeriod(self):
278        date = nowutc()
279        if date <= self._endCorrectionDate:
280            return True
281        else:
282            return False
283
284    def inSubmissionPeriod(self):
285        date = nowutc()
286        if date <= self._endSubmissionDate & date >= self._startSubmissionDate:
287            return True
288        else:
289            return False
290
291    def inEditingPeriod(self):
292        date = nowutc()
293        if date <= self._endEditingDate:
294            return True
295        else:
296            return False
297
298    def inReviewingPeriod(self):
299        date = nowutc()
300        if date <= self._endReviewingDate & date >= self._endCorrectionDate:
301            return True
302        else:
303            return False
304
305    # content reviewing and final judgement questions methods
306    def addReviewingQuestion(self, reviewingQuestion):
307        """ Adds this question at the end of the list of questions
308        """
309        self._reviewingQuestions.append(reviewingQuestion)
310        self.notifyModification()
311
312    def getReviewingQuestions(self):
313        """ Returns the list of questions
314        """
315        return self._reviewingQuestions
316
317    def setReviewingQuestions(self, questions):
318        """ Set the whole list of questions
319        """
320        self._reviewingQuestions = questions
321
322    def removeReviewingQuestion(self, reviewingQuestion):
323        """ Removes a question from the list
324        """
325        if reviewingQuestion in self._reviewingQuestions:
326            self._reviewingQuestions.remove(reviewingQuestion)
327            self.notifyModification()
328        else:
329            raise MaKaCError("Cannot remove a question which doesn't exist")
330
331    # layout editing criteria methods
332    def addLayoutCriteria(self, formCriteria):
333        """ Add a new layout editing criterion
334        """
335        self._formCriteriaList.append(formCriteria)
336        self.notifyModification()
337
338    def getLayoutCriteria(self):
339        """ Get the list of all the layout criteria
340        """
341        return self._formCriteriaList
342
343    def setLayoutCriteria(self, criteria):
344        """ Set the whole list of all the layout criteria
345        """
346        self._formCriteriaList = criteria
347
348    def removeLayoutCriteria(self, criteria):
349        """ Remove one the layout criteria
350        """
351        if criteria in self._formCriteriaList:
352            self._formCriteriaList.remove(criteria)
353            self.notifyModification()
354        else:
355            raise MaKaCError("Cannot remove a criteria which doesn't exist")
356
357
358    #referee methods
359    def addReferee(self, newReferee):
360        """ Adds a new referee to the conference.
361            newReferee has to be an Avatar object.
362            The referee is sent a mail notification.
363        """
364        if newReferee in self.getRefereesList():
365            raise MaKaCError("This referee has already been added to this conference")
366        else:
367            self._refereesList.append(newReferee)
368            newReferee.linkTo(self._conference, "referee")
369            if not self._userCompetences.has_key(newReferee):
370                self._userCompetences[newReferee] = []
371            self.notifyModification()
372            notification = ConferenceReviewingNotification(newReferee, 'Referee', self._conference)
373            GenericMailer.sendAndLog(notification, self._conference, "MaKaC/reviewing.py", newReferee)
374
375
376    def removeReferee(self, referee):
377        """ Remove a referee from the conference.
378            referee has to be an Avatar object.
379            The referee is sent a mail notification.
380        """
381        if referee in self._refereesList:
382            if self._userCompetences.has_key(referee):
383                if not ((referee in self._editorsList) or \
384                        (referee in self._paperReviewManagersList) or \
385                        (referee in self._reviewersList) or \
386                        (referee in self._abstractManagerList) or \
387                        (referee in self._abstractReviewersList)):
388                    self.clearUserCompetences(referee)
389                    del(self._userCompetences[referee])
390            self._refereesList.remove(referee)
391            referee.unlinkTo(self._conference, "referee")
392            self.notifyModification()
393            notification = ConferenceReviewingRemoveNotification(referee, 'Referee', self._conference)
394            GenericMailer.sendAndLog(notification, self._conference, "MaKaC/reviewing.py", referee)
395        else:
396            raise MaKaCError("Cannot remove a referee who is not yet referee")
397
398    def isReferee(self, user):
399        """ Returns if a given user is referee of the conference.
400            user has to be an Avatar object.
401        """
402        return user in self._refereesList
403
404    def getRefereesList(self):
405        """ Returns the list of referees as a list of Avatar objects.
406        """
407        return self._refereesList
408
409    def addRefereeContribution(self, referee, contribution):
410        """ Adds the contribution to the list of contributions for a given referee.
411            referee has to be an Avatar object.
412        """
413        if self._refereeContribution.has_key(referee):
414            self._refereeContribution[referee].append(contribution)
415            self._refereeContribution[referee].sort(key= lambda c: int(c.getId()))
416        else:
417            self._refereeContribution[referee] = [contribution]
418        self.notifyModification()
419
420    def removeRefereeContribution(self, referee, contribution):
421        """ Removes the contribution from the list of contributions of this referee
422            referee has to be an Avatar object.
423        """
424        if self._refereeContribution.has_key(referee):
425            self._refereeContribution[referee].remove(contribution)
426        self.notifyModification()
427
428    def isRefereeContribution(self, referee, contribution):
429        """ Returns if a user is referee for a given contribution
430            referee has to be an Avatar object.
431        """
432        return self._refereeContribution.has_key(referee) and contribution in self._refereeContribution[referee]
433
434    def getJudgedContributions(self, referee):
435        """ Returns the list of contributions for a given referee
436            referee has to be an Avatar object.
437        """
438        if self._refereeContribution.has_key(referee):
439            return self._refereeContribution[referee]
440        else:
441            return []
442
443    #editor methods
444    def addEditor(self, newEditor):
445        """ Adds a new editor to the conference.
446            editor has to be an Avatar object.
447            The editor is sent a mail notification.
448        """
449        if newEditor in self.getEditorsList():
450            raise MaKaCError("This editor has already been added to this conference")
451        else:
452            self._editorsList.append(newEditor)
453            newEditor.linkTo(self._conference, "editor")
454            if not self._userCompetences.has_key(newEditor):
455                self._userCompetences[newEditor] = []
456            self.notifyModification()
457            notification = ConferenceReviewingNotification(newEditor, 'Editor', self._conference)
458            GenericMailer.sendAndLog(notification, self._conference, "MaKaC/reviewing.py", newEditor)
459
460    def removeEditor(self, editor):
461        """ Remove a editor from the conference.
462            editor has to be an Avatar object.
463            The editor is sent a mail notification.
464        """
465        if editor in self._editorsList:
466            if self._userCompetences.has_key(editor):
467                if not ((editor in self._paperReviewManagersList) or \
468                        (editor in self._reviewersList) or \
469                        (editor in self._refereesList) or \
470                        (editor in self._abstractManagerList) or \
471                        (editor in self._abstractReviewersList)):
472                    self.clearUserCompetences(editor)
473                    del(self._userCompetences[editor])
474            self._editorsList.remove(editor)
475            editor.unlinkTo(self._conference, "editor")
476            self.notifyModification()
477            notification = ConferenceReviewingRemoveNotification(editor, 'Editor', self._conference)
478            GenericMailer.sendAndLog(notification, self._conference, "MaKaC/reviewing.py", editor)
479        else:
480            raise MaKaCError("Cannot remove an editor who is not yet editor")
481
482    def isEditor(self, user):
483        """ Returns if a given user is editor of the conference.
484            user has to be an Avatar object.
485        """
486        return user in self._editorsList
487
488    def getEditorsList(self):
489        """ Returns the list of editors as a list of users.
490        """
491        return self._editorsList
492
493    def addEditorContribution(self, editor, contribution):
494        """ Adds the contribution to the list of contributions for a given editor.
495            editor has to be an Avatar object.
496        """
497        if self._editorContribution.has_key(editor):
498            self._editorContribution[editor].append(contribution)
499            self._editorContribution[editor].sort(key= lambda c: int(c.getId()))
500        else:
501            self._editorContribution[editor] = [contribution]
502        self.notifyModification()
503
504    def removeEditorContribution(self, editor, contribution):
505        """ Removes the contribution from the list of contributions of this editor
506            editor has to be an Avatar object.
507        """
508        if self._editorContribution.has_key(editor):
509            self._editorContribution[editor].remove(contribution)
510        self.notifyModification()
511
512    def isEditorContribution(self, editor, contribution):
513        """ Returns if a user is editor for a given contribution
514            editor has to be an Avatar object.
515        """
516        return self._editorContribution.has_key(editor) and contribution in self._editorContribution[editor]
517
518    def getEditedContributions(self, editor):
519        """ Returns the list of contributions for a given editor
520            editor has to be an Avatar object.
521        """
522        if self._editorContribution.has_key(editor):
523            return self._editorContribution[editor]
524        else:
525            return []
526
527    #reviewer methods
528    def addReviewer(self, newReviewer):
529        """ Adds a new reviewer to the conference.
530            newreviewer has to be an Avatar object.
531            The reviewer is sent a mail notification.
532        """
533        if newReviewer in self.getReviewersList():
534            raise MaKaCError("This reviewer has already been added to this conference")
535        else:
536            self._reviewersList.append(newReviewer)
537            newReviewer.linkTo(self._conference, "reviewer")
538            if not self._userCompetences.has_key(newReviewer):
539                self._userCompetences[newReviewer] = []
540            self.notifyModification()
541            notification = ConferenceReviewingNotification(newReviewer, 'Reviewer', self._conference)
542            GenericMailer.sendAndLog(notification, self._conference, "MaKaC/reviewing.py", newReviewer)
543
544    def removeReviewer(self, reviewer):
545        """ Remove a reviewer from the conference.
546            reviewer has to be an Avatar object.
547            The reviewer is sent a mail notification.
548        """
549        if reviewer in self._reviewersList:
550            if self._userCompetences.has_key(reviewer):
551                if not ((reviewer in self._editorsList) or \
552                        (reviewer in self._paperReviewManagersList) or \
553                        (reviewer in self._refereesList) or \
554                        (reviewer in self._abstractManagerList) or \
555                        (reviewer in self._abstractReviewersList)):
556                    self.clearUserCompetences(reviewer)
557                    del(self._userCompetences[reviewer])
558            self._reviewersList.remove(reviewer)
559            reviewer.unlinkTo(self._conference, "reviewer")
560            self.notifyModification()
561            notification = ConferenceReviewingRemoveNotification(reviewer, 'Reviewer', self._conference)
562            GenericMailer.sendAndLog(notification, self._conference, "MaKaC/reviewing.py", reviewer)
563        else:
564            raise MaKaCError("Cannot remove a reviewer who is not yet reviewer")
565
566    def isReviewer(self, user):
567        """ Returns if a given user is reviewer of the conference.
568            user has to be an Avatar object.
569        """
570        return user in self._reviewersList
571
572    def getReviewersList(self):
573        """ Returns the list of reviewers as a list of users.
574        """
575        return self._reviewersList
576
577    def addReviewerContribution(self, reviewer, contribution):
578        """ Adds the contribution to the list of contributions for a given reviewer.
579            reviewer has to be an Avatar object.
580        """
581        if self._reviewerContribution.has_key(reviewer):
582            self._reviewerContribution[reviewer].append(contribution)
583            self._reviewerContribution[reviewer].sort(key= lambda c: int(c.getId()))
584        else:
585            self._reviewerContribution[reviewer] = [contribution]
586        self.notifyModification()
587
588    def removeReviewerContribution(self, reviewer, contribution):
589        """ Removes the contribution from the list of contributions of this reviewer
590            reviewer has to be an Avatar object.
591        """
592        if self._reviewerContribution.has_key(reviewer):
593            self._reviewerContribution[reviewer].remove(contribution)
594        self.notifyModification()
595
596    def isReviewerContribution(self, reviewer, contribution):
597        """ Returns if a user is reviewer for a given contribution
598            reviewer has to be an Avatar object.
599        """
600        return self._reviewerContribution.has_key(reviewer) and contribution in self._reviewerContribution[reviewer]
601
602    def getReviewedContributions(self, reviewer):
603        """ Returns the list of contributions for a given reviewer
604            reviewer has to be an Avatar object.
605        """
606        if self._reviewerContribution.has_key(reviewer):
607            return self._reviewerContribution[reviewer]
608        else:
609            return []
610
611    #paper review manager methods
612    def addPaperReviewManager(self, newPaperReviewManager):
613        """ Adds a new paper review manager to the conference.
614            newPaperReviewManager has to be an Avatar object.
615            The paper review manager is sent a mail notification.
616        """
617        if newPaperReviewManager in self.getPaperReviewManagersList():
618            raise MaKaCError("This paper review manager has already been added to this conference")
619        else:
620            self._paperReviewManagersList.append(newPaperReviewManager)
621            newPaperReviewManager.linkTo(self._conference, "paperReviewManager")
622            if not self._userCompetences.has_key(newPaperReviewManager):
623                self._userCompetences[newPaperReviewManager] = []
624            self.notifyModification()
625            notification = ConferenceReviewingNotification(newPaperReviewManager, 'Paper Review Manager', self._conference)
626            GenericMailer.sendAndLog(notification, self._conference, "MaKaC/reviewing.py", newPaperReviewManager)
627
628    def removePaperReviewManager(self, paperReviewManager):
629        """ Remove a paper review manager from the conference.
630            paperReviewManager has to be an Avatar object.
631            The paper review manager is sent a mail notification.
632        """
633        if paperReviewManager in self._paperReviewManagersList:
634            if self._userCompetences.has_key(paperReviewManager):
635                if not ((paperReviewManager in self._editorsList) or \
636                        (paperReviewManager in self._reviewersList) or \
637                        (paperReviewManager in self._refereesList) or \
638                        (paperReviewManager in self._abstractManagerList) or \
639                        (paperReviewManager in self._abstractReviewersList)):
640                    self.clearUserCompetences(paperReviewManager)
641                    del(self._userCompetences[paperReviewManager])
642            self._paperReviewManagersList.remove(paperReviewManager)
643            paperReviewManager.unlinkTo(self._conference, "paperReviewManager")
644            self.notifyModification()
645            notification = ConferenceReviewingRemoveNotification(paperReviewManager, 'Paper Review Manager', self._conference)
646            GenericMailer.sendAndLog(notification, self._conference, "MaKaC/reviewing.py", paperReviewManager)
647        else:
648            raise MaKaCError("Cannot remove a paper review manager who is not yet paper review manager")
649
650    def isPaperReviewManager(self, user):
651        """ Returns if a given user is paper review manager of the conference.
652            user has to be an Avatar object.
653        """
654        return user in self._paperReviewManagersList
655
656    def getPaperReviewManagersList(self):
657        """ Returns the list of paper review managers as a list of users.
658        """
659        return self._paperReviewManagersList
660
661    #abstract manager methods
662    def addAbstractManager(self, newAbstractManager):
663        """ Adds a new abstract manager to the conference.
664            newAbstractManager has to be an Avatar object.
665            The abstract manager is sent a mail notification.
666        """
667        if newAbstractManager in self.getAbstractManagersList():
668            raise MaKaCError("This abstract manager has already been added to this conference")
669        else:
670            self._abstractManagerList.append(newAbstractManager)
671            newAbstractManager.linkTo(self._conference, "abstractManager")
672            if not self._userCompetences.has_key(newAbstractManager):
673                self._userCompetences[newAbstractManager] = []
674            self.notifyModification()
675            notification = ConferenceReviewingNotification(newAbstractManager, 'Abstract Manager', self._conference)
676            GenericMailer.sendAndLog(notification, self._conference, "MaKaC/reviewing.py", newAbstractManager)
677
678    def removeAbstractManager(self, abstractManager):
679        """ Remove a abstract manager from the conference.
680            abstractManager has to be an Avatar object.
681            The abstract manager is sent a mail notification.
682        """
683        if abstractManager in self._abstractManagerList:
684            if self._userCompetences.has_key(abstractManager):
685                if not ((abstractManager in self._editorsList) or \
686                        (abstractManager in self._paperReviewManagersList) or \
687                        (abstractManager in self._refereesList) or \
688                        (abstractManager in self._reviewersList) or \
689                        (abstractManager in self._abstractReviewersList)):
690                    self.clearUserCompetences(abstractManager)
691                    del(self._userCompetences[abstractManager])
692            self._abstractManagerList.remove(abstractManager)
693            abstractManager.unlinkTo(self._conference, "abstractManager")
694            self.notifyModification()
695            notification = ConferenceReviewingRemoveNotification(abstractManager, 'Abstract Manager', self._conference)
696            GenericMailer.sendAndLog(notification, self._conference, "MaKaC/reviewing.py", abstractManager)
697        else:
698            raise MaKaCError("Cannot remove a abstract manager who is not yet abstract manager")
699
700    def isAbstractManager(self, user):
701        """ Returns if a given user is abstract manager of the conference.
702            user has to be an Avatar object.
703        """
704        return user in self._abstractManagerList
705
706    def getAbstractManagersList(self):
707        """ Returns the list of abstract managers as a list of users.
708        """
709        return self._abstractManagerList
710
711    #abstract reviewer methods
712    def addAbstractReviewer(self, newAbstractReviewer):
713        """ Adds a new abstract reviewer to the conference.
714            newAbstractReviewer has to be an Avatar object.
715            The abstract reviewer is sent a mail notification.
716        """
717        if newAbstractReviewer in self.getAbstractReviewersList():
718            raise MaKaCError("This abstract reviewer has already been added to this conference")
719        else:
720            self._abstractReviewersList.append(newAbstractReviewer)
721            newAbstractReviewer.linkTo(self._conference, "abstractReviewer")
722            if not self._userCompetences.has_key(newAbstractReviewer):
723                self._userCompetences[newAbstractReviewer] = []
724            self.notifyModification()
725            notification = ConferenceReviewingNotification(newAbstractReviewer, 'Abstract Reviewer', self._conference)
726            GenericMailer.sendAndLog(notification, self._conference, "MaKaC/reviewing.py", newAbstractReviewer)
727
728    def removeAbstractReviewer(self, abstractReviewer):
729        """ Remove a abstract reviewer from the conference.
730            abstractReviwer has to be an Avatar object.
731            The abstract reviewer is sent a mail notification.
732        """
733        if abstractReviewer in self._abstractReviewersList:
734            if self._userCompetences.has_key(abstractReviewer):
735                if not ((abstractReviewer in self._editorsList) or \
736                        (abstractReviewer in self._paperReviewManagersList) or \
737                        (abstractReviewer in self._refereesList) or \
738                        (abstractReviewer in self._reviewersList) or \
739                        (abstractReviewer in self._abstractManagerList)):
740                    self.clearUserCompetences(abstractReviewer)
741                    del(self._userCompetences[abstractReviewer])
742            self._abstractReviewersList.remove(abstractReviewer)
743            abstractReviewer.unlinkTo(self._conference, "abstractReviewer")
744            self.notifyModification()
745            notification = ConferenceReviewingRemoveNotification(abstractReviewer, 'Abstract Reviewer', self._conference)
746            GenericMailer.sendAndLog(notification, self._conference, "MaKaC/reviewing.py", abstractReviewer)
747        else:
748            raise MaKaCError("Cannot remove an abstract reviewer who is not yet abstract reviewer")
749
750    def isAbstractReviewer(self, user):
751        """ Returns if a given user is abstract reviewer of the conference.
752            user has to be an Avatar object.
753        """
754        return user in self._abstractReviewersList
755
756    def getAbstractReviewersList(self):
757        """ Returns the list of abstract reviewers as a list of users.
758        """
759        return self._abstractReviewersList
760
761    def addReviewerAbstract(self, abstractReviewer, abstract):
762        """ Adds the abstract to the list of abstracts for a given abstract reviewer.
763            abstractReviewer has to be an Avatar object.
764        """
765        if self._reviewerAbstract.has_key(abstractReviewer):
766            self._reviewerAbstract[abstractReviewer].append(abstract)
767            self._reviewerAbstract[abstractReviewer].sort(key= lambda c: int(c.getId()))
768        else:
769            self._reviewerAbstract[abstractReviewer] = [abstract]
770        self.notifyModification()
771
772    def removeReviewerAbstract(self, abstractReviewer, abstract):
773        """ Removes the contribution from the list of contributions of this reviewer
774            reviewer has to be an Avatar object.
775        """
776        if self._reviewerAbstract.has_key(abstractReviewer):
777            self._reviewerAbstract[abstractReviewer].remove(abstract)
778        self.notifyModification()
779
780    def isReviewerAbstract(self, abstractReviewer, abstract):
781        """ Returns if a user is reviewer for a given contribution
782            reviewer has to be an Avatar object.
783        """
784        return self._reviewerAbstract.has_key(abstractReviewer) and abstract in self._reviewerAbstract[abstractReviewer]
785
786    def getReviewedAbstracts(self, abstractReviewer):
787        """ Returns the list of abstracts for a given reviewer
788            reviewer has to be an Avatar object.
789        """
790        if self._reviewerAbstract.has_key(abstractReviewer):
791            return self._reviewerAbstract[abstractReviewer]
792        else:
793            return []
794
795    # templates methods
796    def setTemplate(self, name, description, format, fd):
797        """ Stores a template.
798            There can be 1 template for any format.
799        """
800
801        cfg = Config.getInstance()
802        tempPath = cfg.getUploadedFilesTempDir()
803        tempFileName = tempfile.mkstemp( suffix="MaKaC.tmp", dir = tempPath )[1]
804        f = open( tempFileName, "wb" )
805        f.write( fd.read() )
806        f.close()
807
808        if format in Template.formats:
809            extension = Template.formats[format]
810        else:
811            extension = ".template"
812
813        id = self.getNewTemplateId()
814
815        fileName = "Contribution_template_" + id + "_c" + self._conference.id + extension
816
817        file = conference.LocalFile()
818        file.setName( fileName )
819        file.setDescription( "Paper reviewing template with id " + id + "with format: " + format + " of the conference " + self._conference.id )
820        file.setFileName( fileName )
821
822        file.setFilePath( tempFileName )
823        file.setOwner( self._conference )
824        file.setId( fileName )
825        file.archive( self._conference._getRepository() )
826
827        self._templates[id] = Template(id, self._conference, name, description, format, file)
828        self.notifyModification()
829
830    def hasTemplates(self):
831        """ Returns if the conference has any reviewing templates
832        """
833        return len(self._templates) > 0
834
835    def getTemplates(self):
836        """ Returns a dictionary of templates. key: id, value: Template object
837        """
838        return self._templates
839
840    def getNewTemplateId(self):
841        """ Returns a new an unused templateId
842            Increments the templateId counter
843        """
844        return self._templateCounter.newCount()
845
846    def deleteTemplate(self, id):
847        """ Removes a reviewing template from the conference, given the id.
848        """
849        del self._templates[id]
850        self.notifyModification()
851
852    #competences methods
853    def isInReviewingTeam(self, user):
854        return user in self._paperReviewManagersList or \
855               user in self._refereesList or \
856               user in self._editorsList or \
857               user in self._reviewersList or \
858               user in self._reviewersList or \
859               user in self._abstractManagerList or \
860               user in self._abstractReviewersList
861
862    def setUserCompetences(self, user, competences):
863        """ Sets a list of competences (a list of strings) to a given user
864        """
865        if self.isInReviewingTeam(user):
866            self.clearUserCompetences(user)
867            self.addUserCompetences(user, competences)
868        else:
869            raise MaKaCError("""User %s is not in the reviewing team for this conference, so you cannot set competences for him/her"""%user.getFullName())
870
871    def clearUserCompetences(self, user):
872        if self.isInReviewingTeam(user):
873            for c in self.getCompetencesByUser(user):
874                self._userCompetencesByTag[c].remove(user)
875                if len(self._userCompetencesByTag[c]) == 0:
876                        del self._userCompetencesByTag[c]
877            self._userCompetences[user] = []
878        else:
879            raise MaKaCError("""User %s is not in the reviewing team for this conference, so you cannot clear competences for him/her"""%user.getFullName())
880
881    def addUserCompetences(self, user, competences):
882        """ Adds a list of competences (a list of strings) to a given user
883        """
884        if self.isInReviewingTeam(user):
885            self._userCompetences[user].extend(competences)
886            for c in competences:
887                if self._userCompetencesByTag.has_key(c):
888                    self._userCompetencesByTag[c].append(user)
889                else:
890                    self._userCompetencesByTag[c] = [user]
891            self.notifyModification()
892        else:
893            raise MaKaCError("""User %s is not in the reviewing team for this conference, so you cannot add competences for him/her"""%user.getFullName())
894
895    def removeUserCompetences(self, user, competences):
896        """ Removes a list of competences (a list of strings) from a given user
897        """
898        if self.isInReviewingTeam(user):
899            if self._userCompetences.has_key(user):
900                for c in competences:
901                    self._userCompetences[user].remove(c)
902                    self._userCompetencesByTag[c].remove(user)
903                    if len(self._userCompetencesByTag[c]) == 0:
904                        del self._userCompetencesByTag[c]
905                self.notifyModification()
906            else:
907                raise MaKaCError("""User %s does not have competences"""%(user.getFullName))
908        else:
909            raise MaKaCError("""User %s is not in the reviewing team for this conference, so you cannot remove competences from him/her"""%user.getFullName())
910
911    def getAllUserCompetences(self, onlyActive = False, role = 'all'):
912        """ Returns a list with the users and their competences.
913            role can be 'referee', 'editor', 'reviewer' or 'all' (a different value or none specified defaults to 'all')
914            The list is composed of tuples of 2 elements: (user, competenceList) where competenceList is a list of strings.
915            The list is ordered by the full name of the users.
916        """
917
918        if onlyActive:
919            if role == 'referee':
920                users = self._refereesList
921            elif role == 'editor':
922                users = self._editorsList
923            elif role == 'reviewer':
924                users = self._reviewersList
925            else:
926                users = self._paperReviewManagersList + self._refereesList + self._editorsList + self._reviewersList
927        else:
928            if role == 'referee':
929                users = [u for u in self._userCompetences.keys() if u in self._refereesList]
930            elif role == 'editor':
931                users = [u for u in self._userCompetences.keys() if u in self._editorsList]
932            elif role == 'reviewer':
933                users = [u for u in self._userCompetences.keys() if u in self._reviewersList]
934            else:
935                users = self._userCompetences.keys()
936
937        users.sort(key = lambda u: u.getFullName())
938        return zip(users, [self._userCompetences[user] for user in users])
939
940    def getCompetencesByUser(self, user):
941        """ Returns the list of competences (a list of strings) for a given user.
942        """
943        return self._userCompetences[user]
944
945    def getUserReviewingRoles(self, user):
946        """ Returns the list of roles (PRM, referee, editor, reviewer) that a given user has
947            in this conference, as a list of strings.
948        """
949        roles=[]
950        if self.isPaperReviewManager(user):
951            roles.append('Manager of Paper Review Module')
952        if self.isReferee(user):
953            roles.append('Referee')
954        if self.isEditor(user):
955            roles.append('Layout Reviewer')
956        if self.isReviewer(user):
957            roles.append('Content Reviewer')
958        if self.isAbstractManager(user):
959            roles.append('Abstracts Manager')
960        if self.isAbstractReviewer(user):
961            roles.append('Abstract Reviewer')
962        return roles
963
964    def notifyModification(self):
965        """ Notifies the DB that a list or dictionary attribute of this object has changed
966        """
967        self._p_changed = 1
968
969
970
971class Template(Persistent):
972    """ This class represents a template for contribution reviewing.
973        A template is a file uploaded by a Conference Manager or a Paper Review Manager.
974        Normal users can download it to have an idea of the format their contribution should have.
975        A conference can have many of these templates.
976    """
977
978    formats = {"Word":".doc",
979               "OpenOffice Writer":".odt",
980               "PowerPoint":".ppt",
981               "OpenOffice Impress":".odp",
982               "LaTeX":".tex"}
983    """ This dictionary contains the different formats proposed to the uploaders.
984        Also, if a template is uploaded with one of these formats, the file will have
985        an appropriate extension.
986    """
987
988    def __init__(self, id, conference, name, description, format, file):
989        self.__id = id
990        self.__conf = conference
991        self.__name = name
992        self.__description = description
993        self.__format = format
994        self.__file = file
995
996    def getId(self):
997        return self.__id
998
999    def getName(self):
1000        return self.__name
1001
1002    def getDescription(self):
1003        return self.__description
1004
1005    def getFormat(self):
1006        return self.__format
1007
1008    def getFile(self):
1009        return self.__file
1010
1011    def getLocator( self ):
1012        """Gives back (Locator) a globaly unique identification encapsulated
1013                in a Locator object for the category instance
1014        """
1015        loc = self.__conf.getLocator()
1016        loc["reviewingTemplateId"] = self.getId()
1017        return loc
1018
1019class ConferenceReviewingNotification(GenericNotification):
1020    """ Template to build an email notification to a newly appointed PRM / Referee / Editor / Reviewer / Abstract Manager / Abstract Reviewer
1021    """
1022
1023    def __init__(self, user, role, conference):
1024        GenericNotification.__init__(self)
1025        self.setFromAddr("Indico Mailer<%s>"%HelperMaKaCInfo.getMaKaCInfoInstance().getSupportEmail())
1026        self.setToList([user.getEmail()])
1027        self.setSubject("""[Indico] You have been chosen as %s for the conference "%s" (id: %s)"""
1028                        % (role, conference.getTitle(), str(conference.getId())))
1029        self.setBody("""Dear Indico user,
1030
1031        You have been chosen as %s of the conference "%s" (id: %s), in order to help with the abstract / contribution reviewing process.
1032        You can go to the conference main page:
1033        %s
1034        After loggin in, you will find a link under 'My Conference' where you can click to perform your new functions.
1035
1036        Thank you for using our system.
1037        """ % ( role, conference.getTitle(), str(conference.getId()), urlHandlers.UHConferenceDisplay.getURL(conference)
1038        ))
1039
1040class ConferenceReviewingRemoveNotification(GenericNotification):
1041    """ Template to build an email notification to a removed PRM / Referee / Editor / Reviewer / Abstract Manager / Abstract Reviewer
1042    """
1043
1044    def __init__(self, user, role, conference):
1045        GenericNotification.__init__(self)
1046        self.setFromAddr("Indico Mailer<%s>"%HelperMaKaCInfo.getMaKaCInfoInstance().getSupportEmail())
1047        self.setToList([user.getEmail()])
1048        self.setSubject("""[Indico] You have been removed as %s of the conference "%s" (id: %s)"""
1049                        % (role, conference.getTitle(), str(conference.getId())))
1050        self.setBody("""Dear Indico user,
1051
1052        We are sorry to inform you that you have been removed as %s of the conference "%s" (id: %s).
1053
1054        Thank you for using our system.
1055        """ % ( role, conference.getTitle(), str(conference.getId())
1056        ))
Note: See TracBrowser for help on using the repository browser.