app/soc/views/models/survey.py
changeset 2658 34b414a80d42
parent 2593 ef703b456527
child 2659 8df08a3b17db
equal deleted inserted replaced
2657:a93c6b2acf9b 2658:34b414a80d42
    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