app/soc/views/models/survey.py
changeset 2478 985fd974e095
parent 2477 e5629995d118
child 2484 f41a9e1eeeea
equal deleted inserted replaced
2477:e5629995d118 2478:985fd974e095
    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
   251     survey_fields = {}
   249     survey_fields = {}
   252 
   250 
   253     if not entity:
   251     if not entity:
   254       # new Survey
   252       # new Survey
   255       if 'serialized' in request.POST:
   253       if 'serialized' in request.POST:
   256         fields, schema, survey_fields = self.importSerialized(request, fields, user)
   254         fields, schema, survey_fields = self.importSerialized(request, fields,
       
   255                                                               user)
   257       fields['author'] = user
   256       fields['author'] = user
   258     else:
   257     else:
   259       fields['author'] = entity.author
   258       fields['author'] = entity.author
   260       schema = self.loadSurveyContent(schema, survey_fields, entity)
   259       schema = self.loadSurveyContent(schema, survey_fields, entity)
   261 
   260 
   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)