|
1 #!/usr/bin/python2.5 |
|
2 # |
|
3 # Copyright 2009 the Melange authors. |
|
4 # |
|
5 # Licensed under the Apache License, Version 2.0 (the "License"); |
|
6 # you may not use this file except in compliance with the License. |
|
7 # You may obtain a copy of the License at |
|
8 # |
|
9 # http://www.apache.org/licenses/LICENSE-2.0 |
|
10 # |
|
11 # Unless required by applicable law or agreed to in writing, software |
|
12 # distributed under the License is distributed on an "AS IS" BASIS, |
|
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
14 # See the License for the specific language governing permissions and |
|
15 # limitations under the License. |
|
16 |
|
17 """Custom widgets used for Survey form fields, plus the SurveyContent form. |
|
18 """ |
|
19 |
|
20 __authors__ = [ |
|
21 '"Daniel Diniz" <ajaksu@gmail.com>', |
|
22 '"James Levy" <jamesalexanderlevy@gmail.com>', |
|
23 '"Lennard de Rijk" <ljvderijk@gmail.com>', |
|
24 ] |
|
25 |
|
26 |
|
27 from itertools import chain |
|
28 import datetime |
|
29 |
|
30 from google.appengine.ext.db import djangoforms |
|
31 |
|
32 from django import forms |
|
33 from django.forms import widgets |
|
34 from django.forms.fields import CharField |
|
35 from django.template import loader |
|
36 from django.utils.encoding import force_unicode |
|
37 from django.utils.html import escape |
|
38 from django.utils.safestring import mark_safe |
|
39 |
|
40 from soc.logic import dicts |
|
41 from soc.logic.lists import Lists |
|
42 from soc.logic.models.survey import logic as survey_logic |
|
43 from soc.logic.models.survey import results_logic |
|
44 from soc.models.survey import SurveyContent |
|
45 |
|
46 |
|
47 class SurveyForm(djangoforms.ModelForm): |
|
48 """Main SurveyContent form. |
|
49 |
|
50 This class is used to produce survey forms for several circumstances: |
|
51 - Admin creating survey from scratch |
|
52 - Admin updating existing survey |
|
53 - User taking survey |
|
54 - User updating already taken survey |
|
55 |
|
56 Using dynamic properties of the survey model (if passed as an arg) the |
|
57 survey form is dynamically formed. |
|
58 """ |
|
59 |
|
60 def __init__(self, *args, **kwargs): |
|
61 """Store special kwargs as attributes. |
|
62 |
|
63 read_only: controls whether the survey taking UI allows data entry. |
|
64 editing: controls whether to show the edit or show form. |
|
65 """ |
|
66 |
|
67 self.kwargs = kwargs |
|
68 self.survey_content = self.kwargs.get('survey_content', None) |
|
69 self.this_user = self.kwargs.get('this_user', None) |
|
70 self.project = self.kwargs.get('project', None) |
|
71 self.survey_record = self.kwargs.get('survey_record', None) |
|
72 |
|
73 del self.kwargs['survey_content'] |
|
74 del self.kwargs['this_user'] |
|
75 del self.kwargs['project'] |
|
76 del self.kwargs['survey_record'] |
|
77 |
|
78 self.read_only = self.kwargs.get('read_only', None) |
|
79 if 'read_only' in self.kwargs: |
|
80 del self.kwargs['read_only'] |
|
81 |
|
82 self.editing = self.kwargs.get('editing', None) |
|
83 if 'editing' in self.kwargs: |
|
84 del self.kwargs['editing'] |
|
85 |
|
86 super(SurveyForm, self).__init__(*args, **self.kwargs) |
|
87 |
|
88 def getFields(self): |
|
89 """Build the SurveyContent (questions) form fields. |
|
90 |
|
91 Populates self.survey_fields, which will be ordered in self.insert_fields. |
|
92 """ |
|
93 |
|
94 if not self.survey_content: |
|
95 return |
|
96 |
|
97 self.survey_fields = {} |
|
98 schema = eval(self.survey_content.schema) |
|
99 has_record = (not self.editing) and self.survey_record |
|
100 extra_attrs = {} |
|
101 |
|
102 # figure out whether we want a read-only view |
|
103 if not self.editing: |
|
104 # only survey taking can be read-only |
|
105 read_only = self.read_only |
|
106 |
|
107 if not read_only: |
|
108 deadline = self.survey_content.survey_parent.get().deadline |
|
109 read_only = deadline and (datetime.datetime.now() > deadline) |
|
110 else: |
|
111 extra_attrs['disabled'] = 'disabled' |
|
112 |
|
113 # add unordered fields to self.survey_fields |
|
114 for field in self.survey_content.dynamic_properties(): |
|
115 |
|
116 # a comment made by the user |
|
117 comment = '' |
|
118 if has_record and hasattr(self.survey_record, field): |
|
119 # previously entered value |
|
120 value = getattr(self.survey_record, field) |
|
121 if hasattr(self.survey_record, 'comment_for_' + field): |
|
122 comment = getattr(self.survey_record, 'comment_for_' + field) |
|
123 else: |
|
124 # use prompts set by survey creator |
|
125 value = getattr(self.survey_content, field) |
|
126 |
|
127 if field not in schema: |
|
128 logging.error('field %s not found in schema %s' % |
|
129 (field, str(schema) ) ) |
|
130 continue |
|
131 elif 'question' in schema[field]: |
|
132 label = schema[field].get('question', None) or field |
|
133 else: |
|
134 label = field |
|
135 |
|
136 # dispatch to field-specific methods |
|
137 if schema[field]["type"] == "long_answer": |
|
138 self.addLongField(field, value, extra_attrs, label=label, |
|
139 comment=comment) |
|
140 elif schema[field]["type"] == "short_answer": |
|
141 self.addShortField(field, value, extra_attrs, label=label, |
|
142 comment=comment) |
|
143 elif schema[field]["type"] == "selection": |
|
144 self.addSingleField(field, value, extra_attrs, schema, label=label, |
|
145 comment=comment) |
|
146 elif schema[field]["type"] == "pick_multi": |
|
147 self.addMultiField(field, value, extra_attrs, schema, label=label, |
|
148 comment=comment) |
|
149 elif schema[field]["type"] == "pick_quant": |
|
150 self.addQuantField(field, value, extra_attrs, schema, label=label, |
|
151 comment=comment) |
|
152 |
|
153 return self.insertFields() |
|
154 |
|
155 def insertFields(self): |
|
156 """Add ordered fields to self.fields. |
|
157 """ |
|
158 |
|
159 survey_order = self.survey_content.getSurveyOrder() |
|
160 |
|
161 # first, insert dynamic survey fields |
|
162 for position, property in survey_order.items(): |
|
163 position = position * 2 |
|
164 self.fields.insert(position, property, self.survey_fields[property]) |
|
165 if not self.editing: |
|
166 property = 'comment_for_' + property |
|
167 self.fields.insert(position - 1, property, |
|
168 self.survey_fields[property]) |
|
169 return self.fields |
|
170 |
|
171 def addLongField(self, field, value, attrs, req=False, label='', tip='', |
|
172 comment=''): |
|
173 """Add a long answer fields to this form. |
|
174 |
|
175 params: |
|
176 field: the current field |
|
177 value: the initial value for this field |
|
178 attrs: additional attributes for field |
|
179 req: required bool |
|
180 label: label for field |
|
181 tip: tooltip text for field |
|
182 comment: initial comment value for field |
|
183 """ |
|
184 |
|
185 widget = widgets.Textarea(attrs=attrs) |
|
186 |
|
187 if not tip: |
|
188 tip = 'Please provide a long answer to this question.' |
|
189 |
|
190 question = CharField(help_text=tip, required=req, label=label, |
|
191 widget=widget, initial=value) |
|
192 self.survey_fields[field] = question |
|
193 |
|
194 if not self.editing: |
|
195 widget = widgets.Textarea(attrs=attrs) |
|
196 comment = CharField(help_text=tip, required=False, label='Comments', |
|
197 widget=widget, initial=comment) |
|
198 self.survey_fields['comment_for_' + field] = comment |
|
199 |
|
200 def addShortField(self, field, value, attrs, req=False, label='', tip='', |
|
201 comment=''): |
|
202 """Add a short answer fields to this form. |
|
203 |
|
204 params: |
|
205 field: the current field |
|
206 value: the initial value for this field |
|
207 attrs: additional attributes for field |
|
208 req: required bool |
|
209 label: label for field |
|
210 tip: tooltip text for field |
|
211 comment: initial comment value for field |
|
212 """ |
|
213 |
|
214 attrs['class'] = "text_question" |
|
215 widget = widgets.TextInput(attrs=attrs) |
|
216 |
|
217 if not tip: |
|
218 tip = 'Please provide a short answer to this question.' |
|
219 |
|
220 question = CharField(help_text=tip, required=req, label=label, |
|
221 widget=widget, max_length=140, initial=value) |
|
222 self.survey_fields[field] = question |
|
223 |
|
224 if not self.editing: |
|
225 widget = widgets.Textarea(attrs=attrs) |
|
226 comment = CharField(help_text=tip, required=False, label='Comments', |
|
227 widget=widget, initial=comment) |
|
228 self.survey_fields['comment_for_' + field] = comment |
|
229 |
|
230 def addSingleField(self, field, value, attrs, schema, req=False, label='', |
|
231 tip='', comment=''): |
|
232 """Add a selection field to this form. |
|
233 |
|
234 params: |
|
235 field: the current field |
|
236 value: the initial value for this field |
|
237 attrs: additional attributes for field |
|
238 schema: schema for survey |
|
239 req: required bool |
|
240 label: label for field |
|
241 tip: tooltip text for field |
|
242 comment: initial comment value for field |
|
243 """ |
|
244 |
|
245 if self.editing: |
|
246 kind = schema[field]["type"] |
|
247 render = schema[field]["render"] |
|
248 widget = UniversalChoiceEditor(kind, render) |
|
249 else: |
|
250 widget = WIDGETS[schema[field]['render']](attrs=attrs) |
|
251 |
|
252 these_choices = [] |
|
253 # add all properties, but select chosen one |
|
254 options = getattr(self.survey_content, field) |
|
255 has_record = not self.editing and self.survey_record |
|
256 if has_record and hasattr(self.survey_record, field): |
|
257 these_choices.append((value, value)) |
|
258 if value in options: |
|
259 options.remove(value) |
|
260 |
|
261 for option in options: |
|
262 these_choices.append((option, option)) |
|
263 if not tip: |
|
264 tip = 'Please select an answer this question.' |
|
265 |
|
266 question = PickOneField(help_text=tip, required=req, label=label, |
|
267 choices=tuple(these_choices), widget=widget) |
|
268 self.survey_fields[field] = question |
|
269 |
|
270 if not self.editing: |
|
271 widget = widgets.Textarea(attrs=attrs) |
|
272 comment = CharField(help_text=tip, required=False, label='Comments', |
|
273 widget=widget, initial=comment) |
|
274 self.survey_fields['comment_for_' + field] = comment |
|
275 |
|
276 def addMultiField(self, field, value, attrs, schema, req=False, label='', |
|
277 tip='', comment=''): |
|
278 """Add a pick_multi field to this form. |
|
279 |
|
280 params: |
|
281 field: the current field |
|
282 value: the initial value for this field |
|
283 attrs: additional attributes for field |
|
284 schema: schema for survey |
|
285 req: required bool |
|
286 label: label for field |
|
287 tip: tooltip text for field |
|
288 comment: initial comment value for field |
|
289 """ |
|
290 |
|
291 if self.editing: |
|
292 kind = schema[field]["type"] |
|
293 render = schema[field]["render"] |
|
294 widget = UniversalChoiceEditor(kind, render) |
|
295 else: |
|
296 widget = WIDGETS[schema[field]['render']](attrs=attrs) |
|
297 |
|
298 # TODO(ajaksu) need to allow checking checkboxes by default |
|
299 if self.survey_record and isinstance(value, basestring): |
|
300 # pass value as 'initial' so MultipleChoiceField renders checked boxes |
|
301 value = value.split(',') |
|
302 else: |
|
303 value = None |
|
304 |
|
305 these_choices = [(v,v) for v in getattr(self.survey_content, field)] |
|
306 if not tip: |
|
307 tip = 'Please select one or more of these choices.' |
|
308 |
|
309 question = PickManyField(help_text=tip, required=req, label=label, |
|
310 choices=tuple(these_choices), widget=widget, |
|
311 initial=value) |
|
312 self.survey_fields[field] = question |
|
313 if not self.editing: |
|
314 widget = widgets.Textarea(attrs=attrs) |
|
315 comment = CharField(help_text=tip, required=False, label='Comments', |
|
316 widget=widget, initial=comment) |
|
317 self.survey_fields['comment_for_' + field] = comment |
|
318 |
|
319 def addQuantField(self, field, value, attrs, schema, req=False, label='', |
|
320 tip='', comment=''): |
|
321 """Add a pick_quant field to this form. |
|
322 |
|
323 params: |
|
324 field: the current field |
|
325 value: the initial value for this field |
|
326 attrs: additional attributes for field |
|
327 schema: schema for survey |
|
328 req: required bool |
|
329 label: label for field |
|
330 tip: tooltip text for field |
|
331 comment: initial comment value for field |
|
332 """ |
|
333 |
|
334 if self.editing: |
|
335 kind = schema[field]["type"] |
|
336 render = schema[field]["render"] |
|
337 widget = UniversalChoiceEditor(kind, render) |
|
338 else: |
|
339 widget = WIDGETS[schema[field]['render']](attrs=attrs) |
|
340 |
|
341 if self.survey_record: |
|
342 value = value |
|
343 else: |
|
344 value = None |
|
345 |
|
346 these_choices = [(v,v) for v in getattr(self.survey_content, field)] |
|
347 if not tip: |
|
348 tip = 'Please select one of these choices.' |
|
349 |
|
350 question = PickQuantField(help_text=tip, required=req, label=label, |
|
351 choices=tuple(these_choices), widget=widget, |
|
352 initial=value) |
|
353 self.survey_fields[field] = question |
|
354 if not self.editing: |
|
355 widget = widgets.Textarea(attrs=attrs) |
|
356 comment = CharField(help_text=tip, required=False, label='Comments', |
|
357 widget=widget, initial=comment) |
|
358 self.survey_fields['comment_for_' + field] = comment |
|
359 |
|
360 |
|
361 class Meta(object): |
|
362 model = SurveyContent |
|
363 exclude = ['schema'] |
|
364 |
|
365 |
|
366 class UniversalChoiceEditor(widgets.Widget): |
|
367 """Edit interface for choice questions. |
|
368 |
|
369 Allows adding and removing options, re-ordering and editing option text. |
|
370 """ |
|
371 |
|
372 def __init__(self, kind, render, attrs=None, choices=()): |
|
373 |
|
374 self.attrs = attrs or {} |
|
375 |
|
376 # Choices can be any iterable, but we may need to render this widget |
|
377 # multiple times. Thus, collapse it into a list so it can be consumed |
|
378 # more than once. |
|
379 self.choices = list(choices) |
|
380 self.kind = kind |
|
381 self.render_as = render |
|
382 |
|
383 def render(self, name, value, attrs=None, choices=()): |
|
384 """ renders UCE widget |
|
385 """ |
|
386 |
|
387 if value is None: |
|
388 value = '' |
|
389 |
|
390 final_attrs = self.build_attrs(attrs, name=name) |
|
391 |
|
392 # find out which options should be selected in type and render drop-downs. |
|
393 selected = 'selected="selected"' |
|
394 context = dict( |
|
395 name=name, |
|
396 is_selection=selected * (self.kind == 'selection'), |
|
397 is_pick_multi=selected * (self.kind == 'pick_multi'), |
|
398 is_pick_quant=selected * (self.kind == 'pick_quant'), |
|
399 is_select=selected * (self.render_as == 'single_select'), |
|
400 is_checkboxes=selected * (self.render_as == 'multi_checkbox'), |
|
401 is_radio_buttons=selected * (self.render_as == 'quant_radio'), |
|
402 ) |
|
403 |
|
404 str_value = forms.util.smart_unicode(value) # normalize to string. |
|
405 chained_choices = enumerate(chain(self.choices, choices)) |
|
406 choices = {} |
|
407 |
|
408 for i, (option_value, option_label) in chained_choices: |
|
409 option_value = escape(forms.util.smart_unicode(option_value)) |
|
410 choices[i] = option_value |
|
411 context['choices'] = choices |
|
412 |
|
413 template = 'soc/survey/universal_choice_editor.html' |
|
414 return loader.render_to_string(template, context) |
|
415 |
|
416 |
|
417 class PickOneField(forms.ChoiceField): |
|
418 """Stub for customizing the single choice field. |
|
419 """ |
|
420 #TODO(james): Ensure that more than one option cannot be selected |
|
421 |
|
422 def __init__(self, *args, **kwargs): |
|
423 super(PickOneField, self).__init__(*args, **kwargs) |
|
424 |
|
425 |
|
426 class PickManyField(forms.MultipleChoiceField): |
|
427 """Stub for customizing the multiple choice field. |
|
428 """ |
|
429 |
|
430 def __init__(self, *args, **kwargs): |
|
431 super(PickManyField, self).__init__(*args, **kwargs) |
|
432 |
|
433 |
|
434 class PickQuantField(forms.MultipleChoiceField): |
|
435 """Stub for customizing the multiple choice field. |
|
436 """ |
|
437 #TODO(james): Ensure that more than one quant cannot be selected |
|
438 |
|
439 def __init__(self, *args, **kwargs): |
|
440 super(PickQuantField, self).__init__(*args, **kwargs) |
|
441 |
|
442 |
|
443 class PickOneSelect(forms.Select): |
|
444 """Stub for customizing the single choice select widget. |
|
445 """ |
|
446 |
|
447 def __init__(self, *args, **kwargs): |
|
448 super(PickOneSelect, self).__init__(*args, **kwargs) |
|
449 |
|
450 |
|
451 class PickManyCheckbox(forms.CheckboxSelectMultiple): |
|
452 """Customized multiple choice checkbox widget. |
|
453 """ |
|
454 |
|
455 def __init__(self, *args, **kwargs): |
|
456 super(PickManyCheckbox, self).__init__(*args, **kwargs) |
|
457 |
|
458 def render(self, name, value, attrs=None, choices=()): |
|
459 """Render checkboxes as list items grouped in a fieldset. |
|
460 |
|
461 This is the pick_multi widget for survey taking |
|
462 """ |
|
463 |
|
464 if value is None: |
|
465 value = [] |
|
466 has_id = attrs and attrs.has_key('id') |
|
467 final_attrs = self.build_attrs(attrs, name=name) |
|
468 |
|
469 # normalize to strings. |
|
470 str_values = set([forms.util.smart_unicode(v) for v in value]) |
|
471 is_checked = lambda value: value in str_values |
|
472 smart_unicode = forms.util.smart_unicode |
|
473 |
|
474 # set container fieldset and list |
|
475 output = [u'<fieldset id="id_%s">\n <ul class="pick_multi">' % name] |
|
476 |
|
477 # add numbered checkboxes wrapped in list items |
|
478 chained_choices = enumerate(chain(self.choices, choices)) |
|
479 for i, (option_value, option_label) in chained_choices: |
|
480 option_label = escape(smart_unicode(option_label)) |
|
481 |
|
482 # If an ID attribute was given, add a numeric index as a suffix, |
|
483 # so that the checkboxes don't all have the same ID attribute. |
|
484 if has_id: |
|
485 final_attrs = dict(final_attrs, id='%s_%s' % (attrs['id'], i)) |
|
486 |
|
487 cb = widgets.CheckboxInput(final_attrs, check_test=is_checked) |
|
488 rendered_cb = cb.render(name, option_value) |
|
489 cb_label = (rendered_cb, option_label) |
|
490 |
|
491 output.append(u' <li><label>%s %s</label></li>' % cb_label) |
|
492 |
|
493 output.append(u' </ul>\n</fieldset>') |
|
494 return u'\n'.join(output) |
|
495 |
|
496 def id_for_label(self, id_): |
|
497 # see the comment for RadioSelect.id_for_label() |
|
498 if id_: |
|
499 id_ += '_fieldset' |
|
500 return id_ |
|
501 id_for_label = classmethod(id_for_label) |
|
502 |
|
503 |
|
504 class PickQuantRadioRenderer(widgets.RadioFieldRenderer): |
|
505 """Used by PickQuantRadio to enable customization of radio widgets. |
|
506 """ |
|
507 |
|
508 def __init__(self, *args, **kwargs): |
|
509 super(PickQuantRadioRenderer, self).__init__(*args, **kwargs) |
|
510 |
|
511 def render(self): |
|
512 """Outputs set of radio fields in a div. |
|
513 """ |
|
514 |
|
515 return mark_safe(u'<div class="quant_radio">\n%s\n</div>' |
|
516 % u'\n'.join([u'%s' % force_unicode(w) for w in self])) |
|
517 |
|
518 |
|
519 class PickQuantRadio(forms.RadioSelect): |
|
520 """TODO(James,Ajaksu) Fix Docstring |
|
521 """ |
|
522 |
|
523 renderer = PickQuantRadioRenderer |
|
524 |
|
525 def __init__(self, *args, **kwargs): |
|
526 super(PickQuantRadio, self).__init__(*args, **kwargs) |
|
527 |
|
528 |
|
529 # in the future, we'll have more widget types here |
|
530 WIDGETS = {'multi_checkbox': PickManyCheckbox, |
|
531 'single_select': PickOneSelect, |
|
532 'quant_radio': PickQuantRadio} |
|
533 |
|
534 |
|
535 class SurveyResults(widgets.Widget): |
|
536 """Render List of Survey Results For Given Survey. |
|
537 """ |
|
538 |
|
539 def render(self, survey, params, filter=filter, limit=1000, offset=0, |
|
540 order=[], idx=0, context={}): |
|
541 """ renders list of survey results |
|
542 |
|
543 params: |
|
544 survey: current survey |
|
545 params: dict of params for rendering list |
|
546 filter: filter for list results |
|
547 limit: limit for list results |
|
548 offset: offset for list results |
|
549 order: order for list results |
|
550 idx: index for list results |
|
551 context: context dict for template |
|
552 """ |
|
553 |
|
554 logic = results_logic |
|
555 filter = {'survey': survey} |
|
556 data = logic.getForFields(filter=filter, limit=limit, offset=offset, |
|
557 order=order) |
|
558 |
|
559 params['name'] = "Survey Results" |
|
560 content = { |
|
561 'idx': idx, |
|
562 'data': data, |
|
563 'logic': logic, |
|
564 'limit': limit, |
|
565 } |
|
566 updates = dicts.rename(params, params['list_params']) |
|
567 content.update(updates) |
|
568 contents = [content] |
|
569 |
|
570 if len(content) == 1: |
|
571 content = content[0] |
|
572 key_order = content.get('key_order') |
|
573 |
|
574 context['list'] = Lists(contents) |
|
575 |
|
576 # TODO(ajaksu) is this the best way to build the results list? |
|
577 for list_ in context['list']._contents: |
|
578 if len(list_['data']) < 1: |
|
579 return "<p>No Survey Results Have Been Submitted</p>" |
|
580 |
|
581 list_['row'] = 'soc/survey/list/results_row.html' |
|
582 list_['heading'] = 'soc/survey/list/results_heading.html' |
|
583 list_['description'] = 'Survey Results:' |
|
584 |
|
585 context['properties'] = survey.survey_content.orderedProperties() |
|
586 context['entity_type'] = "Survey Results" |
|
587 context['entity_type_plural'] = "Results" |
|
588 context['no_lists_msg'] = "No Survey Results" |
|
589 |
|
590 path = (survey.entity_type().lower(), survey.prefix, |
|
591 survey.scope_path, survey.link_id) |
|
592 context['grade_action'] = "/%s/grade/%s/%s/%s" % path |
|
593 |
|
594 markup = loader.render_to_string('soc/survey/results.html', |
|
595 dictionary=context).strip('\n') |
|
596 return markup |
|
597 |
|
598 |
|
599 def getRoleSpecificFields(survey, user, this_project, survey_form, |
|
600 survey_record): |
|
601 """For evaluations, mentors get required Project and Grade fields, and |
|
602 students get a required Project field. |
|
603 |
|
604 Because we need to get a list of the user's projects, we call the |
|
605 logic getProjects method, which doubles as an access check. |
|
606 (No projects means that the survey cannot be taken.) |
|
607 |
|
608 params: |
|
609 survey: the survey being taken |
|
610 user: the survey-taking user |
|
611 this_project: either an already-selected project, or None |
|
612 survey_form: the surveyForm widget for this survey |
|
613 survey_record: an existing survey record for a user-project-survey combo, |
|
614 or None |
|
615 """ |
|
616 |
|
617 from django import forms |
|
618 |
|
619 field_count = len(eval(survey.survey_content.schema).items()) |
|
620 these_projects = survey_logic.getProjects(survey, user) |
|
621 if not these_projects: return False # no projects found |
|
622 |
|
623 project_pairs = [] |
|
624 #insert a select field with options for each project |
|
625 for project in these_projects: |
|
626 project_pairs.append((project.key(), project.title)) |
|
627 if project_pairs: |
|
628 project_tuples = tuple(project_pairs) |
|
629 # add select field containing list of projects |
|
630 projectField = forms.fields.ChoiceField( |
|
631 choices=project_tuples, |
|
632 required=True, |
|
633 widget=forms.Select()) |
|
634 projectField.choices.insert(0, (None, "Choose a Project") ) |
|
635 # if editing an existing survey |
|
636 if not this_project and survey_record: |
|
637 this_project = survey_record.project |
|
638 if this_project: |
|
639 for tup in project_tuples: |
|
640 if tup[1] == this_project.title: |
|
641 if survey_record: project_name = tup[1] + " (Saved)" |
|
642 else: project_name = tup[1] |
|
643 projectField.choices.remove(tup) |
|
644 projectField.choices.insert(0, (tup[0], project_name) ) |
|
645 break |
|
646 survey_form.fields.insert(0, 'project', projectField ) |
|
647 |
|
648 if survey.taking_access == "mentor evaluation": |
|
649 # If this is a mentor, add a field |
|
650 # determining if student passes or fails. |
|
651 # Activate grades handler should determine whether new status |
|
652 # is midterm_passed, final_passed, etc. |
|
653 grade_choices = (('pass', 'Pass'), ('fail', 'Fail')) |
|
654 grade_vals = { 'pass': True, 'fail': False } |
|
655 gradeField = forms.fields.ChoiceField(choices=grade_choices, |
|
656 required=True, |
|
657 widget=forms.Select()) |
|
658 |
|
659 gradeField.choices.insert(0, (None, "Choose a Grade") ) |
|
660 if survey_record: |
|
661 for g in grade_choices: |
|
662 if grade_vals[g[0]] == survey_record.grade: |
|
663 gradeField.choices.insert(0, (g[0],g[1] + " (Saved)") ) |
|
664 gradeField.choices.remove(g) |
|
665 break; |
|
666 gradeField.show_hidden_initial = True |
|
667 |
|
668 survey_form.fields.insert(field_count + 1, 'grade', gradeField) |
|
669 |
|
670 return survey_form |