51 from soc.views.helper import surveys |
51 from soc.views.helper import surveys |
52 from soc.views.helper import widgets |
52 from soc.views.helper import widgets |
53 from soc.views.models import base |
53 from soc.views.models import base |
54 |
54 |
55 |
55 |
56 CHOICE_TYPES = set(('selection', 'pick_multi', 'choice', 'pick_quant')) |
56 DEF_CHOICE_TYPES = set(('selection', 'pick_multi', 'choice', 'pick_quant')) |
57 TEXT_TYPES = set(('long_answer', 'short_answer')) |
57 DEF_TEXT_TYPES = set(('long_answer', 'short_answer')) |
58 PROPERTY_TYPES = tuple(CHOICE_TYPES) + tuple(TEXT_TYPES) |
58 DEF_PROPERTY_TYPES = tuple(DEF_CHOICE_TYPES) + tuple(DEF_TEXT_TYPES) |
59 |
59 |
60 # used in View.getSchemaOptions to map POST values |
60 # used in View.getSchemaOptions to map POST values |
61 BOOL = {'True': True, 'False': False} |
61 DEF_BOOL = {'True': True, 'False': False} |
62 |
62 |
63 _short_answer = ("Short Answer", |
63 DEF_SHORT_ANSWER = ("Short Answer", |
64 "Less than 40 characters. Rendered as a text input. " |
64 "Less than 40 characters. Rendered as a text input. " |
65 "It's possible to add a free form question (Content) " |
65 "It's possible to add a free form question (Content) " |
66 "and a in-input prompt/example text.") |
66 "and a in-input prompt/example text.") |
67 _choice = ("Selection", |
67 DEF_CHOICE = ( |
68 "Can be set as a single choice (selection) or multiple choice " |
68 "Selection", |
69 "(pick_multi) question. Rendered as a select (single choice) " |
69 "Can be set as a single choice (selection) or multiple choice " |
70 "or a group of checkboxes (multiple choice). It's possible to " |
70 "(pick_multi) question. Rendered as a select (single choice) " |
71 "add a free form question (Content) and as many free form options " |
71 "or a group of checkboxes (multiple choice). It's possible to " |
72 "as wanted. Each option can be edited (double-click), deleted " |
72 "add a free form question (Content) and as many free form options " |
73 "(click on (-) button) or reordered (drag and drop).") |
73 "as wanted. Each option can be edited (double-click), deleted " |
74 _long_answer = ("Long Answer", |
74 "(click on (-) button) or reordered (drag and drop).") |
75 "Unlimited length, auto-growing field. Rendered as a textarea. " |
75 DEF_LONG_ANSWER = ( |
76 "It's possible to add a free form question (Content) and " |
76 "Long Answer", |
77 "an in-input prompt/example text.") |
77 "Unlimited length, auto-growing field. Rendered as a textarea. " |
78 QUESTION_TYPES = dict(short_answer=_short_answer, long_answer=_long_answer, |
78 "It's possible to add a free form question (Content) and an in-input " |
79 choice=_choice) |
79 "prompt/example text.") |
80 |
80 |
81 # for to_csv and View.exportSerialized |
81 DEF_QUESTION_TYPES = dict(short_answer=DEF_SHORT_ANSWER, |
82 FIELDS = 'author modified_by' |
82 long_answer=DEF_LONG_ANSWER, choice=DEF_CHOICE) |
83 PLAIN = 'is_featured content created modified' |
83 |
|
84 # for toCSV and View.exportSerialized |
|
85 DEF_FIELDS = 'author modified_by' |
|
86 DEF_PLAIN = 'is_featured content created modified' |
84 |
87 |
85 |
88 |
86 class View(base.View): |
89 class View(base.View): |
87 """View methods for the Survey model. |
90 """View methods for the Survey model. |
88 """ |
91 """ |
128 'View survey results for user'), |
131 'View survey results for user'), |
129 ] |
132 ] |
130 |
133 |
131 new_params['export_content_type'] = 'text/text' |
134 new_params['export_content_type'] = 'text/text' |
132 new_params['export_extension'] = '.csv' |
135 new_params['export_extension'] = '.csv' |
133 new_params['export_function'] = surveys.to_csv(self) |
136 new_params['export_function'] = surveys.toCSV(self) |
134 new_params['delete_redirect'] = '/' |
137 new_params['delete_redirect'] = '/' |
135 new_params['list_key_order'] = [ |
138 new_params['list_key_order'] = [ |
136 'link_id', 'scope_path', 'name', 'short_name', 'title', |
139 'link_id', 'scope_path', 'name', 'short_name', 'title', |
137 'content', 'prefix','read_access','write_access'] |
140 'content', 'prefix','read_access','write_access'] |
138 |
141 |
139 new_params['edit_template'] = 'soc/survey/edit.html' |
142 new_params['edit_template'] = 'soc/survey/edit.html' |
140 new_params['create_template'] = 'soc/survey/edit.html' |
143 new_params['create_template'] = 'soc/survey/edit.html' |
141 new_params['public_template'] = 'soc/survey/public.html' |
144 new_params['public_template'] = 'soc/survey/public.html' |
142 new_params['take_template'] = 'soc/survey/take.html' |
145 new_params['take_template'] = 'soc/survey/take.html' |
143 |
146 |
144 # TODO which one of these are leftovers from Document? |
147 # TODO: which one of these are leftovers from Document? |
145 new_params['no_create_raw'] = True |
148 new_params['no_create_raw'] = True |
146 new_params['no_create_with_scope'] = True |
149 new_params['no_create_with_scope'] = True |
147 new_params['no_create_with_key_fields'] = True |
150 new_params['no_create_with_key_fields'] = True |
148 new_params['no_list_raw'] = True |
151 new_params['no_list_raw'] = True |
149 new_params['sans_link_id_create'] = True |
152 new_params['sans_link_id_create'] = True |
279 |
282 |
280 # get the current questions from the SurveyContent |
283 # get the current questions from the SurveyContent |
281 if question_name not in schema: |
284 if question_name not in schema: |
282 continue |
285 continue |
283 |
286 |
284 if schema[question_name]['type'] not in CHOICE_TYPES: |
287 if schema[question_name]['type'] not in DEF_CHOICE_TYPES: |
285 # Choice questions are always regenerated from request, see |
288 # Choice questions are always regenerated from request, see |
286 # self.get_request_questions() |
289 # self.get_request_questions() |
287 question = getattr(survey_content, question_name) |
290 question = getattr(survey_content, question_name) |
288 survey_fields[question_name] = question |
291 survey_fields[question_name] = question |
289 |
292 |
327 if name not in schema: |
330 if name not in schema: |
328 if 'NEW_' + name in POST: |
331 if 'NEW_' + name in POST: |
329 # new Choice question, set generic type and get its index |
332 # new Choice question, set generic type and get its index |
330 schema[name] = {'type': 'choice'} |
333 schema[name] = {'type': 'choice'} |
331 |
334 |
332 if name in schema and schema[name]['type'] in CHOICE_TYPES: |
335 if name in schema and schema[name]['type'] in DEF_CHOICE_TYPES: |
333 # build an index:content dictionary |
336 # build an index:content dictionary |
334 if name in survey_fields: |
337 if name in survey_fields: |
335 if value not in survey_fields[name]: |
338 if value not in survey_fields[name]: |
336 survey_fields[name][int(number)] = value |
339 survey_fields[name][int(number)] = value |
337 else: |
340 else: |
346 index = int(index) |
349 index = int(index) |
347 |
350 |
348 field_name = prefix.sub('', key) |
351 field_name = prefix.sub('', key) |
349 field = 'id_' + key |
352 field = 'id_' + key |
350 |
353 |
351 for ptype in PROPERTY_TYPES: |
354 for ptype in DEF_PROPERTY_TYPES: |
352 # should only match one |
355 # should only match one |
353 if ptype + "__" in field_name: |
356 if ptype + "__" in field_name: |
354 field_name = field_name.replace(ptype + "__", "") |
357 field_name = field_name.replace(ptype + "__", "") |
355 if field_name not in schema: |
358 if field_name not in schema: |
356 schema[field_name]= {} |
359 schema[field_name]= {} |
373 RENDER_TYPES = {'select': 'selection', |
376 RENDER_TYPES = {'select': 'selection', |
374 'checkboxes': 'pick_multi', |
377 'checkboxes': 'pick_multi', |
375 'radio_buttons': 'pick_quant' } |
378 'radio_buttons': 'pick_quant' } |
376 |
379 |
377 for key in schema: |
380 for key in schema: |
378 if schema[key]['type'] in CHOICE_TYPES and key in survey_fields: |
381 if schema[key]['type'] in DEF_CHOICE_TYPES and key in survey_fields: |
379 render_for = 'render_for_' + key |
382 render_for = 'render_for_' + key |
380 if render_for in POST: |
383 if render_for in POST: |
381 schema[key]['render'] = RENDER[POST[render_for]] |
384 schema[key]['render'] = RENDER[POST[render_for]] |
382 schema[key]['type'] = RENDER_TYPES[POST[render_for]] |
385 schema[key]['type'] = RENDER_TYPES[POST[render_for]] |
383 |
386 |
413 if question_for in POST and POST[question_for]: |
416 if question_for in POST and POST[question_for]: |
414 schema[key]["question"] = POST[question_for] |
417 schema[key]["question"] = POST[question_for] |
415 |
418 |
416 # set wheter the question is required |
419 # set wheter the question is required |
417 required_for = 'required_for_' + key |
420 required_for = 'required_for_' + key |
418 schema[key]['required'] = BOOL[POST[required_for]] |
421 schema[key]['required'] = DEF_BOOL[POST[required_for]] |
419 |
422 |
420 # set wheter the question allows comments |
423 # set wheter the question allows comments |
421 comment_for = 'comment_for_' + key |
424 comment_for = 'comment_for_' + key |
422 schema[key]['has_comment'] = BOOL[POST[comment_for]] |
425 schema[key]['has_comment'] = DEF_BOOL[POST[comment_for]] |
423 |
426 |
424 # set the question index from JS-calculated value |
427 # set the question index from JS-calculated value |
425 index_for = 'index_for_' + key |
428 index_for = 'index_for_' + key |
426 if index_for in POST: |
429 if index_for in POST: |
427 schema[key]['index'] = int(POST[index_for].replace('__', '')) |
430 schema[key]['index'] = int(POST[index_for].replace('__', '')) |
428 |
431 |
429 def createGet(self, request, context, params, seed): |
432 def createGet(self, request, context, params, seed): |
430 """Pass the question types for the survey creation template. |
433 """Pass the question types for the survey creation template. |
431 """ |
434 """ |
432 |
435 |
433 context['question_types'] = QUESTION_TYPES |
436 context['question_types'] = DEF_QUESTION_TYPES |
434 |
437 |
435 # avoid spurious results from showing on creation |
438 # avoid spurious results from showing on creation |
436 context['new_survey'] = True |
439 context['new_survey'] = True |
437 return super(View, self).createGet(request, context, params, seed) |
440 return super(View, self).createGet(request, context, params, seed) |
438 |
441 |
447 |
450 |
448 survey_form = surveys.SurveyEditForm(survey_content=survey_content, |
451 survey_form = surveys.SurveyEditForm(survey_content=survey_content, |
449 survey_logic=params['logic']) |
452 survey_logic=params['logic']) |
450 survey_form.getFields() |
453 survey_form.getFields() |
451 |
454 |
452 local = dict(survey_form=survey_form, question_types=QUESTION_TYPES, |
455 local = dict(survey_form=survey_form, question_types=DEF_QUESTION_TYPES, |
453 survey_h=entity.survey_content) |
456 survey_h=entity.survey_content) |
454 context.update(local) |
457 context.update(local) |
455 |
458 |
456 params['edit_form'] = surveys.HelperForm(params['edit_form']) |
459 params['edit_form'] = surveys.HelperForm(params['edit_form']) |
457 if entity.survey_end and datetime.datetime.now() > entity.survey_end: |
460 if entity.survey_end and datetime.datetime.now() > entity.survey_end: |
645 else: |
648 else: |
646 survey_end_text = " by " + str( |
649 survey_end_text = " by " + str( |
647 survey.survey_end.strftime("%A, %d. %B %Y %I:%M%p")) |
650 survey.survey_end.strftime("%A, %d. %B %Y %I:%M%p")) |
648 |
651 |
649 if survey_record: |
652 if survey_record: |
650 help_text = "You may edit and re-submit this survey" + survey_end_text + "." |
653 help_text = "You may edit and re-submit this survey %s." %( |
|
654 survey_end_text) |
651 status = "edit" |
655 status = "edit" |
652 else: |
656 else: |
653 help_text = "Please complete this survey" + survey_end_text + "." |
657 help_text = "Please complete this survey %s." %( |
|
658 survey_end_text) |
654 status = "create" |
659 status = "create" |
655 |
660 |
656 # update the context with the help_text and status |
661 # update the context with the help_text and status |
657 context_update = dict(status=status, help_text=help_text) |
662 context_update = dict(status=status, help_text=help_text) |
658 context.update(context_update) |
663 context.update(context_update) |
659 |
|
660 def activate(self, request, **kwargs): |
|
661 """This is a hack to support the 'Enable grades' button. |
|
662 """ |
|
663 self.activateGrades(request) |
|
664 redirect_path = request.path.replace('/activate/', '/edit/') + '?activate=1' |
|
665 return http.HttpResponseRedirect(redirect_path) |
|
666 |
|
667 def activateGrades(self, request, **kwargs): |
|
668 """Updates SurveyRecord's grades for a given Survey. |
|
669 """ |
|
670 survey_key_name = survey_logic.getKeyNameFromPath(request.path) |
|
671 survey = Survey.get_by_key_name(survey_key_name) |
|
672 survey_logic.activateGrades(survey) |
|
673 return |
|
674 |
664 |
675 @decorators.merge_params |
665 @decorators.merge_params |
676 @decorators.check_access |
666 @decorators.check_access |
677 def viewResults(self, request, access_type, page_name=None, |
667 def viewResults(self, request, access_type, page_name=None, |
678 params=None, **kwargs): |
668 params=None, **kwargs): |
725 |
715 |
726 @decorators.merge_params |
716 @decorators.merge_params |
727 @decorators.check_access |
717 @decorators.check_access |
728 def exportSerialized(self, request, access_type, page_name=None, |
718 def exportSerialized(self, request, access_type, page_name=None, |
729 params=None, **kwargs): |
719 params=None, **kwargs): |
730 |
720 """Exports Surveys in JSON format. |
731 sur, context = self.getContextEntity(request, page_name, params, kwargs) |
721 |
732 |
722 For args see base.View.public(). |
733 if context is None: |
723 """ |
734 # user cannot see this page, return error response |
724 |
735 return sur |
725 survey_logic = params['logic'] |
|
726 |
|
727 try: |
|
728 sur = survey_logic.getFromKeyFieldsOr404(kwargs) |
|
729 except out_of_band.Error, error: |
|
730 return responses.errorResponse( |
|
731 error, request, template=params['error_public']) |
736 |
732 |
737 json = sur.toDict() |
733 json = sur.toDict() |
738 json.update(dict((f, str(getattr(sur, f))) for f in PLAIN.split())) |
734 json.update(dict((f, str(getattr(sur, f))) for f in DEF_PLAIN.split())) |
739 static = ((f, str(getattr(sur, f).link_id)) for f in FIELDS.split()) |
735 static = ((f, str(getattr(sur, f).link_id)) for f in DEF_FIELDS.split()) |
740 json.update(dict(static)) |
736 json.update(dict(static)) |
741 |
737 |
742 dynamic = sur.survey_content.dynamic_properties() |
738 dynamic = sur.survey_content.dynamic_properties() |
743 content = ((prop, getattr(sur.survey_content, prop)) for prop in dynamic) |
739 content = ((prop, getattr(sur.survey_content, prop)) for prop in dynamic) |
744 json['survey_content'] = dict(content) |
740 json['survey_content'] = dict(content) |
749 data = simplejson.dumps(json, indent=2) |
745 data = simplejson.dumps(json, indent=2) |
750 |
746 |
751 return self.json(request, data=json) |
747 return self.json(request, data=json) |
752 |
748 |
753 def importSerialized(self, request, fields, user): |
749 def importSerialized(self, request, fields, user): |
|
750 """Import Surveys in JSON format. |
|
751 |
|
752 TODO: have this method do a proper import |
|
753 |
|
754 Args: |
|
755 request: Django Requset object |
|
756 fields: ??? |
|
757 user: ??? |
|
758 |
|
759 Returns: |
|
760 Keywords, the survey's schema and the survey content. |
|
761 """ |
754 json = request.POST['serialized'] |
762 json = request.POST['serialized'] |
755 json = simplejson.loads(json)['data'] |
763 json = simplejson.loads(json)['data'] |
756 survey_content = json.pop('survey_content') |
764 survey_content = json.pop('survey_content') |
757 schema = survey_content.pop('schema') |
765 schema = survey_content.pop('schema') |
758 del json['author'] |
766 del json['author'] |
759 del json['created'] |
767 del json['created'] |
760 del json['modified'] |
768 del json['modified'] |
761 #del json['is_featured'] |
769 |
762 # keywords can't be unicode |
770 # keywords can't be unicode |
763 keywords = {} |
771 keywords = {} |
764 for key, val in json.items(): |
772 for key, val in json.items(): |
765 keywords[str(key)] = val |
773 keywords[str(key)] = val |
766 if 'is_featured' in keywords: |
774 if 'is_featured' in keywords: |
767 keywords['is_featured'] = eval(keywords['is_featured']) |
775 keywords['is_featured'] = eval(keywords['is_featured']) |
768 return keywords, schema, survey_content |
776 return keywords, schema, survey_content |
769 |
|
770 def getContextEntity(self, request, page_name, params, kwargs): |
|
771 context = responses.getUniversalContext(request) |
|
772 responses.useJavaScript(context, params['js_uses_all']) |
|
773 context['page_name'] = page_name |
|
774 entity = None |
|
775 |
|
776 # TODO(ajaksu) there has to be a better way in this universe to get these |
|
777 kwargs['prefix'] = 'program' |
|
778 kwargs['link_id'] = request.path.split('/')[-1] |
|
779 kwargs['scope_path'] = '/'.join(request.path.split('/')[4:-1]) |
|
780 |
|
781 entity = survey_logic.getFromKeyFieldsOr404(kwargs) |
|
782 |
|
783 if not self._public(request, entity, context): |
|
784 error = out_of_band.Error('') |
|
785 error = responses.errorResponse( |
|
786 error, request, template=params['error_public'], context=context) |
|
787 return error, None |
|
788 |
|
789 return entity, context |
|
790 |
777 |
791 def getMenusForScope(self, entity, params, id, user): |
778 def getMenusForScope(self, entity, params, id, user): |
792 """List featured surveys if after the survey_start date |
779 """List featured surveys if after the survey_start date |
793 and before survey_end an iff the current user has the right taking access. |
780 and before survey_end an iff the current user has the right taking access. |
794 |
781 |