app/soc/views/helper/surveys.py
changeset 2501 d612b48e6e12
parent 2492 6eac6cd88dad
child 2502 2e096acc8720
equal deleted inserted replaced
2500:ecc16ffe174b 2501:d612b48e6e12
    40 from django.utils.html import escape
    40 from django.utils.html import escape
    41 from django.utils.safestring import mark_safe
    41 from django.utils.safestring import mark_safe
    42 
    42 
    43 from soc.logic import dicts
    43 from soc.logic import dicts
    44 from soc.logic.lists import Lists
    44 from soc.logic.lists import Lists
       
    45 from soc.models.survey import COMMENT_PREFIX
    45 from soc.models.survey import SurveyContent
    46 from soc.models.survey import SurveyContent
    46 
    47 
    47 
    48 
    48 class SurveyForm(djangoforms.ModelForm):
    49 class SurveyForm(djangoforms.ModelForm):
    49   """Main SurveyContent form.
    50   """Main SurveyContent form.
    81         selection=self.addSingleField,
    82         selection=self.addSingleField,
    82         pick_multi=self.addMultiField,
    83         pick_multi=self.addMultiField,
    83         pick_quant=self.addQuantField,
    84         pick_quant=self.addQuantField,
    84         )
    85         )
    85 
    86 
       
    87     self.kwargs['data'] = {}
    86     super(SurveyForm, self).__init__(*args, **self.kwargs)
    88     super(SurveyForm, self).__init__(*args, **self.kwargs)
    87 
    89 
    88   def getFields(self):
    90   def getFields(self, post_dict=None):
    89     """Build the SurveyContent (questions) form fields.
    91     """Build the SurveyContent (questions) form fields.
    90 
    92 
       
    93     params:
       
    94       post_dict: dict with POST data that will be used for validation
       
    95 
    91     Populates self.survey_fields, which will be ordered in self.insert_fields.
    96     Populates self.survey_fields, which will be ordered in self.insert_fields.
       
    97     Also populates self.data, which will be used in form validation.
    92     """
    98     """
    93 
    99 
    94     if not self.survey_content:
   100     if not self.survey_content:
    95       return
   101       return
    96 
   102 
       
   103     post_dict = post_dict or {}
    97     self.survey_fields = {}
   104     self.survey_fields = {}
    98     schema = SurveyContentSchema(self.survey_content.schema)
   105     schema = SurveyContentSchema(self.survey_content.schema)
    99     has_record = (not self.editing) and self.survey_record
   106     has_record = (not self.editing) and (self.survey_record or post_dict)
   100     extra_attrs = {}
   107     extra_attrs = {}
   101 
   108 
   102     # figure out whether we want a read-only view
   109     # figure out whether we want a read-only view
   103     if not self.editing:
   110     if not self.editing:
   104       # only survey taking can be read-only
   111       # only survey taking can be read-only
   115     # add unordered fields to self.survey_fields
   122     # add unordered fields to self.survey_fields
   116     for field in self.survey_content.dynamic_properties():
   123     for field in self.survey_content.dynamic_properties():
   117 
   124 
   118       # a comment made by the user
   125       # a comment made by the user
   119       comment = ''
   126       comment = ''
   120       if has_record and hasattr(self.survey_record, field):
   127 
       
   128       # flag to know where the value came from
       
   129       from_content = False
       
   130 
       
   131       if has_record and field in post_dict:
       
   132         # entered value that is not yet saved
       
   133         value = post_dict[field]
       
   134         if COMMENT_PREFIX + field in post_dict:
       
   135           comment = post_dict[COMMENT_PREFIX + field]
       
   136       elif has_record and hasattr(self.survey_record, field):
   121         # previously entered value
   137         # previously entered value
   122         value = getattr(self.survey_record, field)
   138         value = getattr(self.survey_record, field)
   123         if hasattr(self.survey_record, 'comment_for_' + field):
   139         if hasattr(self.survey_record, COMMENT_PREFIX + field):
   124           comment = getattr(self.survey_record, 'comment_for_' + field)
   140           comment = getattr(self.survey_record, COMMENT_PREFIX + field)
   125       else:
   141       else:
   126         # use prompts set by survey creator
   142         # use prompts set by survey creator
   127         value = getattr(self.survey_content, field)
   143         value = getattr(self.survey_content, field)
       
   144         from_content = True
   128 
   145 
   129       label = schema.getLabel(field)
   146       label = schema.getLabel(field)
   130       if label is None:
   147       if label is None:
   131         continue
   148         continue
   132 
   149 
   133       # dispatch to field-specific methods
   150       # fix validation for pick_multi fields
       
   151       is_multi = schema.getType(field) == 'pick_multi'
       
   152       if not from_content and schema.getType(field) == 'pick_multi':
       
   153         if isinstance(value, basestring):
       
   154           value = value.split(',')
       
   155       elif from_content and is_multi:
       
   156         value = []
       
   157 
       
   158       # record field value for validation
       
   159       if not from_content:
       
   160         self.data[field] = value
       
   161 
       
   162       # find correct field type
   134       addField = self.fields_map[schema.getType(field)]
   163       addField = self.fields_map[schema.getType(field)]
   135       addField(field, value, extra_attrs, schema, label=label, comment=comment)
   164       addField(field, value, extra_attrs, schema, label=label, comment=comment)
       
   165 
       
   166       # handle comments
       
   167       comment_name = COMMENT_PREFIX + field
       
   168       if comment_name in post_dict or hasattr(self.survey_record, comment_name):
       
   169         self.data[comment_name] = comment
       
   170         self.addCommentField(field, comment, extra_attrs, tip='Add a comment.')
   136 
   171 
   137     return self.insertFields()
   172     return self.insertFields()
   138 
   173 
   139   def insertFields(self):
   174   def insertFields(self):
   140     """Add ordered fields to self.fields.
   175     """Add ordered fields to self.fields.
   145     # first, insert dynamic survey fields
   180     # first, insert dynamic survey fields
   146     for position, property in survey_order.items():
   181     for position, property in survey_order.items():
   147       position = position * 2
   182       position = position * 2
   148       self.fields.insert(position, property, self.survey_fields[property])
   183       self.fields.insert(position, property, self.survey_fields[property])
   149       if not self.editing:
   184       if not self.editing:
   150         property = 'comment_for_' + property
   185         # add comment if field has one and this isn't an edit view
   151         self.fields.insert(position - 1, property,
   186         property = COMMENT_PREFIX + property
   152                            self.survey_fields[property])
   187         if property in self.survey_fields:
       
   188           self.fields.insert(position - 1, property,
       
   189                              self.survey_fields[property])
   153     return self.fields
   190     return self.fields
   154 
   191 
   155   def addLongField(self, field, value, attrs, schema, req=False, label='',
   192   def addLongField(self, field, value, attrs, schema, req=False, label='',
   156                    tip='', comment=''):
   193                    tip='', comment=''):
   157     """Add a long answer fields to this form.
   194     """Add a long answer fields to this form.
   174 
   211 
   175     question = CharField(help_text=tip, required=req, label=label,
   212     question = CharField(help_text=tip, required=req, label=label,
   176                          widget=widget, initial=value)
   213                          widget=widget, initial=value)
   177 
   214 
   178     self.survey_fields[field] = question
   215     self.survey_fields[field] = question
   179     self.addCommentField(field, comment, attrs, tip)
       
   180 
   216 
   181   def addShortField(self, field, value, attrs, schema, req=False, label='',
   217   def addShortField(self, field, value, attrs, schema, req=False, label='',
   182                     tip='', comment=''):
   218                     tip='', comment=''):
   183     """Add a short answer fields to this form.
   219     """Add a short answer fields to this form.
   184 
   220 
   201 
   237 
   202     question = CharField(help_text=tip, required=req, label=label,
   238     question = CharField(help_text=tip, required=req, label=label,
   203                          widget=widget, max_length=140, initial=value)
   239                          widget=widget, max_length=140, initial=value)
   204 
   240 
   205     self.survey_fields[field] = question
   241     self.survey_fields[field] = question
   206     self.addCommentField(field, comment, attrs, tip)
       
   207 
   242 
   208   def addSingleField(self, field, value, attrs, schema, req=False, label='',
   243   def addSingleField(self, field, value, attrs, schema, req=False, label='',
   209                      tip='', comment=''):
   244                      tip='', comment=''):
   210     """Add a selection field to this form.
   245     """Add a selection field to this form.
   211 
   246 
   238 
   273 
   239     question = PickOneField(help_text=tip, required=req, label=label,
   274     question = PickOneField(help_text=tip, required=req, label=label,
   240                             choices=tuple(these_choices), widget=widget)
   275                             choices=tuple(these_choices), widget=widget)
   241 
   276 
   242     self.survey_fields[field] = question
   277     self.survey_fields[field] = question
   243     self.addCommentField(field, comment, attrs, tip)
       
   244 
   278 
   245   def addMultiField(self, field, value, attrs, schema, req=False, label='',
   279   def addMultiField(self, field, value, attrs, schema, req=False, label='',
   246                     tip='', comment=''):
   280                     tip='', comment=''):
   247     """Add a pick_multi field to this form.
   281     """Add a pick_multi field to this form.
   248 
   282 
   274     question = PickManyField(help_text=tip, required=req, label=label,
   308     question = PickManyField(help_text=tip, required=req, label=label,
   275                              choices=tuple(these_choices), widget=widget,
   309                              choices=tuple(these_choices), widget=widget,
   276                              initial=value)
   310                              initial=value)
   277 
   311 
   278     self.survey_fields[field] = question
   312     self.survey_fields[field] = question
   279     self.addCommentField(field, comment, attrs, tip)
       
   280 
   313 
   281   def addQuantField(self, field, value, attrs, schema, req=False, label='',
   314   def addQuantField(self, field, value, attrs, schema, req=False, label='',
   282                     tip='', comment=''):
   315                     tip='', comment=''):
   283     """Add a pick_quant field to this form.
   316     """Add a pick_quant field to this form.
   284 
   317 
   307 
   340 
   308     question = PickQuantField(help_text=tip, required=req, label=label,
   341     question = PickQuantField(help_text=tip, required=req, label=label,
   309                              choices=tuple(these_choices), widget=widget,
   342                              choices=tuple(these_choices), widget=widget,
   310                              initial=value)
   343                              initial=value)
   311     self.survey_fields[field] = question
   344     self.survey_fields[field] = question
   312     self.addCommentField(field, comment, attrs, tip)
       
   313 
   345 
   314   def addCommentField(self, field, comment, attrs, tip):
   346   def addCommentField(self, field, comment, attrs, tip):
   315     if not self.editing:
   347     if not self.editing:
   316       widget = widgets.Textarea(attrs=attrs)
   348       widget = widgets.Textarea(attrs=attrs)
   317       comment = CharField(help_text=tip, required=False, label='Comments',
   349       comment_field = CharField(help_text=tip, required=False, label='Comments',
   318                           widget=widget, initial=comment)
   350                           widget=widget, initial=comment)
   319       self.survey_fields['comment_for_' + field] = comment
   351       self.survey_fields[COMMENT_PREFIX + field] = comment_field
   320 
   352 
   321   class Meta(object):
   353   class Meta(object):
   322     model = SurveyContent
   354     model = SurveyContent
   323     exclude = ['schema']
   355     exclude = ['schema']
   324 
   356 
   604   Args:
   636   Args:
   605       survey: a Survey entity
   637       survey: a Survey entity
   606       post_dict: dictionary with data from the POST request
   638       post_dict: dictionary with data from the POST request
   607   """
   639   """
   608 
   640 
   609   # TODO(ljvderijk) deal with the comment fields
   641   # TODO(ljvderijk) deal with the comment fields neatly
   610 
   642 
   611   # get the schema for this survey
   643   # get the schema for this survey
   612   schema = eval(survey.survey_content.schema)
   644   schema = SurveyContentSchema(survey.survey_content.schema)
       
   645   schema_dict = schema.schema
   613 
   646 
   614   # fill a dictionary with the data to be stored in the SurveyRecord
   647   # fill a dictionary with the data to be stored in the SurveyRecord
   615   response_dict = {}
   648   response_dict = {}
   616 
   649 
   617   for name, value in post_dict.items():
   650   for name, value in post_dict.items():
   618     # make sure name is a string
   651     # make sure name is a string
   619     name = name.encode()
   652     name = name.encode()
   620     if name not in schema:
   653 
       
   654     if name not in schema_dict:
   621       # property not in survey schema ignore
   655       # property not in survey schema ignore
   622       continue
   656       continue
   623     else:
   657     else:
   624       pick_multi = schema[name]['type'] == 'pick_multi'
   658       pick_multi = schema.getType(name) == 'pick_multi'
   625 
   659 
   626       if pick_multi and hasattr(post_dict, 'getlist'): # it's a multidict
   660       if pick_multi and hasattr(post_dict, 'getlist'): # it's a multidict
   627         response_dict[name] = ','.join(post_dict.getlist(name))
   661         # validation asks for a list of values
   628       else:
   662         value = post_dict.getlist(name)
   629         response_dict[name] = value
   663 
       
   664     response_dict[name] = value
       
   665 
       
   666     # handle comments
       
   667     comment_name = COMMENT_PREFIX + name
       
   668     if comment_name in post_dict:
       
   669       comment = post_dict.get(comment_name)
       
   670       if comment:
       
   671         response_dict[comment_name] = comment
   630 
   672 
   631   return response_dict
   673   return response_dict
   632 
   674 
   633 
   675 
   634 def getRoleSpecificFields(survey, user, this_project, survey_form,
   676 def getRoleSpecificFields(survey, user, this_project, survey_form,
   794     try:
   836     try:
   795       first = record_query.run().next()
   837       first = record_query.run().next()
   796     except StopIteration:
   838     except StopIteration:
   797       # bail out early if survey_records.run() is empty
   839       # bail out early if survey_records.run() is empty
   798       return header, survey.link_id
   840       return header, survey.link_id
   799   
   841 
   800     # generate results list
   842     # generate results list
   801     recs = record_query.run()
   843     recs = record_query.run()
   802     recs = _get_records(recs, properties)
   844     recs = _get_records(recs, properties)
   803   
   845 
   804     # write results to CSV
   846     # write results to CSV
   805     output = StringIO.StringIO()
   847     output = StringIO.StringIO()
   806     writer = csv.writer(output)
   848     writer = csv.writer(output)
   807     writer.writerow(properties)
   849     writer.writerow(properties)
   808     writer.writerows(recs)
   850     writer.writerows(recs)
   809   
   851 
   810     return header + output.getvalue(), survey.link_id
   852     return header + output.getvalue(), survey.link_id
   811   return wrapper
   853   return wrapper