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 |