Start on adding ProjectSurvey and GradingProjectSurvey.
New classes still need to be made for logic and views so the appropriate methods can be overwritten.
Reviewed by: Lennard de Rijk
--- a/app/soc/logic/models/survey.py Sun Jun 28 22:12:33 2009 +0200
+++ b/app/soc/logic/models/survey.py Sun Jun 28 23:58:03 2009 +0200
@@ -35,6 +35,8 @@
from soc.models.program import Program
from soc.models import student_project
from soc.models.survey import Survey
+from soc.models.survey import ProjectSurvey
+from soc.models.survey import GradingProjectSurvey
from soc.models.survey import SurveyContent
from soc.models.survey_record import SurveyRecord
from soc.models.survey_record_group import SurveyRecordGroup
@@ -199,8 +201,6 @@
user.key().name(), str(students)) )
return False
- this_student = students[0]
-
def getStudentforProject(self, user, project):
"""Get student projects for a given User.
@@ -256,7 +256,7 @@
% (survey.key().name(), survey.taking_access))
return False
- program = survey.scope
+ program = survey.scope or Program.get_by_key_name(survey.scope_path)
for project in program.student_projects.fetch(1000):
this_record_group = SurveyRecordGroup.all().filter(
@@ -289,7 +289,7 @@
project.key().name(), this_record_group.final_status ) )
continue
- # assign the new status to the project and s
+ # assign the new status to the project and surveyrecordgroup
project.status = new_project_status
this_record_group.final_status = new_project_status
@@ -313,12 +313,14 @@
survey = survey entity
user = survey taking user
"""
- this_program = survey.scope
+
+ this_program = survey.scope or Program.get_by_key_name(survey.scope_path)
+
if 'mentor' in survey.taking_access:
these_projects = self.getMentorProjects(user, this_program)
- if 'student' in survey.taking_access:
+ elif 'student' in survey.taking_access:
these_projects = self.getStudentProjects(user, this_program)
logging.info(these_projects)
@@ -403,4 +405,8 @@
# return the scope
return entity.scope
+
logic = Logic()
+# TODO separate project and grading logic into own class to overwrite methods
+project_logic = Logic(model=ProjectSurvey)
+grading_logic = Logic(model=GradingProjectSurvey)
--- a/app/soc/logic/models/survey_record.py Sun Jun 28 22:12:33 2009 +0200
+++ b/app/soc/logic/models/survey_record.py Sun Jun 28 23:58:03 2009 +0200
@@ -22,8 +22,12 @@
]
+from google.appengine.ext import db
+
from soc.logic.models import work
from soc.models.survey_record import SurveyRecord
+from soc.models.survey_record import ProjectSurveyRecord
+from soc.models.survey_record import GradingProjectSurveyRecord
class Logic(work.Logic):
@@ -38,5 +42,69 @@
super(Logic, self).__init__(model=model, base_model=base_model,
scope_logic=scope_logic)
+ def updateSurveyRecord(self, user, survey, survey_record, fields):
+ """ Create a new survey record, or get an existing one.
+
+ params:
+ user = user taking survey
+ survey = survey entity
+ survey_record = existing record, if one exists
+ fields = submitted responses to survey fields
+ """
+
+ if survey_record:
+ create = False
+ for prop in survey_record.dynamic_properties():
+ delattr(survey_record, prop)
+ else:
+ create = True
+ Record = self.getModel()
+ survey_record = Record(user=user, survey=survey)
+
+ schema = eval(survey.survey_content.schema)
+
+ for name, value in fields.items():
+ # TODO(ajaksu) logic below can be improved now we have different models
+ if name == 'project':
+ project = student_project.StudentProject.get(value)
+ survey_record.project = project
+ elif name == 'grade':
+ survey_record.grade = GRADES[value]
+ else:
+ pick_multi = name in schema and schema[name]['type'] == 'pick_multi'
+ if pick_multi and hasattr(fields, 'getlist'): # it's a multidict
+ setattr(survey_record, name, ','.join(fields.getlist(name)))
+ else:
+ setattr(survey_record, name, value)
+
+ # if creating evaluation record, set SurveyRecordGroup
+ db.put(survey_record)
+ return survey_record
+
logic = Logic()
+# TODO separate project and grading logic into own class to overwrite methods
+project_logic = Logic(model=ProjectSurveyRecord)
+grading_logic = Logic(model=GradingProjectSurveyRecord)
+
+
+def updateSurveyRecord(user, survey, survey_record, fields):
+ """Create a new survey record, or get an existing one.
+
+ params:
+ user = user taking survey
+ survey = survey entity
+ survey_record = existing record, if one exists
+ fields = submitted responses to survey fields
+ """
+
+ # TODO(ajaksu) We should use class information here, but being careful about
+ # compatibility with existent records should the class change.
+ if hasattr(survey_record, 'grade'):
+ record_logic = grading_logic
+ elif hasattr(survey_record, 'project'):
+ record_logic = grading_logic
+ else:
+ record_logic = logic
+
+ return record_logic.updateSurveyRecord(user, survey, survey_record, fields)
--- a/app/soc/models/survey.py Sun Jun 28 22:12:33 2009 +0200
+++ b/app/soc/models/survey.py Sun Jun 28 23:58:03 2009 +0200
@@ -32,6 +32,7 @@
from django.utils.translation import ugettext
import soc.models.work
+from soc.models.program import Program
class SurveyContent(db.Expando):
@@ -95,14 +96,13 @@
# these are GSoC specific, so eventually we can subclass this
SURVEY_TAKING_ACCESS = ['student',
'mentor',
- 'student evaluation',
- 'mentor evaluation',
'org_admin',
'user',
'public']
GRADE_OPTIONS = {'midterm':['mid_term_passed', 'mid_term_failed'],
'final':['final_passed', 'final_failed'],
'N/A':[] }
+
prefix = db.StringProperty(default='program', required=True,
choices=['site', 'club', 'sponsor', 'program', 'org', 'user'],
verbose_name=ugettext('Prefix'))
@@ -156,3 +156,42 @@
#: Referenceproperty that specifies the content of this survey.
survey_content = db.ReferenceProperty(SurveyContent,
collection_name="survey_parent")
+
+ #: Survey kind, a helper for handling the different classes on creation.
+ survey_kind = db.StringProperty(default='', required=False,
+ choices=('', 'project', 'grading'))
+
+ def getRecords(self):
+ """Returns all SurveyRecords belonging to this survey.
+ """
+ return self.survey_records
+
+
+class ProjectSurvey(Survey):
+ """Survey for Students that have a StudentProject.
+ """
+
+ def __init__(self, *args, **kwargs):
+ super(ProjectSurvey, self).__init__(*args, **kwargs)
+ self.prefix = 'program'
+ self.taking_access = 'student'
+ self.scope = Program.get_by_key_name(self.scope_path)
+
+ def getRecords(self):
+ """Returns all ProjectSurveyRecords belonging to this survey.
+ """
+ return self.project_survey_records
+
+
+class GradingProjectSurvey(ProjectSurvey):
+ """Survey for Mentors that have a StudentProject.
+ """
+
+ def __init__(self, *args, **kwargs):
+ super(GradingProjectSurvey, self).__init__(*args, **kwargs)
+ self.taking_access = 'mentor'
+
+ def getRecords(self):
+ """Returns all GradingProjectSurveyRecords belonging to this survey.
+ """
+ return self.grading_survey_records
--- a/app/soc/models/survey_record.py Sun Jun 28 22:12:33 2009 +0200
+++ b/app/soc/models/survey_record.py Sun Jun 28 23:58:03 2009 +0200
@@ -16,8 +16,9 @@
"""SurveyRecord represents a single Survey result.
-SurveyRecordGroup represents a cluster (mentor/student) of SurveyRecords
-for an evaluation period.
+ProjectSurveyRecord allows linking two result sets by StudentProject.
+
+GradingProjectSurveyRecord stores the grade in an evaluation survey.
"""
__authors__ = [
@@ -32,37 +33,24 @@
from django.utils.translation import ugettext
from soc.models.survey import Survey
+from soc.models.survey import GradingProjectSurvey
+from soc.models.survey import ProjectSurvey
import soc.models.student_project
import soc.models.user
-class SurveyRecord(db.Expando):
+class BaseSurveyRecord(db.Expando):
"""Record produced each time Survey is taken.
Like SurveyContent, this model includes dynamic properties
corresponding to the fields of the survey.
-
- This also contains a Binary grade value that can be added/edited
- by the administrator of the Survey.
"""
- #: The survey for which this entity is a record.
- survey = db.ReferenceProperty(Survey, collection_name="survey_records")
-
#: Reference to the User entity which took this survey.
user = db.ReferenceProperty(reference_class=soc.models.user.User,
required=True, collection_name="surveys_taken",
verbose_name=ugettext('Created by'))
- #: Reference to the Project that this record belongs to.
- # TODO should be moved to its own subclass
- project = db.ReferenceProperty(soc.models.student_project.StudentProject,
- collection_name="survey_records")
-
- #: Grade given to the project that this survey is about.
- # TODO should be moved to its own subclass
- grade = db.BooleanProperty(required=False)
-
#: Date when this record was created.
created = db.DateTimeProperty(auto_now_add=True)
@@ -75,8 +63,59 @@
Right now it gets all dynamic values, but it could also be confined to
the SurveyContent entity linked to the survey entity.
"""
- survey_order = self.survey.survey_content.getSurveyOrder()
+ survey_order = self.getSurvey().survey_content.getSurveyOrder()
values = []
for position, property in survey_order.items():
values.insert(position, getattr(self, property, None))
return values
+
+
+# TODO(ajaksu) think of a better way to handle the survey reference
+class SurveyRecord(BaseSurveyRecord):
+
+ #: The survey for which this entity is a record.
+ survey = db.ReferenceProperty(Survey, collection_name="survey_records")
+
+ def getSurvey(self):
+ """Returns the Survey belonging to this record.
+ """
+ return self.survey
+
+class ProjectSurveyRecord(SurveyRecord):
+ """Record linked to a Project, enabling to store which Projects had their
+ Survey done.
+ """
+
+ #: The survey for which this entity is a record.
+ project_survey = db.ReferenceProperty(ProjectSurvey,
+ collection_name="project_survey_records")
+
+ #: Reference to the Project that this record belongs to.
+ project = db.ReferenceProperty(soc.models.student_project.StudentProject,
+ collection_name="survey_records")
+
+ def getSurvey(self):
+ """Returns the ProjectSurvey that belongs to this record.
+ """
+ return self.project_survey
+
+
+class GradingProjectSurveyRecord(ProjectSurveyRecord):
+ """Grading record for evaluation surveys.
+
+ Represents the grading part of a evaluation survey group (usually a pair)
+ where the grading (e.g. Mentor's) survey is linked to a non-grading (e.g
+ Student's) one by a project.
+ """
+
+ #: The survey for which this entity is a record.
+ grading_survey = db.ReferenceProperty(GradingProjectSurvey,
+ collection_name="grading_survey_records")
+
+ #: Required grade given to the project that this survey is about.
+ grade = db.BooleanProperty(required=True)
+
+ def getSurvey(self):
+ """Returns the GradingProjectSurvey that belongs to this record.
+ """
+ return self.grading_survey
--- a/app/soc/models/survey_record_group.py Sun Jun 28 22:12:33 2009 +0200
+++ b/app/soc/models/survey_record_group.py Sun Jun 28 23:58:03 2009 +0200
@@ -26,12 +26,15 @@
from google.appengine.ext import db
-from soc.models.survey_record import SurveyRecord
+from soc.models.survey_record import GradingProjectSurveyRecord
+from soc.models.survey_record import ProjectSurveyRecord
import soc.models.user
class SurveyRecordGroup(db.Expando):
- """Because Mentors and Students take different surveys,
+ """Explicitly group SurveyRecords with a common project.
+
+ Because Mentors and Students take different surveys,
we cannot simply link survey records by a common project and survey.
Instead, we establish a SurveyRecordGroup.
@@ -44,12 +47,15 @@
against unpredictable behavior.
"""
+ # TODO Create SurveyGroup model that contains the two Surveys as to make
+ # it possible to setup which surveys should be grouped.
+
#: Mentor SurveyRecord for this evaluation.
- mentor_record = db.ReferenceProperty(SurveyRecord, required=False,
+ mentor_record = db.ReferenceProperty(GradingProjectSurveyRecord, required=False,
collection_name='mentor_record_groups')
#: Student SurveyRecord for this evaluation.
- student_record = db.ReferenceProperty(SurveyRecord, required=False,
+ student_record = db.ReferenceProperty(ProjectSurveyRecord, required=False,
collection_name='student_record_groups')
#: Project for this evaluation.
--- a/app/soc/views/helper/surveys.py Sun Jun 28 22:12:33 2009 +0200
+++ b/app/soc/views/helper/surveys.py Sun Jun 28 23:58:03 2009 +0200
@@ -26,6 +26,7 @@
from itertools import chain
import datetime
+import logging
from google.appengine.ext.db import djangoforms
@@ -95,7 +96,7 @@
return
self.survey_fields = {}
- schema = eval(self.survey_content.schema)
+ schema = SurveyContentSchema(self.survey_content.schema)
has_record = (not self.editing) and self.survey_record
extra_attrs = {}
@@ -105,7 +106,7 @@
read_only = self.read_only
if not read_only:
- deadline = self.survey_content.survey_parent.get().deadline
+ deadline = self.survey_content.survey_parent.get().survey_end
read_only = deadline and (datetime.datetime.now() > deadline)
else:
extra_attrs['disabled'] = 'disabled'
--- a/app/soc/views/models/survey.py Sun Jun 28 22:12:33 2009 +0200
+++ b/app/soc/views/models/survey.py Sun Jun 28 23:58:03 2009 +0200
@@ -29,18 +29,21 @@
import StringIO
import string
+from google.appengine.ext import db
+
from django import forms
from django import http
from django.utils import simplejson
-from google.appengine.ext import db
-
from soc.cache import home
from soc.logic import cleaning
from soc.logic import dicts
from soc.logic.models.survey import GRADES
from soc.logic.models.survey import logic as survey_logic
+from soc.logic.models.survey import project_logic
+from soc.logic.models.survey import grading_logic
from soc.logic.models.survey_record import logic as results_logic
+from soc.logic.models.survey_record import updateSurveyRecord
from soc.logic.models.user import logic as user_logic
from soc.models.survey import Survey
from soc.models.survey_record import SurveyRecord
@@ -108,6 +111,7 @@
rights['grade'] = ['checkIsSurveyGradable']
new_params = {}
+ # TODO(ajaksu) pass logic in a way views can use them
new_params['logic'] = survey_logic
new_params['rights'] = rights
@@ -268,8 +272,8 @@
else:
# save/update the submitted survey
context['notice'] = "Survey Submission Saved"
- survey_record = survey_logic.updateSurveyRecord(user, survey,
- survey_record, request.POST)
+ survey_record = updateSurveyRecord(user, survey, survey_record,
+ request.POST)
survey_content = survey.survey_content
if not survey_record and read_only:
@@ -392,6 +396,19 @@
super(View, self)._editContext(request, context)
+ def createPost(self, request, context, params):
+
+ # TODO(ajaksu) create new View class for other surveys
+ survey_type = request.POST.get('survey_type')
+ if not survey_type:
+ self._logic = params['logic'] = survey_logic
+ elif survey_type == 'project':
+ self._logic = params['logic'] = project_logic
+ elif survey_type == 'grading':
+ self._logic = params['logic'] = grading_logic
+
+ return super(View, self).createPost(request, context, params)
+
def _editPost(self, request, entity, fields):
"""See base.View._editPost().
@@ -908,13 +925,13 @@
properties = leading + survey.survey_content.orderedProperties()
try:
- first = survey.survey_records.run().next()
+ first = survey.getRecords().run().next()
except StopIteration:
# bail out early if survey_records.run() is empty
return header, survey.link_id
# generate results list
- recs = survey.survey_records.run()
+ recs = survey.getRecords().run()
recs = _get_records(recs, properties)
# write results to CSV