21 '"Daniel Diniz" <ajaksu@gmail.com>', |
21 '"Daniel Diniz" <ajaksu@gmail.com>', |
22 '"James Levy" <jamesalexanderlevy@gmail.com>', |
22 '"James Levy" <jamesalexanderlevy@gmail.com>', |
23 '"Lennard de Rijk" <ljvderijk@gmail.com>', |
23 '"Lennard de Rijk" <ljvderijk@gmail.com>', |
24 ] |
24 ] |
25 |
25 |
26 import csv |
|
27 import datetime |
26 import datetime |
28 import re |
27 import re |
29 import StringIO |
|
30 import string |
28 import string |
31 |
29 |
32 from google.appengine.ext import db |
30 from google.appengine.ext import db |
33 |
31 |
34 from django import forms |
32 from django import forms |
35 from django import http |
33 from django import http |
36 from django.utils import simplejson |
34 from django.utils import simplejson |
37 |
35 |
38 from soc.logic import cleaning |
36 from soc.logic import cleaning |
39 from soc.logic import dicts |
37 from soc.logic import dicts |
40 from soc.logic.models.survey import GRADES |
|
41 from soc.logic.models.survey import logic as survey_logic |
38 from soc.logic.models.survey import logic as survey_logic |
42 from soc.logic.models.user import logic as user_logic |
39 from soc.logic.models.user import logic as user_logic |
43 from soc.models.survey import Survey |
40 from soc.models.survey import Survey |
44 from soc.models.survey_record import SurveyRecord |
41 from soc.models.survey_record import SurveyRecord |
45 from soc.models.user import User |
42 from soc.models.user import User |
127 'View survey results for user'), |
124 'View survey results for user'), |
128 ] |
125 ] |
129 |
126 |
130 new_params['export_content_type'] = 'text/text' |
127 new_params['export_content_type'] = 'text/text' |
131 new_params['export_extension'] = '.csv' |
128 new_params['export_extension'] = '.csv' |
132 new_params['export_function'] = to_csv |
129 new_params['export_function'] = surveys.to_csv |
133 new_params['delete_redirect'] = '/' |
130 new_params['delete_redirect'] = '/' |
134 new_params['list_key_order'] = [ |
131 new_params['list_key_order'] = [ |
135 'link_id', 'scope_path', 'name', 'short_name', 'title', |
132 'link_id', 'scope_path', 'name', 'short_name', 'title', |
136 'content', 'prefix','read_access','write_access'] |
133 'content', 'prefix','read_access','write_access'] |
137 |
134 |
138 new_params['edit_template'] = 'soc/survey/edit.html' |
135 new_params['edit_template'] = 'soc/survey/edit.html' |
139 new_params['create_template'] = 'soc/survey/edit.html' |
136 new_params['create_template'] = 'soc/survey/edit.html' |
|
137 new_params['public_template'] = 'soc/survey/public.html' |
140 new_params['take_template'] = 'soc/survey/take.html' |
138 new_params['take_template'] = 'soc/survey/take.html' |
141 |
139 |
142 # TODO which one of these are leftovers from Document? |
140 # TODO which one of these are leftovers from Document? |
143 new_params['no_create_raw'] = True |
141 new_params['no_create_raw'] = True |
144 new_params['no_create_with_scope'] = True |
142 new_params['no_create_with_scope'] = True |
438 """Process GET requests for the specified entity. |
437 """Process GET requests for the specified entity. |
439 |
438 |
440 Builds the SurveyForm that represents the Survey question contents. |
439 Builds the SurveyForm that represents the Survey question contents. |
441 """ |
440 """ |
442 |
441 |
443 # TODO(ajaksu) Move CHOOSE_A_PROJECT_FIELD and CHOOSE_A_GRADE_FIELD |
|
444 # to template. |
|
445 |
|
446 CHOOSE_A_PROJECT_FIELD = """<tr class="role-specific"> |
|
447 <th><label>Choose Project:</label></th> |
|
448 <td> |
|
449 <select disabled="TRUE" id="id_survey__NA__selection__project" |
|
450 name="survey__1__selection__see"> |
|
451 <option>Survey Taker's Projects For This Program</option></select> |
|
452 </td></tr> |
|
453 """ |
|
454 |
|
455 CHOOSE_A_GRADE_FIELD = """<tr class="role-specific"> |
|
456 <th><label>Assign Grade:</label></th> |
|
457 <td> |
|
458 <select disabled=TRUE id="id_survey__NA__selection__grade" |
|
459 name="survey__1__selection__see"> |
|
460 <option>Pass/Fail</option> |
|
461 </select></td></tr> |
|
462 """ |
|
463 |
|
464 self._entity = entity |
442 self._entity = entity |
465 survey_content = entity.survey_content |
443 survey_content = entity.survey_content |
466 user = user_logic.getForCurrentAccount() |
444 user = user_logic.getForCurrentAccount() |
467 # no project or survey_record needed for survey prototype |
445 # no project or survey_record needed for survey prototype |
468 project = None |
446 project = None |
469 survey_record = None |
447 survey_record = None |
470 |
448 |
471 |
|
472 survey_form = surveys.SurveyForm(survey_content=survey_content, |
449 survey_form = surveys.SurveyForm(survey_content=survey_content, |
473 this_user=user, project=project, |
450 this_user=user, project=project, |
474 survey_logic=params['logic'], |
451 survey_logic=params['logic'], |
475 survey_record=survey_record, |
452 survey_record=survey_record, |
476 editing=True, read_only=False) |
453 editing=True, read_only=False) |
477 survey_form.getFields() |
454 survey_form.getFields() |
478 |
455 |
479 |
|
480 # activate grades flag -- TODO: Can't configure notice on edit page |
|
481 if request._get.get('activate'): |
|
482 context['notice'] = "Evaluation Grades Have Been Activated" |
|
483 |
|
484 local = dict(survey_form=survey_form, question_types=QUESTION_TYPES, |
456 local = dict(survey_form=survey_form, question_types=QUESTION_TYPES, |
485 survey_h=entity.survey_content) |
457 survey_h=entity.survey_content) |
486 context.update(local) |
458 context.update(local) |
487 |
459 |
488 params['edit_form'] = HelperForm(params['edit_form']) |
460 params['edit_form'] = surveys.HelperForm(params['edit_form']) |
489 if entity.survey_end and datetime.datetime.now() > entity.survey_end: |
461 if entity.survey_end and datetime.datetime.now() > entity.survey_end: |
490 # are we already passed the survey_end? |
462 # are we already passed the survey_end? |
491 context["passed_survey_end"] = True |
463 context["passed_survey_end"] = True |
492 |
464 |
493 return super(View, self).editGet(request, entity, context, params=params) |
465 return super(View, self).editGet(request, entity, context, params=params) |
735 error, request, template=params['error_public'], context=context) |
707 error, request, template=params['error_public'], context=context) |
736 return error, None |
708 return error, None |
737 |
709 |
738 return entity, context |
710 return entity, context |
739 |
711 |
740 def getMenusForScope(self, entity, params): |
|
741 """List featured surveys if after the survey_start date and before survey_end. |
|
742 """ |
|
743 |
|
744 # only list surveys for registered users |
|
745 user = user_logic.getForCurrentAccount() |
|
746 if not user: |
|
747 return [] |
|
748 |
|
749 filter = { |
|
750 'prefix' : params['url_name'], |
|
751 'scope_path': entity.key().id_or_name(), |
|
752 'is_featured': True, |
|
753 } |
|
754 |
|
755 entities = self._logic.getForFields(filter) |
|
756 submenus = [] |
|
757 now = datetime.datetime.now() |
|
758 |
|
759 # cache ACL |
|
760 survey_rights = {} |
|
761 |
|
762 # add a link to all featured documents |
|
763 for entity in entities: |
|
764 |
|
765 # only list those surveys the user can read |
|
766 if entity.read_access not in survey_rights: |
|
767 |
|
768 params = dict(prefix=entity.prefix, scope_path=entity.scope_path, |
|
769 link_id=entity.link_id, user=user) |
|
770 |
|
771 # TODO(ajaksu) use access.Checker.checkIsSurveyReadable |
|
772 checker = access.rights_logic.Checker(entity.prefix) |
|
773 roles = checker.getMembership(entity.read_access) |
|
774 rights = self._params['rights'] |
|
775 can_read = access.Checker.hasMembership(rights, roles, params) |
|
776 |
|
777 # cache ACL for a given entity.read_access |
|
778 survey_rights[entity.read_access] = can_read |
|
779 |
|
780 if not can_read: |
|
781 pass#continue |
|
782 |
|
783 elif not survey_rights[entity.read_access]: |
|
784 pass#continue |
|
785 |
|
786 # omit if either before survey_start or after survey_end |
|
787 if entity.survey_start and entity.survey_start > now: |
|
788 pass#continue |
|
789 |
|
790 if entity.survey_end and entity.survey_end < now: |
|
791 pass#continue |
|
792 |
|
793 taken_status = "" |
|
794 taken_status = "(new)" |
|
795 #TODO only if a document is readable it might be added |
|
796 submenu = (redirects.getPublicRedirect(entity, self._params), |
|
797 'Survey ' + taken_status + ': ' + entity.short_name, |
|
798 'show') |
|
799 |
|
800 submenus.append(submenu) |
|
801 return submenus |
|
802 |
|
803 class HelperForm(object): |
|
804 """Thin wrapper for adding values to params['edit_form'].fields. |
|
805 """ |
|
806 |
|
807 def __init__(self, form=None): |
|
808 """Store the edit_form. |
|
809 """ |
|
810 |
|
811 self.form = form |
|
812 |
|
813 def __call__(self, instance=None): |
|
814 """Transparently instantiate and add initial values to the edit_form. |
|
815 """ |
|
816 |
|
817 form = self.form(instance=instance) |
|
818 form.fields['created_by'].initial = instance.author.name |
|
819 form.fields['last_modified_by'].initial = instance.modified_by.name |
|
820 form.fields['doc_key_name'].initial = instance.key().id_or_name() |
|
821 return form |
|
822 |
|
823 |
|
824 def _get_csv_header(sur): |
|
825 """CSV header helper, needs support for comment lines in CSV. |
|
826 """ |
|
827 |
|
828 tpl = '# %s: %s\n' |
|
829 |
|
830 # add static properties |
|
831 fields = ['# Melange Survey export for \n# %s\n#\n' % sur.title] |
|
832 fields += [tpl % (k,v) for k,v in sur.toDict().items()] |
|
833 fields += [tpl % (f, str(getattr(sur, f))) for f in PLAIN.split()] |
|
834 fields += [tpl % (f, str(getattr(sur, f).link_id)) for f in FIELDS.split()] |
|
835 fields.sort() |
|
836 |
|
837 # add dynamic properties |
|
838 fields += ['#\n#---\n#\n'] |
|
839 dynamic = sur.survey_content.dynamic_properties() |
|
840 dynamic = [(prop, getattr(sur.survey_content, prop)) for prop in dynamic] |
|
841 fields += [tpl % (k,v) for k,v in sorted(dynamic)] |
|
842 |
|
843 # add schema |
|
844 fields += ['#\n#---\n#\n'] |
|
845 schema = sur.survey_content.schema |
|
846 indent = '},\n#' + ' ' * 9 |
|
847 fields += [tpl % ('Schema', schema.replace('},', indent)) + '#\n'] |
|
848 |
|
849 return ''.join(fields).replace('\n', '\r\n') |
|
850 |
|
851 |
|
852 def _get_records(recs, props): |
|
853 """Fetch properties from SurveyRecords for CSV export. |
|
854 """ |
|
855 |
|
856 records = [] |
|
857 props = props[1:] |
|
858 for rec in recs: |
|
859 values = tuple(getattr(rec, prop, None) for prop in props) |
|
860 leading = (rec.user.link_id,) |
|
861 records.append(leading + values) |
|
862 return records |
|
863 |
|
864 |
|
865 def to_csv(survey): |
|
866 """CSV exporter. |
|
867 """ |
|
868 |
|
869 # get header and properties |
|
870 header = _get_csv_header(survey) |
|
871 leading = ['user', 'created', 'modified'] |
|
872 properties = leading + survey.survey_content.orderedProperties() |
|
873 |
|
874 try: |
|
875 first = survey.survey_records.run().next() |
|
876 except StopIteration: |
|
877 # bail out early if survey_records.run() is empty |
|
878 return header, survey.link_id |
|
879 |
|
880 # generate results list |
|
881 recs = survey.survey_records.run() |
|
882 recs = _get_records(recs, properties) |
|
883 |
|
884 # write results to CSV |
|
885 output = StringIO.StringIO() |
|
886 writer = csv.writer(output) |
|
887 writer.writerow(properties) |
|
888 writer.writerows(recs) |
|
889 |
|
890 return header + output.getvalue(), survey.link_id |
|
891 |
|
892 |
712 |
893 view = View() |
713 view = View() |
894 |
714 |
895 admin = decorators.view(view.admin) |
715 admin = decorators.view(view.admin) |
896 create = decorators.view(view.create) |
716 create = decorators.view(view.create) |