1 # This file is part of Indico.
2 # Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
4 # Indico is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3 of the
7 # License, or (at your option) any later version.
9 # Indico is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with Indico; if not, see <http://www.gnu.org/licenses/>.
16 from cStringIO
import StringIO
17 from flask
import session
19 from MaKaC
.webinterface
import urlHandlers
20 from MaKaC
.webinterface
.rh
import conferenceModif
21 from MaKaC
.webinterface
.pages
import evaluations
22 from datetime
import datetime
23 from MaKaC
.evaluation
import Box
,Choice
,Textbox
,Textarea
,Password
,Select
,Radio
,Checkbox
24 from MaKaC
.evaluation
import Evaluation
,Question
,MultipleChoicesAnswer
,TextAnswer
25 from MaKaC
.common
import utils
26 from MaKaC
.errors
import FormValuesError
, MaKaCError
27 from MaKaC
.export
.excel
import ExcelGenerator
28 from xml
.dom
.minidom
import parseString
,Element
29 from indico
.core
.config
import Config
30 from MaKaC
.i18n
import _
31 from indico
.web
.flask
.util
import send_file
34 class RHEvaluationBase(conferenceModif
.RHConferenceModifBase
):
35 """Basis of an Evaluation"""
37 def _checkProtection(self
):
38 conferenceModif
.RHConferenceModifBase
._checkProtection
(self
)
39 if not self
._conf
.hasEnabledSection("evaluation"):
40 raise MaKaCError( _("The Evaluation form was disabled by the conference managers."), _("evaluation"))
42 class RHEvaluationSetup(RHEvaluationBase
):
43 """Modification of an Evaluation."""
46 return evaluations
.WPConfModifEvaluationSetup(self
, self
._conf
).display()
48 class RHEvaluationSetupChangeStatus( RHEvaluationBase
):
49 """changes status of an Evaluation."""
51 def _checkParams( self
, params
):
52 RHEvaluationBase
._checkParams
( self
, params
)
53 self
._newStatus
= params
.get("changeTo", False)
56 evaluation
= self
._conf
.getEvaluation()
57 if self
._newStatus
== "True" :
58 evaluation
.setVisible(True)
60 evaluation
.setVisible(False)
61 evaluation
.notifyModification()
62 self
._redirect
(urlHandlers
.UHConfModifEvaluationSetup
.getURL(self
._conf
))
64 class RHEvaluationSetupSpecialAction( RHEvaluationBase
):
65 """processes a special action."""
68 params
= self
.getRequestParams()
69 evaluation
= self
._conf
.getEvaluation()
70 sessionVarName
= "selectedSubmissions_%s_%s"%(self
._conf
.getId(), evaluation
.getId())
71 if 'exportXML' in params
:
72 return self
._exportXml
(evaluation
, params
)
73 elif 'importXML' in params
:
74 return evaluations
.WPConfModifEvaluationSetupImportXml( self
, self
._conf
).display()
75 elif 'importedXML' in params
:
76 self
._importedXml
(evaluation
, params
)
77 elif 'removeSubmissions' in params
:
78 session
.pop(sessionVarName
, None)
79 evaluation
.removeAllSubmissions()
80 elif 'removeQuestions' in params
:
81 session
.pop(sessionVarName
, None)
82 evaluation
.removeAllQuestions()
83 elif 'reinit' in params
:
84 session
.pop(sessionVarName
, None)
86 self
._redirect
(urlHandlers
.UHConfModifEvaluationSetup
.getURL(self
._conf
))
88 def _exportXml(self
, evaluation
, params
):
89 """exportation of an evaluation."""
90 from MaKaC
.common
.xmlGen
import XMLGen
92 evaluation
.exportXml(xmlGen
)
93 return send_file(Evaluation
._XML
_FILENAME
, StringIO(xmlGen
.getXml()), 'XML', inline
=False)
95 def _importedXml(self
, evaluation
, params
):
96 """ Importation of an evaluation.
97 Note: original 'isVisible' and the dates are kept.
100 xmlString
= params
["xmlFile"].file.read()
101 except AttributeError: #no file given
102 self
._redirect
(urlHandlers
.UHConfModifEvaluationSetup
.getURL(self
._conf
))
105 doc
= parseString(xmlString
)
107 raise MaKaCError( _("""System can't import an evaluation from your file.
108 Be sure to import the right XML file."""), _("evaluation"))
110 evalNode
= self
._getElement
(doc
,"evaluation")
111 if params
.get("configOption","")=="imported":
112 #parse node /evaluation/
113 title
= self
._getValue
(evalNode
,"title")
114 announcement
= self
._getValue
(evalNode
,"announcement")
115 submissionsLimit
= self
._getValue
(evalNode
,"submissionsLimit")
116 contactInfo
= self
._getValue
(evalNode
,"contactInfo")
117 mandatoryAccount
= self
._getValue
(evalNode
,"mandatoryAccount")
118 mandatoryParticipant
= self
._getValue
(evalNode
,"mandatoryParticipant")
119 anonymous
= self
._getValue
(evalNode
,"anonymous")
120 evaluation
.setTitle(title
)
121 evaluation
.setAnnouncement(announcement
)
122 evaluation
.setSubmissionsLimit(submissionsLimit
)
123 evaluation
.setContactInfo(contactInfo
)
124 evaluation
.setMandatoryAccount(mandatoryAccount
)
125 evaluation
.setMandatoryParticipant(mandatoryParticipant
)
126 if anonymous
.strip()=="" :
127 evaluation
.setAnonymous(True)
129 evaluation
.setAnonymous(anonymous
)
130 #parse node /evaluation/notifications/
131 notificationsNode
= self
._getElement
(evalNode
, "notifications")
132 evaluation
.removeAllNotifications()
133 for notificationNode
in notificationsNode
.childNodes
:
134 if isinstance(notificationNode
, Element
) :
135 from MaKaC
.registration
import Notification
136 notification
= Notification()
137 toList
= self
._getValue
(notificationNode
,"toList")
138 ccList
= self
._getValue
(notificationNode
,"ccList")
139 notification
.setToList( utils
.getEmailList(toList
) )
140 notification
.setCCList( utils
.getEmailList(ccList
) )
141 evaluation
.setNotification(notificationNode
.tagName
, notification
)
142 if params
.get("questionsOption","")!="current" : # in ["imported", "both"]
143 if params
.get("questionsOption","")=="imported" :
144 evaluation
.removeAllQuestions()
145 #parse node /evaluation/questions/
146 questionsNode
= self
._getElement
(evalNode
,"questions")
147 for questionNode
in questionsNode
.childNodes
:
148 if isinstance(questionNode
, Element
):
149 questionValue
= self
._getValue
(questionNode
,"questionValue")
150 keyword
= self
._getValue
(questionNode
,"keyword")
151 required
= self
._getValue
(questionNode
,"required")
152 description
= self
._getValue
(questionNode
,"description")
153 help = self
._getValue
(questionNode
,"help")
155 question
= eval(questionNode
.tagName
)()
156 if isinstance(question
, Question
):
157 question
.setQuestionValue(questionValue
)
158 question
.setKeyword(keyword
)
159 question
.setRequired(required
)
160 question
.setDescription(description
)
161 question
.setHelp(help)
162 evaluation
.insertQuestion(question
)
164 raise MaKaCError( _("""xml parse error: unknown question type "%s"
165 """)%questionNode
.tagName
, _("evaluation"))
166 if isinstance(question
, Box
):
167 defaultAnswer
= self
._getValue
(questionNode
,"defaultAnswer")
168 question
.setDefaultAnswer(defaultAnswer
)
169 elif isinstance(question
, Choice
):
170 #parse node /evaluation/questions/*/choiceItems/
171 choiceItemsNode
= self
._getElement
(questionNode
,"choiceItems")
172 for choiceItemNode
in choiceItemsNode
.childNodes
:
173 if isinstance(choiceItemNode
, Element
):
174 itemText
= self
._getValue
(choiceItemNode
,"itemText")
175 if itemText
.strip()!="" :
176 isSelected
= self
._getValue
(choiceItemNode
,"isSelected")
177 question
.insertChoiceItem(itemText
, isSelected
)
179 def _getElement(self
, container
, tagname
):
180 """ Find the first element with given tagname contained in given container element.
181 If not found, return an empty Element tagged "_empty" (in order to avoid crashes).
184 container -- an Element containing the wanted element
185 tagname -- tag (str) of the wanted element
187 if not hasattr(container
, "getElementsByTagName"):
188 return Element("_empty")
189 #get a leaf node with given tagname contained within given node
190 leafNodes
= container
.getElementsByTagName(tagname
)
191 if len(leafNodes
)>0 :
194 return Element("_empty")
196 def _getValue(self
, container
, tagname
):
197 """ finds the first leaf element with given tagname contained in given element,
198 and returns its value.
201 container -- a non leaf Element containing the wanted leaf element
202 tagname -- tag (str) of the wanted leaf element
204 if not hasattr(container
, "getElementsByTagName"):
206 leafNode
= self
._getElement
(container
, tagname
)
207 #given a leaf node, returns its value.
208 return utils
.nodeValue(leafNode
)
210 class RHEvaluationSetupDataModif( RHEvaluationBase
):
211 """called when you want to change general parameters of your evaluation."""
213 def _process( self
):
214 return evaluations
.WPConfModifEvaluationSetupDataModif( self
, self
._conf
).display()
216 class RHEvaluationSetupPerformDataModif( RHEvaluationBase
):
217 """performs changes to general parameters of the evaluation."""
219 def _process( self
):
220 params
= self
.getRequestParams()
221 if params
.has_key("modify"):
222 from MaKaC
.registration
import Notification
223 evaluation
= self
._conf
.getEvaluation()
225 sDate
= datetime( int( params
["sYear"] ), \
226 int( params
["sMonth"] ), \
227 int( params
["sDay"] ) )
229 raise FormValuesError("The start date you have entered is not correct: %s"%e, "Evaluation")
231 eDate
= datetime( int( params
["eYear"] ), \
232 int( params
["eMonth"] ), \
233 int( params
["eDay"] ) )
235 raise FormValuesError("The end date you have entered is not correct: %s"%e, "Evaluation")
237 raise FormValuesError("End date can't be before start date!", "Evaluation")
238 evaluation
.setStartDate(sDate
)
239 evaluation
.setEndDate(eDate
)
240 evaluation
.setAnonymous(params
.has_key("anonymous"))
241 evaluation
.setAnnouncement(params
.get("announcement",""))
242 evaluation
.setTitle( params
.get("title","Evaluation") )
243 evaluation
.setContactInfo( params
.get("contactInfo","") )
244 evaluation
.setMandatoryParticipant(params
.has_key("mandatoryParticipant"))
245 evaluation
.setMandatoryAccount(params
.has_key("mandatoryAccount"))
247 evaluation
.setSubmissionsLimit( params
.get("submissionsLimit",0) )
249 raise FormValuesError("You must enter an integer for 'Max number of submissions'!", "Evaluation")
251 evaluationStartNotifyTo
= utils
.getEmailList(params
.get("evaluationStartNotifyTo", ""))
252 evaluationStartNotifyCc
= utils
.getEmailList(params
.get("evaluationStartNotifyCc", ""))
253 newSubmissionNotifyTo
= utils
.getEmailList(params
.get("newSubmissionNotifyTo", ""))
254 newSubmissionNotifyCc
= utils
.getEmailList(params
.get("newSubmissionNotifyCc", ""))
255 if params
.has_key("notifyAllAdherents") :
256 if self
.getWebFactory()!=None : #Event == Meeting/Lecture
257 for participant
in evaluation
.getConference().getParticipation().getParticipantList() :
258 email
= participant
.getEmail()
259 if email
not in evaluationStartNotifyTo
:
260 evaluationStartNotifyTo
.append(email
)
261 else : #Event == Conference
262 for registrant
in evaluation
.getConference().getRegistrantsList() :
263 email
= registrant
.getEmail()
264 if email
not in evaluationStartNotifyTo
:
265 evaluationStartNotifyTo
.append(email
)
266 if len( evaluationStartNotifyTo
+ evaluationStartNotifyCc
) < 1 :
267 evaluation
.removeNotification(Evaluation
._EVALUATION
_START
)
269 evaluationStartNotification
= Notification()
270 evaluationStartNotification
.setToList(evaluationStartNotifyTo
)
271 evaluationStartNotification
.setCCList(evaluationStartNotifyCc
)
272 evaluation
.setNotification(Evaluation
._EVALUATION
_START
, evaluationStartNotification
)
273 if len( newSubmissionNotifyTo
+ newSubmissionNotifyCc
) < 1 :
274 evaluation
.removeNotification(Evaluation
._NEW
_SUBMISSION
)
276 newSubmissionNotification
= Notification()
277 newSubmissionNotification
.setToList(newSubmissionNotifyTo
)
278 newSubmissionNotification
.setCCList(newSubmissionNotifyCc
)
279 evaluation
.setNotification(Evaluation
._NEW
_SUBMISSION
, newSubmissionNotification
)
282 self
._redirect
(urlHandlers
.UHConfModifEvaluationSetup
.getURL(self
._conf
))
285 class RHEvaluationEdit(RHEvaluationBase
):
286 """Edition of questions of an Evaluation."""
289 return evaluations
.WPConfModifEvaluationEdit(self
, self
._conf
).display()
291 class RHEvaluationEditPerformChanges( RHEvaluationBase
):
292 """performs changes for Evaluation questions."""
294 def _process( self
):
298 self
._evaluation
= self
._conf
.getEvaluation()
299 params
= self
.getRequestParams()
300 save
= params
.has_key("save")
301 type = params
.get("type", "")
302 mode
= params
.get("mode","")
303 questionValue
= params
.get("questionValue","").strip()
304 keyword
= params
.get("keyword","").strip()
310 if questionValue
=="" or keyword
=="":
311 raise FormValuesError("Please enter question and keyword!", "Evaluation")
312 if type in Question
._CHOICE
_SUBTYPES
:
313 choiceItem_1
= params
.get("choiceItem_1","").strip()
314 choiceItem_2
= params
.get("choiceItem_2","").strip()
315 if choiceItem_1
=="" or choiceItem_2
=="":
316 raise FormValuesError("Please enter all values for Choice Items.", "Evaluation")
317 if choiceItem_1
==choiceItem_2
:
318 raise FormValuesError("Please enter different values for Choice Items.", "Evaluation")
323 if mode
==Question
._ADD
and save
:
324 #create new Question depending on the type selected...
325 if type == Question
._TEXTBOX
: question
= Textbox()
326 elif type == Question
._TEXTAREA
: question
= Textarea()
327 elif type == Question
._PASSWORD
: question
= Password()
328 elif type == Question
._SELECT
: question
= Select()
329 elif type == Question
._RADIO
: question
= Radio()
330 elif type == Question
._CHECKBOX
: question
= Checkbox()
332 raise FormValuesError("unknown question type!", "Evaluation")
333 #set params for question
334 self
._setQuestionParams
(question
, params
)
339 elif mode
==Question
._EDIT
:
340 #the user edited a question.
342 #DON'T USE params:newPos BUT params:questionPos which is edited question position!!!
343 question
= self
._evaluation
.getQuestionAt(params
.get("questionPos",-1))
345 raise FormValuesError("No question found at the given position!", "Evaluation")
347 #set params for question
348 self
._setQuestionParams
(question
, params
)
349 #look if the user requested to change the position of a question.
351 questions_nb
= self
._evaluation
.getNbOfQuestions()
352 for i
in range(1, questions_nb
+1):
353 posChange
= int(params
.get("posChange_"+str(i
), -1))
355 self
._evaluation
.getQuestionAt(i
).setPosition(posChange
)
360 elif mode
==Question
._REMOVE
:
361 #DON'T USE params:newPos BUT params:questionPos which is edited question position!!!
362 self
._evaluation
.removeQuestion(int(params
.get("questionPos",-1)))
365 self
._redirect
(urlHandlers
.UHConfModifEvaluationEdit
.getURL(self
._conf
))
367 def _setQuestionParams(self
, question
, params
):
368 """ set the parameters for the given question with the dictionary (params).
370 question -- question to be edited.
371 params -- dictionary of parameters.
374 if not isinstance(question
, Question
) or not isinstance(params
, dict): return
375 #setting the attributes for the Question...
376 question
.setQuestionValue(params
.get("questionValue",""))
377 question
.setKeyword(params
.get("keyword",""))
378 question
.setRequired(params
.has_key("required"))
379 question
.setDescription(params
.get("description",""))
380 question
.setHelp(params
.get("help",""))
381 # question.setLevel(params.get("level",1))
383 newPos
= int(params
.get("newPos",-1))
384 #[ADD] When no evaluation for this question: add question in the evaluation.
385 if question
.getEvaluation()==None:
386 #USE params:newPos for setting the new posision.
387 self
._evaluation
.insertQuestion(question
, newPos
)
388 #[EDIT] When this question is linked to an evaluation: set its position.
390 question
.setPosition(newPos
)
392 if isinstance(question
, Box
):
393 question
.setDefaultAnswer(params
.get("defaultAnswer",""))
395 elif isinstance(question
, Choice
):
396 selectedChoiceItems
= params
.get("selectedChoiceItems",[])
397 #reinit choice items if already exists.
398 question
.removeAllChoiceItems()
399 #init variables for while loop
402 while itemText
!= None:
403 itemText
= params
.get( "choiceItem_"+str(currentItemId
) , None )
404 #If text has been well filled in, add it. Otherwise do nothing (i.e. don't add it).
405 if itemText
!=None and itemText
.strip()!="" :
406 isSelected
= str(currentItemId
) in selectedChoiceItems
407 question
.insertChoiceItem(itemText
, isSelected
)
411 class RHEvaluationPreview(RHEvaluationBase
):
412 """Preview of an Evaluation."""
415 params
= self
.getRequestParams()
416 if params
.get("status","")==_("submitted") and params
.has_key("submit"):
417 return evaluations
.WPConfModifEvaluationPreviewSubmitted(self
, self
._conf
).display()
419 return evaluations
.WPConfModifEvaluationPreview(self
, self
._conf
).display()
422 class RHEvaluationResults(RHEvaluationBase
):
423 """Results of an Evaluation."""
426 return evaluations
.WPConfModifEvaluationResults(self
, self
._conf
).display()
429 class RHEvaluationResultsOptions(RHEvaluationBase
):
430 """Do asked actions for the results."""
436 evaluation
= self
._conf
.getEvaluation()
437 params
= self
.getRequestParams()
438 export
= params
.has_key("exportStats")
439 select
= params
.has_key(Evaluation
._SELECT
_SUBMITTERS
)
440 remove
= params
.has_key(Evaluation
._REMOVE
_SUBMITTERS
)
446 excelGen
= ExcelGenerator()
448 excelGen
.addValue("Pseudonym")
449 excelGen
.addValue("submissionDate")
450 excelGen
.addValue("modificationDate")
451 for question
in evaluation
.getQuestions() :
452 excelGen
.addValue(question
.getKeyword())
453 #fill blank cells for the choice items if there are.
454 if question
.getAnswerClass()==MultipleChoicesAnswer
:
455 #above: only Checkbox is concerned but this way it's more generic.
456 for i
in range(1, question
.getNbOfChoiceItems()) :
457 excelGen
.addValue("---")
460 for submission
in evaluation
.getSubmissions() :
462 excelGen
.addValue(submission
.getSubmitterName())
463 excelGen
.addValue(submission
.getSubmissionDate(str))
464 excelGen
.addValue(submission
.getModificationDate(str))
465 for question
in evaluation
.getQuestions() :
466 answer
= submission
.getDictQuestionsAnswers().get(question
,None)
467 if question
.getAnswerClass()==TextAnswer
:
469 excelGen
.addValue("")
471 excelGen
.addValue(utils
.putbackQuotes(answer
.getAnswerValue()))
472 elif question
.getAnswerClass()==MultipleChoicesAnswer
:
474 for i
in range(0, question
.getNbOfChoiceItems()) :
475 excelGen
.addValue("")
477 for ci
in question
.getChoiceItemsOrderedKeys() :
478 if ci
in answer
.getAnswerValue() :
479 excelGen
.addValue(utils
.putbackQuotes(ci
))
481 excelGen
.addValue("")
482 return send_file(Evaluation
._CSV
_FILENAME
, StringIO(excelGen
.getExcelContent()), 'CSV')
488 return evaluations
.WPConfModifEvaluationResultsSubmitters(
489 self
, self
._conf
, Evaluation
._REMOVE
_SUBMITTERS
).display()
494 return evaluations
.WPConfModifEvaluationResultsSubmitters(
495 self
, self
._conf
, Evaluation
._SELECT
_SUBMITTERS
).display()
497 self
._redirect
(urlHandlers
.UHConfModifEvaluationResults
.getURL(self
._conf
))
499 class RHEvaluationResultsSubmittersActions(RHEvaluationBase
):
500 """Do asked actions for the submitters."""
506 evaluation
= self
._conf
.getEvaluation()
507 params
= self
.getRequestParams()
508 remove
= params
.has_key(Evaluation
._REMOVE
_SUBMITTERS
)
509 select
= params
.has_key(Evaluation
._SELECT
_SUBMITTERS
)
510 sessionVarName
= "selectedSubmissions_%s_%s"%(self
._conf
.getId(), evaluation
.getId())
516 removedSubmitters
= set(params
.get("removedSubmitters", []))
517 if removedSubmitters
:
518 #remove submissions in session variable
519 if sessionVarName
in session
:
520 selectedSubmissions
= session
.setdefault(sessionVarName
, set())
521 selectedSubmissions
-= removedSubmitters
522 session
.modified
= True
523 #remove submissions in database
524 removedSubmissions
= set(s
for s
in evaluation
.getSubmissions() if s
.getId() in removedSubmitters
)
525 for submission
in removedSubmissions
:
526 evaluation
.removeSubmission(submission
)
532 selectedSubmitters
= params
.get("selectedSubmitters", [])
533 if isinstance(selectedSubmitters
, str):
534 selectedSubmitters
= [selectedSubmitters
]
535 #insert selected submissions list in a session variable
536 selected
= set(s
.getId() for s
in evaluation
.getSubmissions() if s
.getId() in selectedSubmitters
)
537 if len(selected
) != len(evaluation
.getSubmissions()):
538 session
[sessionVarName
] = selected
540 # Everything selected => DELETE session var since that means "everything" and will stay that way when
541 # a new evaluation is submitted.
542 session
.pop(sessionVarName
, None)
545 self
._redirect
(urlHandlers
.UHConfModifEvaluationResults
.getURL(self
._conf
))