app/soc/modules/ghop/views/models/program.py
changeset 2891 aba681d72b0a
child 2935 0b8b82b6764e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/modules/ghop/views/models/program.py	Tue Sep 08 21:22:23 2009 +0200
@@ -0,0 +1,605 @@
+#!/usr/bin/python2.5
+#
+# Copyright 2009 the Melange authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""GHOP specific views for Programs.
+"""
+
+__authors__ = [
+    '"Madhusudan.C.S" <madhusudancs@gmail.com>',
+    '"Lennard de Rijk" <ljvderijk@gmail.com>',
+  ]
+
+
+import datetime
+import os
+
+from django import forms
+from django import http
+from django.utils.translation import ugettext
+
+from soc.logic import cleaning
+from soc.logic import dicts
+from soc.logic.helper import timeline as timeline_helper
+from soc.logic.models import host as host_logic
+from soc.views import out_of_band
+from soc.views import helper
+from soc.views.helper import decorators
+from soc.views.helper import dynaform
+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 widgets
+from soc.views.models import document as document_view
+from soc.views.models import program 
+from soc.views.sitemap import sidebar
+
+import soc.cache.logic
+
+from soc.modules.ghop.logic.models import mentor as ghop_mentor_logic
+from soc.modules.ghop.logic.models import org_admin as ghop_org_admin_logic
+from soc.modules.ghop.logic.models import program as ghop_program_logic
+from soc.modules.ghop.logic.models import student as ghop_student_logic
+from soc.modules.ghop.logic.models import task as ghop_task_logic
+from soc.modules.ghop.models import task as ghop_task_model
+from soc.modules.ghop.views.helper import access as ghop_access
+from soc.modules.ghop.views.helper import redirects as ghop_redirects
+
+import soc.modules.ghop.logic.models.program
+
+
+class View(program.View):
+  """View methods for the GHOP Program model.
+  """
+
+  DEF_PARTICIPATING_ORGS_MSG_FMT = ugettext(
+      'The following is a list of all the participating organizations under '
+      'the programme %(name)s. To know more about each organization and see '
+      'the tasks published by them please visit the corresponding links.')
+
+  DEF_TASK_QUOTA_ALLOCATION_MSG = ugettext(
+      "Use this view to assign task quotas.")
+
+  DEF_TASK_QUOTA_ERROR_MSG_FMT = ugettext(
+      "Task Quota limit for the organizations %s do not contain"
+      " a valid number(>0) and has not been updated.")
+
+  def __init__(self, params=None):
+    """Defines the fields and methods required for the program View class
+    to provide the user with list, public, create, edit and delete views.
+
+    Params:
+      params: a dict with params for this View
+    """
+
+    rights = ghop_access.GHOPChecker(params)
+    rights['show'] = ['allow']
+    rights['create'] = [('checkSeeded', ['checkHasActiveRoleForScope',
+                                         host_logic.logic])]
+    rights['edit'] = [('checkIsHostForProgram',
+                       [ghop_program_logic.logic])]
+    rights['delete'] = ['checkIsDeveloper']
+    rights['task_difficulty'] = [('checkIsHostForProgram',
+                                  [ghop_program_logic.logic])]
+    rights['task_type'] = [('checkIsHostForProgram',
+                            [ghop_program_logic.logic])]
+    rights['difficulty_tag_edit'] = [('checkIsHostForProgram',
+                                      [ghop_program_logic.logic])]
+    rights['type_tag_edit'] = [('checkIsHostForProgram',
+                                [ghop_program_logic.logic])]
+
+    new_params = {}
+    new_params['logic'] = soc.modules.ghop.logic.models.program.logic
+    new_params['rights'] = rights
+
+    new_params['name'] = "GHOP Program"
+    new_params['module_name'] = "program"
+    new_params['sidebar_grouping'] = 'Programs'
+
+    new_params['module_package'] = 'soc.modules.ghop.views.models'
+    new_params['url_name'] = 'ghop/program'
+
+    # TODO: this list can be reduced after GSoC has been moved
+    new_params['extra_dynaexclude'] = ['apps_tasks_limit',
+                                       'min_slots', 'max_slots',
+                                       'slots', 'slot_allocation',
+                                       'allocations_visible',
+                                       'task_difficulties', 'task_types',
+                                       ]
+
+    patterns = []
+    patterns += [
+        (r'^%(url_name)s/(?P<access_type>assign_task_quotas)/%(key_fields)s$',
+          '%(module_package)s.%(module_name)s.assign_task_quotas',
+          'Assign task quota limits'),
+        (r'^%(url_name)s/(?P<access_type>task_difficulty)/%(key_fields)s$',
+         '%(module_package)s.%(module_name)s.task_difficulty_edit',
+         'Edit Task Difficulty Tags'),
+        (r'^%(url_name)s/(?P<access_type>task_type)/%(key_fields)s$',
+         '%(module_package)s.%(module_name)s.task_type_edit',
+         'Edit Task Type Tags'),
+        (r'^%(url_name)s/(?P<access_type>difficulty_tag_edit)$',
+         '%(module_package)s.%(module_name)s.difficulty_tag_edit',
+         'Edit a Difficulty Tag'),
+        (r'^%(url_name)s/(?P<access_type>type_tag_edit)$',
+         '%(module_package)s.%(module_name)s.task_type_tag_edit',
+         'Edit a Task Type Tag'),
+        ]
+
+    new_params['extra_django_patterns'] = patterns
+
+    params = dicts.merge(params, new_params, sub_merge=True)
+
+    super(View, self).__init__(params=params)
+
+    dynafields = [
+        {'name': 'task_difficulties',
+         'base': forms.CharField,
+         'label': 'Task Difficulty Levels',
+         'widget': widgets.ReadOnlyInput(),
+         'required': False,
+         'help_text': ugettext('Lists all the difficulty levels that '
+                               'can be assigned to a task. Edit them '
+                               'from the Program menu on sidebar.'),
+         },
+         {'name': 'task_types',
+         'base': forms.CharField,
+         'label': 'Task Type Tags',
+         'widget': widgets.ReadOnlyInput(),
+         'required': False,
+         'help_text': ugettext('Lists all the types a task can be in. '
+                               'Edit them from the Program menu on sidebar.'),
+         },
+        ]
+
+    dynaproperties = params_helper.getDynaFields(dynafields)
+
+    edit_form = dynaform.extendDynaForm(
+        dynaform=self._params['edit_form'],
+        dynaproperties=dynaproperties)
+
+    self._params['edit_form'] = edit_form
+
+  def _editGet(self, request, entity, form):
+    """See base.View._editGet().
+    """
+
+    # TODO: can't a simple join operation do this?
+    tds = ghop_task_model.TaskDifficultyTag.get_by_scope(entity)
+    if tds:
+      td_str = ''
+      for td in tds[:-1]:
+        td_str += str(td) + ', '
+
+      td_str += str(tds[-1])
+
+      form.fields['task_difficulties'].initial = td_str
+
+    tts = ghop_task_model.TaskTypeTag.get_by_scope(entity)
+    if tts:
+      tt_str = ''
+      for tt in tts[:-1]:
+        tt_str += str(tt) + ', '
+
+      tt_str += str(tts[-1])
+
+      form.fields['task_types'].initial = tt_str
+
+    return super(View, self)._editGet(request, entity, form)
+
+  @decorators.merge_params
+  @decorators.check_access
+  def assignTaskQuotas(self, request, access_type, page_name=None,
+                       params=None, filter=None, **kwargs):
+    """View that allows to assign task quotas for accepted GHOP organization.
+
+    This view allows the program admin to set the task quota limits
+    and change them at any time when the program is active. 
+    """
+
+    # TODO: Once GAE Task APIs arrive, this view will be managed by them
+    program = ghop_program_logic.logic.getFromKeyFieldsOr404(kwargs)
+
+    from soc.modules.ghop.views.models import \
+        organization as ghop_organization_view
+
+    org_params = ghop_organization_view.view.getParams().copy()
+
+    context = {}
+
+    if request.method == 'POST':
+      return self.assignTaskQuotasPost(request, context, org_params, 
+                                       page_name, params, program,
+                                       **kwargs)
+    else: # request.method == 'GET'
+      return self.assignTaskQuotasGet(request, context, org_params,
+                                      page_name, params, program, 
+                                      **kwargs)
+
+  def assignTaskQuotasPost(self, request, context, org_params, 
+                           page_name, params, entity, **kwargs):
+    """Handles the POST request for the task quota allocation page.
+
+    Args:
+        entity: the program entity
+        rest: see base.View.public()
+    """
+
+    ghop_org_logic = org_params['logic']
+
+    error_orgs = ''
+    for link_id, task_count in request.POST.items():
+      fields = {
+          'link_id': link_id,
+          'scope': entity,
+          'scope_path': entity.key().id_or_name(),
+          }
+      key_name = ghop_org_logic.getKeyNameFromFields(fields)
+
+      try:
+        task_count = int(task_count)
+        if task_count >= 0:
+          fields['task_quota_limit'] = task_count
+          ghop_org_logic.updateOrCreateFromKeyName(fields, key_name)
+        else:
+          raise ValueError
+      except ValueError:
+        org_entity = ghop_org_logic.getFromKeyName(key_name)
+        error_orgs += org_entity.name + ', ' 
+
+    if error_orgs:
+      context['error_message'] = self.DEF_TASK_QUOTA_ERROR_MSG_FMT % (
+          error_orgs[:-2])    
+
+      return self.assignTaskQuotasGet(request, context, org_params,
+                                      page_name, params, entity,
+                                      **kwargs) 
+
+    # redirect to the same page
+    return http.HttpResponseRedirect('')
+
+  def assignTaskQuotasGet(self, request, context, org_params, 
+                          page_name, params, entity, **kwargs):
+    """Handles the GET request for the task quota allocation page.
+
+    Args:
+        entity: the program entity
+        rest see base.View.public()
+    """
+
+    org_params['list_template'] = ('modules/ghop/program/'
+        'allocation/allocation.html')
+    org_params['list_heading'] = ('modules/ghop/program/'
+        'allocation/heading.html')
+    org_params['list_row'] = 'modules/ghop/program/allocation/row.html'
+    org_params['list_pagination'] = 'soc/list/no_pagination.html'
+
+    description = self.DEF_TASK_QUOTA_ALLOCATION_MSG
+
+    filter = {
+        'scope': entity,
+        'status': 'active',
+        }
+
+    content = self._getAcceptedOrgsList(description, org_params,
+                                        filter, False)
+
+    contents = [content]
+
+    return self._list(request, org_params, contents, page_name, context)
+
+  @decorators.merge_params
+  def getExtraMenus(self, id, user, params=None):
+    """See soc.views.models.program.View.getExtraMenus().
+    """
+
+    logic = params['logic']
+    rights = params['rights']
+
+    # only get all invisible and visible programs
+    fields = {'status': ['invisible', 'visible']}
+    entities = logic.getForFields(fields)
+
+    menus = []
+
+    rights.setCurrentUser(id, user)
+
+    for entity in entities:
+      items = []
+
+      if entity.status == 'visible':
+        # show the documents for this program, even for not logged in users
+        items += document_view.view.getMenusForScope(entity, params)
+        items += self._getTimeDependentEntries(entity, params, id, user)
+
+      try:
+        # check if the current user is a host for this program
+        rights.doCachedCheck('checkIsHostForProgram',
+                             {'scope_path': entity.scope_path,
+                              'link_id': entity.link_id}, [])
+
+        if entity.status == 'invisible':
+          # still add the document links so hosts can see how it looks like
+          items += document_view.view.getMenusForScope(entity, params)
+          items += self._getTimeDependentEntries(entity, params, id, user)
+
+        items += [(redirects.getReviewOverviewRedirect(
+            entity, {'url_name': 'ghop/org_app', 'scope_view': self}),
+            "Review Organization Applications", 'any_access')]
+        # add link to edit Program Profile
+        items += [(redirects.getEditRedirect(entity, params),
+            'Edit Program Profile', 'any_access')]
+        # add link to Assign Task Quota limits
+        items += [(ghop_redirects.getAssignTaskQuotasRedirect(entity, params),
+            'Assign Task Quota limits', 'any_access')]
+        # add link to edit Program Timeline
+        items += [(redirects.getEditRedirect(
+            entity, {'url_name': 'ghop/timeline'}),
+            "Edit Program Timeline", 'any_access')]
+        # add link to create a new Program Document
+        items += [(redirects.getCreateDocumentRedirect(entity, 'ghop/program'),
+            "Create a New Document", 'any_access')]
+        # add link to list all Program Document
+        items += [(redirects.getListDocumentsRedirect(entity, 'ghop/program'),
+            "List Documents", 'any_access')]
+        # add link to edit Task Difficulty Levels
+        items += [(ghop_redirects.getDifficultyEditRedirect(
+            entity, {'url_name': 'ghop/program'}),
+            "Edit Task Difficulty Levels", 'any_access')]
+        # add link to edit Task Type Tags
+        items += [(ghop_redirects.getTaskTypeEditRedirect(
+            entity, {'url_name': 'ghop/program'}),
+            "Edit Task Type Tags", 'any_access')]
+
+      except out_of_band.Error:
+        pass
+
+      items = sidebar.getSidebarMenu(id, user, items, params=params)
+      if not items:
+        continue
+
+      menu = {}
+      menu['heading'] = entity.short_name
+      menu['items'] = items
+      menu['group'] = 'Programs'
+      menus.append(menu)
+    
+    return menus
+
+  def _getTimeDependentEntries(self, ghop_program_entity, params, id, user):
+    """Returns a list with time dependent menu items.
+    """
+
+    items = []
+
+    timeline_entity = ghop_program_entity.timeline
+
+    # get the student entity for this user and program
+    filter = {'user': user,
+              'scope': ghop_program_entity,
+              'status': 'active'}
+    student_entity = ghop_student_logic.logic.getForFields(filter, unique=True)
+
+    if student_entity:
+      items += self._getStudentEntries(ghop_program_entity, student_entity,
+                                       params, id, user)
+
+    # get mentor and org_admin entity for this user and program
+    filter = {'user': user,
+              'program': ghop_program_entity,
+              'status': 'active'}
+    mentor_entity = ghop_mentor_logic.logic.getForFields(filter, unique=True)
+    org_admin_entity = ghop_org_admin_logic.logic.getForFields(
+        filter, unique=True)
+
+    if mentor_entity or org_admin_entity:
+      items += self._getOrganizationEntries(
+          ghop_program_entity, org_admin_entity,
+          mentor_entity, params, id, user)
+
+    if user and not (student_entity or mentor_entity or org_admin_entity):
+      if timeline_helper.isActivePeriod(timeline_entity, 'student_signup'):
+        # this user does not have a role yet for this program
+        items += [('/ghop/student/apply/%s' % (
+            ghop_program_entity.key().id_or_name()),
+            "Register as a Student", 'any_access')]
+
+    if timeline_helper.isAfterEvent(timeline_entity, 'org_signup_start'):
+      url = ghop_redirects.getParticipatingOrgsRedirect(
+          ghop_program_entity, params)
+      # add a link to list all the organizations
+      items += [(url, "List participating Organizations", 'any_access')]
+
+    return items
+
+  def _getStudentEntries(self, ghop_program_entity, student_entity, 
+                         params, id, user):
+    """Returns a list with menu items for students in a specific program.
+    """
+
+    items = []
+
+    timeline_entity = ghop_program_entity.timeline
+
+    if timeline_helper.isAfterEvent(timeline_entity,
+                                   'student_signup_start'):
+      # add a link to show all projects
+      items += [(redirects.getListProjectsRedirect(ghop_program_entity,
+          {'url_name':'ghop/task'}),
+          "List my Tasks", 'any_access')]
+
+    items += [(redirects.getEditRedirect(student_entity, 
+        {'url_name': 'ghop/student'}),
+        "Edit my Student Profile", 'any_access')]
+
+    items += [(redirects.getManageRedirect(student_entity,
+        {'url_name':'ghop/student'}),
+        "Resign as a Student", 'any_access')]
+
+    return items
+
+  @decorators.merge_params
+  @decorators.check_access
+  def taskDifficultyEdit(self, request, access_type, page_name=None,
+                         params=None, filter=None, **kwargs):
+    """View method used to edit Difficulty Level tags.
+    """
+
+    params = dicts.merge(params, self._params)
+
+    try:
+      entity = self._logic.getFromKeyFieldsOr404(kwargs)
+    except out_of_band.Error, error:
+      return helper.responses.errorResponse(
+          error, request, template=params['error_public'])
+
+    context = helper.responses.getUniversalContext(request)
+    helper.responses.useJavaScript(context, params['js_uses_all'])
+    context['page_name'] = page_name
+
+    context['program_key_name'] = entity.key().name()
+
+    context['difficulties'] = ghop_task_model.TaskDifficultyTag.get_by_scope(
+        entity)
+
+    params['edit_template'] = 'modules/ghop/program/tag/difficulty.html'
+
+    return self._constructResponse(request, entity, context, None, params)
+
+  @decorators.merge_params
+  @decorators.check_access
+  def difficultyTagEdit(self, request, access_type, page_name=None,
+                        params=None, filter=None, **kwargs):
+    """View method used to edit a supplied Difficulty level tag.
+    """
+
+    get_params = request.GET
+
+    order = get_params.getlist('order')
+    program_key_name = get_params.get('program_key_name')
+
+    program_entity = ghop_program_logic.logic.getFromKeyName(
+        program_key_name)
+
+    if order:
+      for index, elem in enumerate(order):
+        ghop_task_model.TaskDifficultyTag.update_order(
+              program_entity, elem, index)
+      return http.HttpResponse()
+    else:
+      tag_data = get_params.getlist('tag_data')
+
+      tag_name = tag_data[0].strip()
+      tag_value = tag_data[1].strip()
+
+      if tag_name:
+        if not tag_value:
+          ghop_task_model.TaskDifficultyTag.delete_tag(
+              program_entity, tag_name)
+        elif tag_name != tag_value:
+          ghop_task_model.TaskDifficultyTag.copy_tag(
+              program_entity, tag_name, tag_value)
+      else:
+        ghop_task_model.TaskDifficultyTag.get_or_create(
+            program_entity, tag_value)
+
+      return http.HttpResponse(tag_value)
+
+  @decorators.merge_params
+  @decorators.check_access
+  def taskTypeEdit(self, request, access_type, page_name=None,
+                   params=None, filter=None, **kwargs):
+    """View method used to edit Task Type tags.
+    """
+
+    params = dicts.merge(params, self._params)
+
+    try:
+      entity = self._logic.getFromKeyFieldsOr404(kwargs)
+    except out_of_band.Error, error:
+      return helper.responses.errorResponse(
+          error, request, template=params['error_public'])
+
+    context = helper.responses.getUniversalContext(request)
+    helper.responses.useJavaScript(context, params['js_uses_all'])
+    context['page_name'] = page_name
+
+    context['program_key_name'] = entity.key().name()
+
+    context['task_types'] = ghop_task_model.TaskTypeTag.get_by_scope(
+        entity)
+
+    params['edit_template'] = 'modules/ghop/program/tag/task_type.html'
+
+    return self._constructResponse(request, entity, context, None, params)
+
+  @decorators.merge_params
+  @decorators.check_access
+  def taskTypeTagEdit(self, request, access_type, page_name=None,
+                      params=None, filter=None, **kwargs):
+    """View method used to edit a supplied Task Type tag.
+    """
+
+    get_params = request.GET
+
+    order = get_params.getlist('order')
+    program_key_name = get_params.get('program_key_name')
+
+    program_entity = ghop_program_logic.logic.getFromKeyName(
+        program_key_name)
+
+    if order:
+      for index, elem in enumerate(order):
+        ghop_task_model.TaskTypeTag.update_order(
+              program_entity, elem, index)
+      return http.HttpResponse()
+    else:
+      tag_data = get_params.getlist('tag_data')
+      program_key_name = get_params.get('program_key_name')
+
+      tag_name = tag_data[0].strip()
+      tag_value = tag_data[1].strip()
+
+      program_entity = ghop_program_logic.logic.getFromKeyName(
+          program_key_name)
+
+      if tag_name:
+        if not tag_value:
+          ghop_task_model.TaskTypeTag.delete_tag(
+              program_entity, tag_name)
+        elif tag_name != tag_value:
+          ghop_task_model.TaskTypeTag.copy_tag(
+              program_entity, tag_name, tag_value)
+      else:
+        ghop_task_model.TaskTypeTag.get_or_create(program_entity, tag_value)
+
+      return http.HttpResponse(tag_value)
+
+
+view = View()
+
+admin = decorators.view(view.admin)
+assign_task_quotas = decorators.view(view.assignTaskQuotas)
+create = decorators.view(view.create)
+delete = decorators.view(view.delete)
+edit = decorators.view(view.edit)
+list = decorators.view(view.list)
+public = decorators.view(view.public)
+export = decorators.view(view.export)
+home = decorators.view(view.home)
+difficulty_tag_edit = decorators.view(view.difficultyTagEdit)
+task_type_tag_edit = decorators.view(view.taskTypeTagEdit)
+task_difficulty_edit = decorators.view(view.taskDifficultyEdit)
+task_type_edit = decorators.view(view.taskTypeEdit)