Major update to the views concerning StudentProjects.
authorLennard de Rijk <ljvderijk@gmail.com>
Tue, 07 Apr 2009 08:49:47 +0000
changeset 2115 fafd021def7e
parent 2114 1b3830abcdf7
child 2116 68d7679a2af2
Major update to the views concerning StudentProjects. Organization admins can now reassign a Mentor for each of their active (meaning student has not failed or finished) projects. To facilitate this an overview has been added for the Organization Admins in which they can choose which Project to edit. Students can now edit the public information that is used in their projects. Several TODO's have been added to make it easier to navigate to these pages. Patch by: Lennard de Rijk Reviewed by: to-be-reviewed
app/soc/logic/cleaning.py
app/soc/views/models/student_project.py
--- a/app/soc/logic/cleaning.py	Tue Apr 07 08:44:56 2009 +0000
+++ b/app/soc/logic/cleaning.py	Tue Apr 07 08:49:47 2009 +0000
@@ -589,7 +589,7 @@
     return cleaned_data
   return wrapper
 
-def validate_new_student_project(org_field, mentor_field, student_field):
+def validate_student_project(org_field, mentor_field, student_field):
   """Validates the form of a student proposal.
 
   Args:
@@ -599,7 +599,7 @@
 
   Raises ValidationError if:
     -A valid Organization does not exist for the given keyname
-    -The mentor link_id does not match the mentors for the active organization
+    -The mentor link_id does not match a mentor for the active organization
     -The student link_id does not match a student in the org's Program
   """
 
--- a/app/soc/views/models/student_project.py	Tue Apr 07 08:44:56 2009 +0000
+++ b/app/soc/views/models/student_project.py	Tue Apr 07 08:49:47 2009 +0000
@@ -29,10 +29,18 @@
 from soc.logic import cleaning
 from soc.logic import dicts
 from soc.logic.models import mentor as mentor_logic
+from soc.logic.models.organization import logic as org_logic
+from soc.logic.models.org_admin import logic as org_admin_logic
 from soc.logic.models import student as student_logic
+from soc.logic.models.student_project import logic as project_logic
 from soc.views.helper import access
 from soc.views.helper import decorators
+from soc.views.helper import dynaform
+from soc.views.helper import forms as forms_helper
+from soc.views.helper import lists
+from soc.views.helper import params as params_helper
 from soc.views.helper import redirects
+from soc.views.helper import responses
 from soc.views.helper import widgets
 from soc.views.models import base
 from soc.views.models import organization as org_view
@@ -54,12 +62,20 @@
 
     rights = access.Checker(params)
     rights['create'] = ['checkIsDeveloper']
-    # TODO who should be able to edit this?
     rights['edit'] = ['checkIsDeveloper']
-    
     rights['delete'] = ['checkIsDeveloper']
     rights['show'] = ['allow']
     rights['list'] = ['checkIsDeveloper']
+    rights['manage'] = [('checkHasActiveRoleForScope',
+                         org_admin_logic),
+        ('checkStudentProjectHasStatus', ['accepted', 'mid_term_passed'])]
+    rights['manage_overview'] = [('checkHasActiveRoleForScope',
+                         org_admin_logic)]
+    # TODO lack of better name here!
+    rights['st_edit'] = ['checkIsMyStudentProject',
+        ('checkStudentProjectHasStatus', 
+            ['accepted', 'mid_term_passed', 'passed'])
+        ]
 
     new_params = {}
     new_params['logic'] = soc.logic.models.student_project.logic
@@ -83,28 +99,58 @@
             required=True),
         'mentor_id': forms.CharField(label='Mentor Link ID',
             required=True),
+        'clean_abstract': cleaning.clean_content_length('abstract'),
         'clean_student': cleaning.clean_link_id('student'),
         'clean_mentor': cleaning.clean_link_id('mentor'),
         'clean_additional_info': cleaning.clean_url('additional_info'),
-        'clean': cleaning.validate_new_student_project('scope_path',
+        'clean': cleaning.validate_student_project('scope_path',
             'mentor_id', 'student_id')
         }
 
     new_params['edit_extra_dynaproperties'] = {
-        'student_id': forms.CharField(label='Student Link ID',
-            widget=widgets.ReadOnlyInput),
-        'mentor_id': forms.CharField(label='Mentor Link ID',
-            widget=widgets.ReadOnlyInput),
         'link_id': forms.CharField(widget=forms.HiddenInput),
-        'clean': (lambda x: x.cleaned_data)
         }
 
-    # TODO(ljvderijk) OrgAdmins should be able to assign another Mentor
+    patterns = [
+        (r'^%(url_name)s/(?P<access_type>manage_overview)/%(scope)s$',
+        'soc.views.models.%(module_name)s.manage_overview',
+        'Overview of %(name_plural)s to Manage for'),
+        (r'^%(url_name)s/(?P<access_type>manage)/%(key_fields)s$',
+        'soc.views.models.%(module_name)s.manage',
+        'Manage %(name)s'),
+        (r'^%(url_name)s/(?P<access_type>st_edit)/%(key_fields)s$',
+        'soc.views.models.%(module_name)s.st_edit',
+        'Edit my %(name)s'),
+    ]
+
+    new_params['extra_django_patterns'] = patterns
+
+    new_params['edit_template'] = 'soc/student_project/edit.html'
+
+    # TODO(ljvderijk) list all projects for one org on their homepage
+    # TODO(ljvderijk) list all projects for one student
+    # TODO(ljvderijk) add manage item to org menu for admins
 
     params = dicts.merge(params, new_params)
 
     super(View, self).__init__(params=params)
 
+    # create the form that students will use to edit their projects
+    dynaproperties = {
+        'clean_abstract': cleaning.clean_content_length('abstract'),
+        'clean_additional_info': cleaning.clean_url('additional_info'),
+        }
+
+    student_edit_form = dynaform.newDynaForm(
+        dynabase = params['dynabase'],
+        dynamodel = params['logic'].getModel(),
+        dynaexclude = params['create_dynaexclude'],
+        dynaproperties = dynaproperties,
+    )
+
+    params['student_edit_form'] = student_edit_form
+
+
   def _editGet(self, request, entity, form):
     """See base.View._editGet().
     """
@@ -127,18 +173,246 @@
     # fill in the scope via call to super
     super(View, self)._editPost(request, entity, fields)
 
-    if not entity:
-      # creating a new project so set the program, student and mentor field
-      fields['program'] = fields['scope'].scope
+    # editing a project so set the program, student and mentor field
+    if entity:
+      organization = entity.scope
+    else:
+      organization = fields['scope']
+
+    fields['program'] = organization.scope
+
+    filter = {'scope': fields['program'],
+              'link_id': fields['student_id']}
+    fields['student'] = student_logic.logic.getForFields(filter, unique=True)
+
+    filter = {'scope': organization,
+              'link_id': fields['mentor_id'],
+              'status': 'active'}
+    fields['mentor'] = mentor_logic.logic.getForFields(filter, unique=True)
+
+  @decorators.merge_params
+  @decorators.check_access
+  def manage(self, request, access_type,
+             page_name=None, params=None, **kwargs):
+    """View that allows Organization Admins to manage their Student Projects.
+
+    For params see base.View().public()
+    """
+
+    try:
+      entity = self._logic.getFromKeyFieldsOr404(kwargs)
+    except out_of_band.Error, error:
+      return helper.responses.errorResponse(
+          error, request, template=params['error_public'])
+
+    # get the context for this webpage
+    context = responses.getUniversalContext(request)
+    responses.useJavaScript(context, params['js_uses_all'])
+    context['page_name'] = "%s '%s' from %s" %(page_name, entity.title,
+                                               entity.student.name())
+
+    # use another template and make the cancel button goes to the public view
+    params['edit_template'] = 'soc/student_project/manage.html'
+    params['edit_cancel_redirect'] = redirects.getManageOverviewRedirect(
+        entity.scope, params)
+
+    # get all mentors for this organization
+    fields = {'scope': entity.scope,
+              'status': 'active'}
+    mentors = mentor_logic.logic.getForFields(fields)
+
+    choices = [(mentor.link_id,'%s (%s)' %(mentor.name(), mentor.link_id))
+                  for mentor in mentors]
+
+    # create the form that org admins will use to reassign a mentor
+    dynafields = [
+        {'name': 'mentor_id',
+         'base': forms.ChoiceField,
+         'label': 'Mentor',
+         'required': True,
+         'passthrough': ['required', 'choices', 'label'],
+         'choices': choices,
+        },]
+
+    dynaproperties = params_helper.getDynaFields(dynafields)
+
+    mentor_edit_form = dynaform.newDynaForm(
+        dynabase = params['dynabase'],
+        dynaproperties = dynaproperties,
+    )
+
+    params['mentor_edit_form'] = mentor_edit_form
+
+    if request.POST:
+      return self.managePost(request, context, params, entity, **kwargs)
+    else: #request.GET
+      return self.manageGet(request, context, params, entity, **kwargs)
+
+  def manageGet(self, request, context, params, entity, **kwargs):
+    """Handles the GET request for the project's manage page.
+
+    Args:
+        entity: the student project entity
+        rest: see base.View.public()
+    """
+
+    # populate form with the current mentor
+    initial = {'mentor_id': entity.mentor.link_id}
+    form = params['mentor_edit_form'](initial=initial)
+
+    return self._constructResponse(request, entity, context, form, params)
+
+  def managePost(self, request, context, params, entity, **kwargs):
+    """Handles the POST request for the project's manage page.
+
+    Args:
+        entity: the student project entity
+        rest: see base.View.public()
+    """
+
+    form = params['mentor_edit_form'](request.POST)
+
+    if not form.is_valid():
+      return self._constructResponse(request, entity, context, form, params)
+
+    key_name, fields = forms_helper.collectCleanedFields(form)
+
+    # get the mentor from the form
+    fields = {'link_id': fields['mentor_id'],
+              'scope': entity.scope,
+              'status': 'active'}
+    mentor = mentor_logic.logic.getForFields(fields, unique=True)
+
+    # update the project with the assigned mentor
+    fields = {'mentor': mentor}
+    project_logic.updateEntityProperties(entity, fields)
+
+    return self.manageGet(request, context, params, entity)
 
-      filter = {'scope': fields['program'],
-                'link_id': fields['student_id']}
-      fields['student'] = student_logic.logic.getForFields(filter, unique=True)
+  @decorators.merge_params
+  @decorators.check_access
+  def manageOverview(self, request, access_type,
+             page_name=None, params=None, **kwargs):
+    """View that allows Organization Admins to see an overview of 
+       their Organization's Student Projects.
+
+    For params see base.View().public()
+    """
+
+    # make sure the organization exists
+    org_entity = org_logic.getFromKeyNameOr404(kwargs['scope_path'])
+    fields = {'scope': org_entity}
+
+    # get the context for this webpage
+    context = responses.getUniversalContext(request)
+    responses.useJavaScript(context, params['js_uses_all'])
+    context['page_name'] = '%s %s' %(page_name, org_entity.name)
+
+    list_params = params.copy()
+
+    #list all active projects
+    fields['status'] = ['accepted', 'mid_term_passed']
+    active_params = list_params.copy()
+    active_params['list_description'] = \
+        'List of all active %(name_plural)s' % list_params
+    active_params['list_action'] = (redirects.getManageRedirect, list_params)
+
+    active_list = lists.getListContent(
+        request, active_params, fields, idx=0)
+
+    # list all failed projects
+    fields['status'] = ['mid_term_failed', 'final_failed']
+    failed_params = list_params.copy()
+    failed_params['list_description'] = ('List of all failed %(name_plural)s, '
+        'these cannot be managed.') % list_params
+    failed_params['list_action'] = (redirects.getPublicRedirect, list_params)
+
+    failed_list = lists.getListContent(
+        request, failed_params, fields, idx=1, need_content=True)
+
+    #list all completed projects
+    fields['status'] = ['passed']
+    completed_params = list_params.copy()
+    completed_params['list_description'] = ('List of %(name_plural)s that have '
+        'successfully completed the program, these cannot be managed.' % list_params)
+    completed_params['list_action'] = (redirects.getPublicRedirect, list_params)
+
+    completed_list = lists.getListContent(
+        request, completed_params, fields, idx=2, need_content=True)
+
+    # always show the list with active projects
+    content = [active_list]
+
+    if failed_list != None:
+      # do not show empty failed list
+      content.append(failed_list)
+
+    if completed_list != None:
+      # do not show empty completed list
+      content.append(completed_list)
 
-      filter = {'scope': fields['scope'],
-                'link_id': fields['mentor_id'],
-                'status': 'active'}
-      fields['mentor'] = mentor_logic.logic.getForFields(filter, unique=True)
+    # call the _list method from base to display the list
+    return self._list(request, list_params, content,
+                      context['page_name'], context)
+
+  @decorators.merge_params
+  @decorators.check_access
+  def stEdit(self, request, access_type,
+             page_name=None, params=None, **kwargs):
+    """View that allows students to edit information about their project.
+
+    For params see base.View().public()
+    """
+
+    try:
+      entity = self._logic.getFromKeyFieldsOr404(kwargs)
+    except out_of_band.Error, error:
+      return helper.responses.errorResponse(
+          error, request, template=params['error_public'])
+
+    # get the context for this webpage
+    context = responses.getUniversalContext(request)
+    responses.useJavaScript(context, params['js_uses_all'])
+    context['page_name'] = page_name
+    # cancel should go to the public view
+    params['edit_cancel_redirect'] = redirects.getPublicRedirect(entity, params)
+
+    if request.POST:
+      return self.stEditPost(request, context, params, entity, **kwargs)
+    else: #request.GET
+      return self.stEditGet(request, context, params, entity, **kwargs)
+
+  def stEditGet(self, request, context, params, entity, **kwargs):
+    """Handles the GET request for the student's edit page.
+
+    Args:
+        entity: the student project entity
+        rest: see base.View.public()
+    """
+
+    # populate form with the existing entity
+    form = params['student_edit_form'](instance=entity)
+
+    return self._constructResponse(request, entity, context, form, params)
+
+  def stEditPost(self, request, context, params, entity, **kwargs):
+    """Handles the POST request for the student's edit page.
+
+    Args:
+        entity: the student project entity
+        rest: see base.View.public()
+    """
+
+    form = params['student_edit_form'](request.POST)
+
+    if not form.is_valid():
+      return self._constructResponse(request, entity, context, form, params)
+
+    key_name, fields = forms_helper.collectCleanedFields(form)
+
+    project_logic.updateEntityProperties(entity, fields)
+
+    return self.stEditGet(request, context, params, entity, **kwargs)
 
 
 view = View()
@@ -148,6 +422,9 @@
 delete = decorators.view(view.delete)
 edit = decorators.view(view.edit)
 list = decorators.view(view.list)
+manage = decorators.view(view.manage)
+manage_overview = decorators.view(view.manageOverview)
 public = decorators.view(view.public)
+st_edit = decorators.view(view.stEdit)
 export = decorators.view(view.export)
 pick = decorators.view(view.pick)