Added GHOP Task View.
authorMadhusudan.C.S <madhusudancs@gmail.com>
Tue, 08 Sep 2009 20:54:19 +0200
changeset 2885 f064654837f7
parent 2884 2be8f6b8379e
child 2886 52f9b098a6da
Added GHOP Task View. TODO's have been added on the places where the code struggles with consistency and compactness issues. Reviewed by: to-be-reviewed
app/soc/modules/ghop/views/models/task.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/modules/ghop/views/models/task.py	Tue Sep 08 20:54:19 2009 +0200
@@ -0,0 +1,1421 @@
+#!/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.
+
+"""Views for Tasks.
+"""
+
+__authors__ = [
+    '"Madhusudan.C.S" <madhusudancs@gmail.com>'
+  ]
+
+
+import datetime
+import time
+
+from google.appengine.ext import db
+
+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.models import host as host_logic 
+from soc.logic.models import student as student_logic
+from soc.logic.models import user as user_logic
+from soc.views import helper
+from soc.views import out_of_band
+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 requests
+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.modules.ghop.logic import cleaning as ghop_cleaning
+from soc.modules.ghop.logic.models import comment as ghop_comment_logic
+from soc.modules.ghop.logic.models import mentor as ghop_mentor_logic
+from soc.modules.ghop.logic.models import organization as ghop_org_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 task as ghop_task_logic
+from soc.modules.ghop.models import task as ghop_task_model
+from soc.modules.ghop.views.helper import access
+from soc.modules.ghop.views.helper import redirects as ghop_redirects
+from soc.modules.ghop.views.models import organization as ghop_org_view
+from soc.modules.ghop.tasks import task_update
+
+import soc.modules.ghop.logic.models.task
+
+
+class View(base.View):
+  """View methods for the Tasks.
+  """
+
+  DEF_AWAITING_REG_MSG = ugettext(
+      'The task is open but you cannot claim this task since you '
+      'have not completed the student signup after finishing your '
+      'first task.')
+
+  DEF_CAN_EDIT_TASK_MSG = ugettext(
+      'The task can be edited by clicking on the edit link '
+      'next to the title above.')
+
+  DEF_MAX_TASK_LIMIT_MSG_FMT = ugettext(
+      'The task is open but you cannot claim this task since you '
+      'either have already claimed %d task.')
+
+  DEF_SIGNIN_TO_COMMENT_MSG = ugettext(
+      '<a href=%s>Sign in</a> to perform any action or comment on '
+      'this task.')
+
+  DEF_STUDENT_SIGNUP_MSG = ugettext(
+      'You have successfully completed this task. Sign up as a student '
+      'before you proceed further.')
+
+  DEF_TASK_CLAIMED_BY_YOU_MSG = ugettext( 
+      'This task has been claimed by you!')
+
+  DEF_TASK_CLAIMED_BY_STUDENT_MSG = ugettext(
+      'This task has been claimed by a student!')
+
+  DEF_TASK_CLAIMED_MSG = ugettext(
+      'The task is already claimed and the work is in progress.')
+
+  DEF_TASK_CLAIM_REQUESTED_MSG = ugettext(
+      'A student has requested to claim this task. Accept or '
+      'Reject the request.')
+
+  DEF_TASK_CLOSED_MSG = ugettext('The task is closed.')
+
+  DEF_TASK_CMPLTD_BY_YOU_MSG = ugettext(
+      'You have successfully completed this task!')
+
+  DEF_TASK_NO_MORE_SUBMIT_MSG = ugettext(
+      'You have submitted the work to this task, but deadline has passed '
+      'You cannot submit any more work until your mentor extends the '
+      'deadline.')
+
+  DEF_TASK_MENTOR_REOPENED_MSG = ugettext(
+      'The task has been reopened.')
+
+  DEF_TASK_NEEDS_REVIEW_MSG = ugettext(
+      'Student has submitted his work for this task! It needs review.')
+
+  DEF_TASK_OPEN_MSG = ugettext(
+      'This task is open. If you are GHOP student, you can claim it!')
+
+  DEF_TASK_REOPENED_MSG = ugettext(
+      'This task has been reopened. If you are a GHOP student, '
+      'you can clam it!')
+
+  DEF_TASK_REQ_CLAIMED_BY_YOU_MSG = ugettext(
+      'You have requested to claim this task!')
+
+  DEF_TASK_UNPUBLISHED_MSG = ugettext(
+      'The task is not yet published. It can be edited by clicking on '
+      'the edit button below.')
+
+  DEF_WS_MSG_FMT = ugettext(
+      '(To see the work submitted <a href=#ws%d>click here</a>.)')
+
+  def __init__(self, params=None):
+    """Defines the fields and methods required for the task View class
+    to provide the user with the necessary views.
+
+    Params:
+      params: a dict with params for this View
+    """
+
+    rights = access.GHOPChecker(params)
+    # TODO: create and suggest_task don't need access checks which use state
+    # also feels like roles are being checked twice?
+    rights['create'] = [
+        ('checkCanOrgAdminOrMentorEdit', ['scope_path', True]),
+        ('checkRoleAndStatusForTask',
+            [['ghop/org_admin'], ['active'], 
+            []])]
+    rights['edit'] = [
+        ('checkCanOrgAdminOrMentorEdit', ['scope_path', False]),
+        ('checkRoleAndStatusForTask',
+            [['ghop/org_admin'], ['active'], 
+            ['Unapproved', 'Unpublished', 'Open']])]
+    rights['delete'] = ['checkIsDeveloper']
+    rights['show'] = ['checkStatusForTask']
+    rights['list_org_tasks'] = [
+        ('checkCanOrgAdminOrMentorEdit', ['scope_path', False])]
+    rights['suggest_task'] = [
+        ('checkCanOrgAdminOrMentorEdit', ['scope_path', True]),
+        ('checkRoleAndStatusForTask',
+            [['ghop/org_admin', 'ghop/mentor'], ['active'], 
+            []])]
+    rights['search'] = ['allow']
+
+    new_params = {}
+    new_params['logic'] = soc.modules.ghop.logic.models.task.logic
+    new_params['rights'] = rights
+
+    new_params['name'] = "Task"
+    new_params['module_name'] = "task"
+    new_params['sidebar_grouping'] = 'Tasks'
+
+    new_params['module_package'] = 'soc.modules.ghop.views.models'
+    new_params['url_name'] = 'ghop/task'
+
+    new_params['scope_view'] = ghop_org_view
+    new_params['scope_redirect'] = redirects.getCreateRedirect
+
+    new_params['list_heading'] = 'modules/ghop/task/list/heading.html' 
+    new_params['list_row'] = 'modules/ghop/task/list/row.html'
+
+    new_params['extra_dynaexclude'] = ['task_type', 'mentors', 'user',
+                                       'student', 'program', 'status', 
+                                       'deadline', 'created_by',
+                                       'created_on', 'modified_by',
+                                       'modified_on', 'history',
+                                       'link_id', 'difficulty']
+
+    patterns = []
+    patterns += [
+        (r'^%(url_name)s/(?P<access_type>suggest_task)/%(scope)s$',
+        '%(module_package)s.%(module_name)s.suggest_task',
+        'Mentors suggest %(name)s'),
+        (r'^%(url_name)s/(?P<access_type>suggest_task)/%(key_fields)s$',
+        '%(module_package)s.%(module_name)s.suggest_task',
+        'Mentors edit a %(name)s'),
+        (r'^%(url_name)s/(?P<access_type>list_org_tasks)/%(scope)s$',
+        '%(module_package)s.%(module_name)s.list_org_tasks',
+        'List Organization %(name)s'),
+        (r'^%(url_name)s/(?P<access_type>search)/'
+         '(?P<scope_path>%(ulnp)s)/%(lnp)s$',
+        '%(module_package)s.%(module_name)s.search',
+        'Search for %(name)ss'),
+        ]
+
+    new_params['extra_django_patterns'] = patterns
+
+    new_params['create_dynafields'] = [
+        {'name': 'arbit_tags',
+         'base': forms.fields.CharField,
+         'label': 'Arbitrary Tags',
+         'required': False,
+         'group': ugettext('Tags'),
+         'help_text': ugettext(
+             'Enter arbitrary Tags for this task separated by comma.\n'
+             '<b>Note:</b> Tag names are case sensitive. If the tag is same '
+             'as the program mandated tag, it will be considered as a '
+             'mandatory tag.')
+         },
+        ]
+
+    new_params['create_extra_dynaproperties'] = {
+        'description': forms.fields.CharField(required=True,
+            widget=widgets.FullTinyMCE(attrs={'rows': 25, 'cols': 100})),
+        'scope_path': forms.CharField(widget=forms.HiddenInput,
+            required=True),
+        'time_to_complete': forms.IntegerField(min_value=1,
+                                               required=True),
+        'clean_description': cleaning.clean_html_content('description'),
+        'clean_arbit_tags': cleaning.str2set('arbit_tags'),
+        }
+
+    new_params['edit_extra_dynaproperties'] = {
+        'link_id': forms.CharField(widget=forms.HiddenInput)
+        }
+
+    new_params['public_template'] = 'modules/ghop/task/public.html'
+
+    params = dicts.merge(params, new_params, sub_merge=True)
+
+    super(View, self).__init__(params=params)
+
+    # holds the base form for the task creation and editing
+    self._params['base_create_form'] = self._params['create_form']
+    self._params['base_edit_form'] = self._params['edit_form'] 
+
+    # extend create and edit form for org admins
+    dynafields = [
+        {'name': 'mentors_list',
+         'required': True,
+         'base': forms.fields.CharField,
+         'label': 'Assign mentors',
+         'help_text': 'Assign mentors to this task by '
+             'giving their link_ids separated by comma.',
+         },
+        {'name': 'published',
+         'required': False,
+         'initial': False,
+         'base': forms.fields.BooleanField,
+         'label': 'Publish the task',
+         'help_text': ugettext('By ticking this box, the task will be'
+                               'made public and can be claimed by students.'),
+         }
+        ]
+
+    dynaproperties = params_helper.getDynaFields(dynafields)
+
+    dynaproperties['clean_mentors_list'] = ghop_cleaning.cleanMentorsList(
+        'mentors_list')
+
+    create_form = dynaform.extendDynaForm(
+        dynaform=self._params['create_form'],
+        dynaproperties=dynaproperties)
+
+    self._params['create_form'] = create_form
+
+    edit_form = dynaform.extendDynaForm(
+        dynaform=self._params['edit_form'],
+        dynaproperties=dynaproperties)
+
+    self._params['edit_form'] = edit_form
+
+    # create the comment form
+    dynafields = [
+        {'name': 'comment',
+         'base': forms.CharField,
+         'widget': widgets.FullTinyMCE(attrs={'rows': 10, 'cols': 40}),
+         'label': 'Comment',
+         'required': False,
+         'example_text': 'Comment is optional.<br/>'
+             'Choose an appropriate Action below and Save your '
+             'changes.<br /><br />Caution, you will not be able '
+             'to edit your comment!',
+         },
+         ]
+
+    dynaproperties = params_helper.getDynaFields(dynafields)
+    dynaproperties['clean_comment'] = cleaning.clean_html_content('comment')
+    dynaproperties['clean'] = ghop_cleaning.clean_comment(
+        'comment', 'action', 'work_submission')
+
+    comment_form = dynaform.newDynaForm(dynamodel=None, 
+        dynabase=helper.forms.BaseForm, dynainclude=None, 
+        dynaexclude=None, dynaproperties=dynaproperties)
+    self._params['comment_form'] = comment_form
+
+  def _getTagsForProgram(self, form_name, params, **kwargs):
+    """Extends form dynamically from difficulty levels in program entity.
+
+    Args:
+     form_name: the Form entry in params to extend
+     params: the params for the view
+    """
+
+    # obtain program_entity using scope_path which holds 
+    # the org_entity key_name
+    org_entity = ghop_org_logic.logic.getFromKeyName(kwargs['scope_path'])
+    program_entity = ghop_program_logic.logic.getFromKeyName(
+        org_entity.scope_path)
+
+    # get a list difficulty levels stored for the program entity
+    tds = ghop_task_model.TaskDifficultyTag.get_by_scope(
+        program_entity) 
+
+    difficulties = []
+    for td in tds:
+      difficulties.append((td.tag, td.tag))
+
+    # get a list of task type tags stored for the program entity
+    tts = ghop_task_model.TaskTypeTag.get_by_scope(program_entity) 
+
+    type_tags = []
+    for tt in tts:
+      type_tags.append((tt.tag, tt.tag))
+
+    # create the difficultly level field containing the choices 
+    # defined in the program entity
+    dynafields = [
+        {'name': 'difficulty',
+         'base': forms.ChoiceField,
+         'label': 'Difficulty level',
+         'required': True,
+         'passthrough': ['initial', 'required', 'choices',
+                         'label', 'help_text'],
+         'choices': difficulties,
+         'group': ugettext('Tags'),
+         'help_text': ugettext('Difficulty Level of this task.'),
+         },
+         {'name': 'type_tags',
+         'base': forms.MultipleChoiceField,
+         'label': 'Task Types',
+         'required': True,
+         'passthrough': ['initial', 'required', 'choices',
+                         'label', 'help_text'],
+         'choices': type_tags,
+         'group': ugettext('Tags'),
+         'help_text': ugettext('Task Type tags mandated by the program. You '
+                               'must select one or more of them.'),
+         },
+       ]
+
+    dynaproperties = params_helper.getDynaFields(dynafields)
+
+    extended_form = dynaform.extendDynaForm(
+        dynaform=params[form_name],
+        dynaproperties=dynaproperties)
+
+    return extended_form
+
+  @decorators.check_access
+  def create(self, request, access_type,
+             page_name=None, params=None, **kwargs):
+    """Replaces the create Form with the dynamic one.
+
+    For args see base.View.create().
+    """
+
+    params = dicts.merge(params, self._params)
+
+    # extend create_form to include difficulty levels
+    params['create_form'] = self._getTagsForProgram(
+        'create_form', params, **kwargs)
+
+    return super(View, self).create(request, 'allow', page_name=page_name,
+                                    params=params, **kwargs)
+
+  @decorators.merge_params
+  @decorators.check_access
+  def edit(self, request, access_type,
+           page_name=None, params=None, seed=None, **kwargs):
+    """See base.View.edit().
+    """
+
+    logic = params['logic']
+
+    context = helper.responses.getUniversalContext(request)
+    helper.responses.useJavaScript(context, params['js_uses_all'])
+    context['page_name'] = page_name
+
+    try:
+      entity = logic.getFromKeyFieldsOr404(kwargs)
+    except out_of_band.Error, error:
+      msg = self.DEF_CREATE_NEW_ENTITY_MSG_FMT % {
+          'entity_type_lower' : params['name'].lower(),
+          'entity_type' : params['name'],
+          'create' : params['missing_redirect']
+          }
+      error.message_fmt = error.message_fmt + msg
+      return helper.responses.errorResponse(
+          error, request, context=context)
+
+    user_account = user_logic.logic.getForCurrentAccount()
+
+    filter = {
+        'user': user_account,
+        }
+
+    # extend edit_form to include difficulty levels
+    params['edit_form'] = self._getTagsForProgram(
+        'edit_form', params, **kwargs)
+
+    org_admin_entities = ghop_org_admin_logic.logic.getForFields(filter) 
+
+    if entity and entity.status == 'Unapproved':
+      approval_req = True 
+      for org_admin_entity in org_admin_entities:
+        if org_admin_entity.key().name() == entity.created_by.key().name():
+          approval_req = False
+          break
+
+      if approval_req:
+        dynafields = [
+            {'name': 'approved',
+             'required': False,
+             'initial': False,
+             'base': forms.fields.BooleanField,
+             'label': 'Approve the task',
+             'help_text': 'By ticking this box, the task will be'
+                 'will be approved for publishing.',
+            }
+            ]
+
+        dynaproperties = params_helper.getDynaFields(dynafields)
+
+        edit_form = dynaform.extendDynaForm(
+            dynaform=params['edit_form'],
+            dynaproperties=dynaproperties)
+
+        params['edit_form'] = edit_form
+
+    if request.method == 'POST':
+      return self.editPost(request, entity, context, params=params)
+    else:
+      return self.editGet(request, entity, context, params=params)
+
+  def _editGet(self, request, entity, form):
+    """See base.View._editGet().
+    """
+
+    if entity.task_type:
+      form.fields['type_tags'].initial = entity.tags_string(
+          entity.task_type, ret_list=True)
+    if entity.arbit_tag:
+      form.fields['arbit_tags'].initial = entity.tags_string(
+          entity.arbit_tag)
+
+    if entity.difficulty:
+      form.fields['difficulty'].initial = entity.tags_string( 
+          entity.difficulty)
+
+    if entity.mentors and 'mentors_list' in form.fields:
+      mentor_entities = db.get(entity.mentors)
+      mentors_list = []
+      for mentor in mentor_entities:
+        mentors_list.append(mentor.link_id)
+      form.fields['mentors_list'].initial = ', '.join(mentors_list)
+
+    form.fields['link_id'].initial = entity.link_id
+
+    # checks if the task is already approved or not and sets
+    # the form approved field
+    if 'approved' in form.fields:
+      if entity.status == 'Unapproved':
+        form.fields['approved'].initial = False
+      else:
+        form.fields['approved'].initial = True
+ 
+    # checks if the task is already published or not and sets
+    # the form published field 
+    if 'published' in form.fields:
+      if entity.status == 'Unapproved' or entity.status == 'Unpublished':
+        form.fields['published'].initial = False
+      else:
+        form.fields['published'].initial = True
+
+    return super(View, self)._editGet(request, entity, form)
+
+  def _editPost(self, request, entity, fields):
+    """See base._editPost().
+    """
+
+    super(View, self)._editPost(request, entity, fields)
+
+    # TODO: this method can be made more clear, it seems a bit of a mess
+
+    if not entity:
+      program_entity = fields['scope'].scope
+      fields['program'] = program_entity
+    else:
+      program_entity = entity.program 
+
+    user_account = user_logic.logic.getForCurrentAccount()
+
+    filter = {
+        'user': user_account,
+        'scope': fields['scope'],
+        'status': 'active'
+        }
+
+    role_entity = ghop_org_admin_logic.logic.getForFields(
+        filter, unique=True)
+
+    if role_entity:
+      # this user can publish/approve the task
+      if fields.get('approved') and fields.get('published'):
+        fields['status'] = 'Open'
+      elif not fields.get('approved'):
+        fields['status'] = 'Unapproved'
+      else:
+        fields['status'] = 'Unpublished'
+
+      fields['mentors'] = []
+      if fields.get('mentors_list'):
+
+        for mentor_link_id in fields['mentors_list']:
+          properties = {
+              'scope_path': fields['scope_path'],
+              'link_id': mentor_link_id,
+              }
+
+          mentor_entity = ghop_mentor_logic.logic.getFromKeyFields(properties)
+          fields['mentors'].append(mentor_entity.key())
+    else:
+      role_entity = ghop_mentor_logic.logic.getForFields(
+          filter, unique=True)
+      if not entity:
+        # creating a new task
+        fields['status'] = 'Unapproved'
+
+    # explicitly change the last_modified_on since the content has been edited
+    fields['modified_on'] = datetime.datetime.now()
+
+    if not entity:
+      fields['link_id'] = 't%i' % (int(time.time()*100))
+      fields['modified_by'] = role_entity
+      fields['created_by'] = role_entity 
+      fields['created_on'] = datetime.datetime.now()
+    else:
+      fields['link_id'] = entity.link_id
+      fields['modified_by'] = role_entity
+      if 'status' not in fields:
+        fields['status'] = entity.status
+
+    fields['difficulty'] = {
+        'tags': fields['difficulty'],
+        'scope': program_entity
+        }
+
+    fields['task_type'] = {
+        'tags': fields['type_tags'],
+        'scope': program_entity
+        }
+
+    fields['arbit_tag'] = {
+        'tags': fields['arbit_tags'],
+        'scope': program_entity
+        }
+
+    return
+
+  @decorators.merge_params
+  @decorators.check_access
+  def suggestTask(self, request, access_type, page_name=None, 
+                  params=None, **kwargs):
+    """View used to allow mentors to create or edit a task. 
+
+    Tasks created by mentors must be approved by org admins
+    before they are published.
+    """
+
+    params = dicts.merge(params, self._params)
+
+    if 'link_id' in kwargs:
+      # extend create_form to include difficulty levels
+      params['mentor_form'] = self._getTagsForProgram(
+          'base_edit_form', params, **kwargs)
+      try:
+        entity = self._logic.getFromKeyFieldsOr404(kwargs)
+      except out_of_band.Error, error:
+        return helper.responses.errorResponse(
+            error, request, template=params['error_public'])
+    else:
+      # extend create_form to include difficulty levels
+      params['mentor_form'] = self._getTagsForProgram(
+          'base_create_form', params, **kwargs)
+      entity = None
+
+    context = helper.responses.getUniversalContext(request)
+    helper.responses.useJavaScript(context, params['js_uses_all'])
+    context['page_name'] = page_name
+
+    if request.method == 'POST':
+      return self.suggestTaskPost(request, context,
+                                  params, entity)
+    else:
+      return self.suggestTaskGet(request, context,
+                                 params, entity, **kwargs)
+
+  def suggestTaskPost(self, request, context, params, entity):
+    """Handles the POST request for the suggest task view.
+    """
+
+    form = params['mentor_form'](request.POST)
+
+    if not form.is_valid():
+      return self._constructResponse(request, None, context, form, params)
+
+    _, fields = helper.forms.collectCleanedFields(form)
+    # TODO: this non-standard view shouldn't call _editPost but its own method
+    self._editPost(request, entity, fields)
+
+    logic = params['logic']
+    if entity:
+      entity = logic.updateEntityProperties(entity, fields)
+    else:
+      entity = logic.updateOrCreateFromFields(fields)
+
+    page_params = params['edit_params']
+
+    redirect = ghop_redirects.getSuggestTaskRedirect(
+        entity, params)
+
+    return http.HttpResponseRedirect(redirect)
+
+  def suggestTaskGet(self, request, context, params, entity, **kwargs):
+    """Handles the GET request for the suggest task view.
+    """
+
+    if entity:
+      # populate form with the existing entity
+      form = params['mentor_form'](instance=entity)
+
+      self._editGet(request, entity, form)
+    else:
+      form = params['mentor_form']()
+
+    return self._constructResponse(request, entity, context, form, params)
+
+  @decorators.merge_params
+  @decorators.check_access
+  def listOrgTasks(self, request, access_type,
+                   page_name=None, params=None, **kwargs):
+    """See base.View.list()
+    """
+    if request.method == 'POST':
+      return self.listOrgTasksPost(request, params, **kwargs)
+    else: # request.method == 'GET'
+      return self.listOrgTasksGet(request, page_name, params, **kwargs)
+
+  def listOrgTasksPost(self, request, params, **kwargs):
+    """Handles the POST request for the list tasks view.
+    """
+
+    # update the status of task entities that have been approved
+    # and published
+    task_entities = []
+    for key_name, published in request.POST.items():
+      task_entity = ghop_task_logic.logic.getFromKeyName(key_name)
+      if task_entity:
+        task_entity.status = 'Open'
+
+        task_entities.append(task_entity)
+
+    # bulk update the task_entities
+    # TODO: Have to be replaced by Task Queue APIs later
+    db.put(task_entities)
+
+    # redirect to the same page
+    return http.HttpResponseRedirect('')
+
+  def listOrgTasksGet(self, request, page_name, params, **kwargs):
+    """Handles the GET request for the list tasks view.
+    """
+
+    from soc.modules.ghop.views.helper import list_info as list_info_helper
+
+    org_entity =  ghop_org_logic.logic.getFromKeyNameOr404(
+        kwargs['scope_path'])
+
+    contents = []
+    context = {}
+
+    user_account = user_logic.logic.getForCurrentAccount()
+
+    fields = {
+        'user': user_account,
+        'scope': org_entity,
+        'status': 'active'
+        }
+
+    up_params = params.copy()
+    # give the capability to approve tasks for the org_admins
+    if ghop_org_admin_logic.logic.getForFields(fields, unique=True):
+      up_params['list_template'] = 'modules/ghop/task/approve/approve.html'
+      up_params['list_heading'] = 'modules/ghop/task/approve/heading.html'
+      up_params['list_row'] = 'modules/ghop/task/approve/row.html'
+
+    up_params['list_action'] = (redirects.getPublicRedirect,
+                                up_params)
+
+    up_params['list_description'] = ugettext(
+       'List of Unapproved or Unpublished tasks')
+
+    filter = {
+        'scope': org_entity,
+        'status': ['Unapproved', 'Unpublished'],
+        }
+
+    up_list = lists.getListContent(request, up_params, filter,
+                                   order=order, idx=0, 
+                                   need_content=True)
+
+    if up_list:
+      up_mentors_list = {}
+      for task_entity in up_list['data']:
+        up_mentors_list[task_entity.key()] = db.get(task_entity.mentors)
+
+      up_list['info'] = (list_info_helper.getTasksInfo(up_mentors_list), None)
+
+      contents.append(up_list)
+      context['up_list'] = True
+
+    ap_params = up_params.copy()
+    ap_params['list_template'] = 'soc/models/list.html'
+    ap_params['list_heading'] = 'modules/ghop/task/list/heading.html'
+    ap_params['list_row'] = 'modules/ghop/task/list/row.html'
+
+    ap_params['list_action'] = (redirects.getPublicRedirect,
+                                ap_params)
+
+    ap_params['list_description'] = ugettext(
+       'List of published tasks')
+
+    filter = {
+        'scope': org_entity,
+        'status': ['Open', 'Reopened', 'ClaimRequested', 'Claimed',
+            'ActionNeeded', 'Closed', 'AwaitingRegistration', 
+            'NeedsWork', 'NeedsReview'],
+        }
+
+    ap_list = lists.getListContent(request, ap_params, filter,
+                                   order=order, idx=1, 
+                                   need_content=True)
+
+    if ap_list:
+      ap_mentors_list = {}
+      for task_entity in ap_list['data']:
+        ap_mentors_list[task_entity.key()] = db.get(task_entity.mentors)
+
+      ap_list['info'] = (list_info_helper.getTasksInfo(ap_mentors_list), None)
+
+      contents.append(ap_list)
+
+    # call the _list method from base to display the list
+    return self._list(request, up_params, contents, page_name, context)
+
+  @decorators.merge_params
+  @decorators.check_access
+  def public(self, request, access_type,
+             page_name=None, params=None, **kwargs):
+    """See base.View.public().
+    """
+
+    # create default template context for use with any templates
+    context = helper.responses.getUniversalContext(request)
+    helper.responses.useJavaScript(context, params['js_uses_all'])
+    context['page_name'] = page_name
+    entity = None
+    logic = params['logic']
+
+    try:
+      entity, comment_entities, ws_entities = (
+          logic.getFromKeyFieldsWithCWSOr404(kwargs))
+    except out_of_band.Error, error:
+      return helper.responses.errorResponse(
+          error, request, template=params['error_public'], context=context)
+
+    if entity.status in ['Claimed', 'NeedsReview',
+                         'ActionNeeded', 'NeedsWork']:
+      entity, comment_entity, ws_entity = (
+          ghop_task_logic.logic.updateTaskStatus(entity))
+      if comment_entity:
+        comment_entities.append(comment_entity)
+      if ws_entity:
+        ws_entities.append(ws_entity)
+
+    context['entity'] = entity
+    context['entity_key_name'] = entity.key().id_or_name() 
+    context['entity_type'] = params['name']
+    context['entity_type_url'] = params['url_name']
+
+    user_account = user_logic.logic.getForCurrentAccount()
+
+    # get some entity specific context
+    self.updatePublicContext(context, entity, comment_entities,
+                             ws_entities, user_account, params)
+
+    validation = self._constructActionsList(context, entity, 
+                                            user_account, params)
+
+    context = dicts.merge(params['context'], context)
+
+    if request.method == 'POST':
+      return self.publicPost(request, context, params, entity, 
+                             user_account, validation, **kwargs)
+    else: # request.method == 'GET'
+      return self.publicGet(request, context, params, entity,
+                            user_account, **kwargs)
+
+  def publicPost(self, request, context, params, entity,
+                 user_account=None, validation=None, **kwargs):
+    """Handles the POST request for the entity's public page.
+
+    Args:
+        entity: the task entity
+        rest: see base.View.public()
+    """
+
+    from soc.modules.ghop.logic.models import student as ghop_student_logic
+
+    form = params['comment_form'](request.POST)
+
+    if not form.is_valid():
+      template = params['public_template']
+      context['comment_form'] = form
+      return self._constructResponse(request, entity, context,
+                                     form, params, template=template)
+
+    _, fields = helper.forms.collectCleanedFields(form)
+
+    changes = []
+
+    action = fields['action']
+
+    properties = None
+    ws_properties = None
+
+    # TODO: this can be separated into several methods that handle the changes
+    if validation == 'claim_request' and action == 'request':
+      deadline = datetime.datetime.now() + datetime.timedelta(
+            hours=entity.time_to_complete)
+
+      properties = {
+          'status': 'ClaimRequested',
+          'user': user_account,
+          'deadline': deadline,
+          }
+
+      st_filter = {
+          'user': user_account,
+          'scope': entity.program,
+          'status': 'active'
+          }
+      student_entity = ghop_student_logic.logic.getForFields(
+          st_filter, unique=True)
+
+      if student_entity:
+        properties['student'] = student_entity
+
+      changes.extend([ugettext('User-Student'), 
+                      ugettext('Action-Claim Requested'), 
+                      ugettext('Status-%s' % (properties['status']))
+                      ])
+    elif (validation == 'claim_withdraw' or 
+        validation == 'needs_review') and action == 'withdraw':
+      properties = {
+          'user': None,
+          'student': None,
+          'status': 'Reopened',
+          'deadline': None,
+          }
+
+      changes.extend([ugettext('User-Student'),
+                      ugettext('Action-Withdrawn'),
+                      ugettext('Status-%s' % (properties['status']))
+                      ])
+    elif validation == 'needs_review' and action == 'needs_review':
+      properties = {
+          'status': 'NeedsReview',
+          }
+
+      changes.extend([
+          ugettext('User-Student'),
+          ugettext('Action-Submitted work and Requested for review'),
+          ugettext('Status-%s' % (properties['status']))])
+
+      ws_properties = {
+          'parent': entity,
+          'link_id': 't%i' % (int(time.time()*100)),
+          'scope_path': entity.key().name(),
+          'scope': entity.scope,
+          'user': user_account,
+          'information': fields['comment'],
+          'url_to_work': fields['work_submission'],
+          'submitted_on': datetime.datetime.now(),
+          }
+    elif validation == 'accept_claim':
+      if action == 'accept':
+        deadline = datetime.datetime.now() + datetime.timedelta(
+            hours=entity.time_to_complete)
+
+        properties = {
+            'status': 'Claimed',
+            'deadline': deadline,
+            }
+
+        changes.extend([ugettext('User-Mentor'),
+                        ugettext('Action-Claim Accepted'),
+                        ugettext('Status-%s' % (properties['status']))
+                        ])
+
+        task_update.spawnUpdateTask(entity)
+      if action == 'reject':
+        properties = {
+            'user': None,
+            'student': None,
+            'status': 'Reopened',
+            'deadline': None,
+            }
+
+        changes.extend([ugettext('User-Mentor'),
+                        ugettext('Action-Claim Rejected'),
+                        ugettext('Status-%s' % (properties['status']))
+                        ])
+    elif validation == 'close':
+      if action == 'needs_work':
+        properties = {
+            'status': 'NeedsWork',
+            }
+
+        changes.extend([ugettext('User-Mentor'),
+                        ugettext('Action-Requested more work'),
+                        ugettext('Status-%s' % (properties['status']))
+                        ])
+
+        if fields['extended_deadline'] > 0:
+          deadline = entity.deadline + datetime.timedelta(
+              hours=fields['extended_deadline'])
+
+          properties['deadline'] = deadline
+
+          changes.append(ugettext('DeadlineExtendedBy-%d hrs to %s' % (
+              fields['extended_deadline'], deadline.strftime(
+                  '%d %B %Y, %H :%M'))))
+
+          task_update.spawnUpdateTask(entity)
+        else:
+          changes.append(ugettext('NoDeadlineExtensionGiven'))
+      elif action == 'reopened':
+        properties = {
+          'user': None,
+          'student': None,
+          'status': 'Reopened',
+          'deadline': None,
+          }
+
+        changes.extend([ugettext('User-Mentor'),
+                        ugettext('Action-Reopened'),
+                        ugettext('Status-%s' % (properties['status']))
+                        ])
+      elif action == 'closed':
+        properties = {
+            'deadline': None,
+            }
+
+        if entity.student:
+          properties['status'] = 'Closed'
+        else:
+          properties['status'] = 'AwaitingRegistration'
+
+        changes.extend([ugettext('User-Mentor'),
+                        ugettext('Action-Closed the task'),
+                        ugettext('Status-%s' % (properties['status']))
+                        ])
+
+    comment_properties = {
+        'parent': entity,
+        'scope_path': entity.key().name(),
+        'created_by': user_account,
+        'changes': changes,
+        }
+
+    if ws_properties:
+      comment_properties['content'] = self.DEF_WS_MSG_FMT
+    else:
+      comment_properties['content'] = fields['comment']
+
+    ghop_task_logic.logic.updateEntityPropertiesWithCWS(
+        entity, properties, comment_properties, ws_properties)
+
+    # redirect to the same page
+    return http.HttpResponseRedirect('')
+
+  def publicGet(self, request, context, params, entity,
+                user_account, **kwargs):
+    """Handles the GET request for the entity's public page.
+
+    Args:
+        entity: the task entity
+        rest see base.View.public()
+    """
+
+    context['comment_form'] = params['comment_form']()
+
+    template = params['public_template']
+
+    return responses.respond(request, template, context=context)
+
+  def updatePublicContext(self, context, entity, comment_entities,
+                          ws_entities, user_account, params):
+    """Updates the context for the public page with information.
+
+    Args:
+      context: the context that should be updated
+      entity: a task used to set context
+      user_account: user entity of the logged in user
+      params: dict with params for the view using this context
+    """
+
+    mentor_entities = db.get(entity.mentors)
+    mentors_str = ""
+    for mentor in mentor_entities:
+      mentors_str += mentor.name() + ", "
+
+    if mentors_str:
+      context['mentors_str'] = mentors_str[:-2]
+    else:
+      context['mentors_str'] = "Not Assigned" 
+
+    context['difficulty_str'] = entity.tags_string(entity.difficulty)
+
+    context['task_type_str'] = entity.tags_string(entity.task_type)
+
+    if entity.deadline:
+      # TODO: it should be able to abuse Django functionality for this
+      ttc = entity.deadline - datetime.datetime.now()
+      (ttc_min, ttc_hour) = ((ttc.seconds / 60), (ttc.seconds / 3600))
+      if ttc_min >= 60:
+        ttc_min = ttc_min % 60
+      if ttc_hour > 1:
+        if ttc_min == 0:
+          ttc_str = '%d hours' % (ttc_hour)
+        else:
+          ttc_str = '%d:%02d hours' % (ttc_hour, ttc_min)
+        if ttc.days == 1:
+          ttc_str = '%d day, %s' % (ttc.days, ttc_str)
+        elif ttc.days > 1:
+          ttc_str = '%d days, %s' % (ttc.days, ttc_str)
+      else:
+        ttc_str = '%d mins' % (ttc_min)
+      context['time_to_complete'] = ttc_str
+    else:
+      if entity.status == 'NeedsReview':
+        context['time_to_complete'] = 'No Time Left'
+      else:
+        context['time_to_complete'] = '%d hours' % (entity.time_to_complete)
+
+    context['comments'] = comment_entities
+
+    context['work_submissions'] = ws_entities
+
+  def _constructActionsList(self, context, entity,
+                            user_account, params):
+    """Constructs a list of actions for the task page and extends
+    the comment form with this list.
+
+    This method also returns the validation used by POST method to 
+    validate the user input data.
+
+    Args:
+      context: the context that should be updated
+      entity: a task used to set context
+      user_account: user entity of the logged in user
+      params: dict with params for the view using this context
+    """
+
+    # variable that holds what kind of validation this user
+    # and task combination pass.
+    validation = None
+
+    # The following header messages are shown for non-logged in
+    # general public, logged in public and the student.
+    if entity.status is 'Closed':
+      context['header_msg'] = self.DEF_TASK_CLOSED_MSG
+      validation = 'closed'
+
+    if entity.status == 'Open':
+      context['header_msg'] = self.DEF_TASK_OPEN_MSG
+    elif entity.status == 'Reopened':
+      context['header_msg'] = self.DEF_TASK_REOPENED_MSG
+
+    if user_account:
+      actions = [('noaction', 'Comment without action')]
+
+      # if the user is logged give him the permission to claim
+      # the task only if he none of program host, org admin or mentor
+      filter = {
+          'user': user_account,
+          }
+
+      host_entity = host_logic.logic.getForFields(filter, unique=True)
+
+      filter['scope_path'] = entity.scope_path
+      org_admin_entity = ghop_org_admin_logic.logic.getForFields(
+          filter, unique=True)
+      mentor_entity = ghop_mentor_logic.logic.getForFields(
+          filter, unique=True)
+
+      if host_entity or org_admin_entity or mentor_entity:
+        validation, mentor_actions = self._constructMentorActions(
+            context, entity)
+        actions += mentor_actions
+        if entity.status in ['Unapproved', 'Unpublished', 'Open']:
+          if host_entity or org_admin_entity:
+            context['edit_link'] = redirects.getEditRedirect(entity, params)
+          elif mentor_entity:
+            context['suggest_link'] = ghop_redirects.getSuggestTaskRedirect(
+                entity, params)
+      else:
+        validation, student_actions = self._constructStudentActions(
+            context, entity, user_account)
+        actions += student_actions
+
+      # create the difficultly level field containing the choices 
+      # defined in the program entity
+      dynafields = [
+          {'name': 'action',
+           'base': forms.ChoiceField,
+           'label': 'Action',
+           'required': False,
+           'passthrough': ['initial', 'required', 'choices'],
+           'choices': actions,
+           },
+         ]
+
+      if validation == 'needs_review':
+        dynafields.append(
+            {'name': 'work_submission',
+             'base': forms.URLField,
+             'label': 'Submit Work',
+             'required': False,
+             'help_text': 'Provide a link to your work in this box. '
+                 'Please use the comment box if you need to explain '
+                 'of your work.',
+             })
+
+      if validation == 'close':
+        dynafields.append(
+            {'name': 'extended_deadline',
+             'base': forms.IntegerField,
+             'min_value': 1,
+             'label': 'Extend deadline by',
+             'required': False,
+             'passthrough': ['min_value', 'required', 'help_text'],
+             'help_text': 'Optional: Specify the number of hours by '
+                 'which you want to extend the deadline for the task '
+                 'for this student. ',
+             })
+
+      dynaproperties = params_helper.getDynaFields(dynafields)
+      if validation == 'needs_review':
+        dynaproperties['clean_work_submission'] = cleaning.clean_url(
+            'work_submission')
+
+      extended_comment_form = dynaform.extendDynaForm(
+          dynaform=params['comment_form'],
+          dynaproperties=dynaproperties)
+
+      params['comment_form'] = extended_comment_form
+    else:
+      # list of statuses a task can be in after it is requested to be
+      # claimed before closing or re-opening
+      claim_status = ['ClaimRequested', 'Claimed', 'ActionNeeded',
+                      'NeedsWork', 'NeedsReview']
+      if entity.status in claim_status:
+        context['header_msg'] = self.DEF_TASK_CLAIMED_MSG
+      elif entity.status in ['AwaitingRegistration', 'Closed']:
+        context['header_msg'] = self.DEF_TASK_CLOSED_MSG
+
+      context['signin_comment_msg'] = self.DEF_SIGNIN_TO_COMMENT_MSG % (
+          context['sign_in'])
+
+    return validation
+
+  def _constructMentorActions(self, context, entity):
+    """Constructs the list of actions for mentors, org admins and
+    hosts.
+    """
+
+    # variable that holds what kind of validation this user
+    # and task combination pass.
+    validation = None
+
+    actions = []
+
+    if entity.status in ['Unapproved', 'Unpublished']:
+      context['header_msg'] = self.DEF_TASK_UNPUBLISHED_MSG
+      context['comment_disabled'] = True
+    elif entity.status == 'Open':
+      context['header_msg'] = self.DEF_CAN_EDIT_TASK_MSG
+    elif entity.status == 'Reopened':
+      context['header_msg'] = self.DEF_TASK_MENTOR_REOPENED_MSG
+    elif entity.status == 'ClaimRequested':
+      actions.extend([('accept', 'Accept claim request'),
+                      ('reject', 'Reject claim request')])
+      context['header_msg'] = self.DEF_TASK_CLAIM_REQUESTED_MSG
+      validation = 'accept_claim'
+    elif entity.status == 'Claimed':
+      context['header_msg'] = self.DEF_TASK_CLAIMED_BY_STUDENT_MSG
+    elif entity.status == 'NeedsReview':
+      context['header_msg'] = self.DEF_TASK_NEEDS_REVIEW_MSG
+      actions.extend([('needs_work', 'Needs More Work'),
+                      ('reopened', 'Reopen the task'),
+                      ('closed', 'Close the task')])
+      validation = 'close'
+    elif entity.status in ['AwaitingRegistration', 'Closed']:
+      context['header_msg'] = self.DEF_TASK_CLOSED_MSG
+
+    return validation, actions
+
+  def _constructStudentActions(self, context, entity, user_account):
+    """Constructs the list of actions for students.
+    """
+
+    # variable that holds what kind of validation this user
+    # and task combination pass.
+    validation = None
+
+    actions = []
+
+    if entity.status in ['Open', 'Reopened']:
+      task_filter = {
+          'user': user_account,
+          'status': ['ClaimRequested', 'Claimed', 'ActionNeeded', 
+                     'NeedsWork', 'NeedsReview']
+          }
+      task_entities = ghop_task_logic.logic.getForFields(task_filter)
+
+      if len(task_entities) >= entity.program.nr_simultaneous_tasks:
+        context['header_msg'] = self.DEF_MAX_TASK_LIMIT_MSG_FMT % (
+            entity.program.nr_simultaneous_tasks)
+        validation = 'claim_ineligible'
+        return validation, actions
+
+      task_filter['status'] = 'AwaitingRegistration'
+      task_entities = ghop_task_logic.logic.getForFields(task_filter)
+
+      if task_entities:
+        context['header_msg'] = self.DEF_AWAITING_REG_MSG
+        validation = 'claim_ineligible'
+      else:
+        actions.append(('request', 'Request to claim the task'))
+        validation = 'claim_request'
+
+    # TODO: lot of double information here that can be simplified
+    if entity.user and user_account.key() == entity.user.key():
+      if entity.status  == 'ClaimRequested':
+        context['header_msg'] = self.DEF_TASK_REQ_CLAIMED_BY_YOU_MSG
+        actions.append(('withdraw', 'Withdraw from the task'))
+        validation = 'claim_withdraw'
+      elif entity.status in ['Claimed', 'NeedsWork', 
+                             'NeedsReview', 'ActionNeeded']:
+        context['header_msg'] = self.DEF_TASK_CLAIMED_BY_YOU_MSG
+        actions.extend([
+            ('withdraw', 'Withdraw from the task'),
+            ('needs_review', 'Submit work and Request for review')])
+        validation = 'needs_review'
+      elif entity.status == 'NeedsReview':
+        context['header_msg'] = self.DEF_TASK_NO_MORE_SUBMIT_MSG
+        actions.append(('withdraw', 'Withdraw from the task'))
+        if datetime.datetime.now < entity.deadline:
+          actions.append(
+              ('needs_review', 'Submit work and Request for review'))
+        validation = 'needs_review'
+      elif entity.status == 'AwaitingRegistration':
+        context['header_msg'] = self.DEF_STUDENT_SIGNUP_MSG
+      elif entity.status == 'Closed':
+        context['header_msg'] = self.DEF_TASK_CMPLTD_BY_YOU_MSG
+    else:
+      if entity.status in ['ClaimRequested', 'Claimed', 
+                           'ActionNeeded', 'NeedsWork',
+                           'NeedsReview']:
+        context['header_msg'] = self.DEF_TASK_CLAIMED_MSG
+      if entity.status in ['AwaitingRegistration', 'Closed']:
+        context['header_msg'] = self.DEF_TASK_CLOSED_MSG
+
+    return validation, actions
+
+  @decorators.merge_params
+  @decorators.check_access
+  def search(self, request, access_type, page_name=None,
+             params=None, filter=None, order=None,**kwargs):
+    """View method to search for GHOP Tasks.
+
+    Args:
+      request: the standard Django HTTP request object
+      access_type : the name of the access type which should be checked
+      page_name: the page name displayed in templates as page and header title
+      params: a dict with params for this View
+      kwargs: the Key Fields for the specified entity
+    """
+
+    from soc.modules.ghop.views.helper import list_info as list_info_helper
+
+    get_params = request.GET
+
+    contents = []
+    context = {}
+    if not filter:
+      filter = {}
+
+    public_status = ['Open', 'Reopened', 'ClaimRequested', 'Claimed',
+                     'ActionNeeded', 'Closed', 'AwaitingRegistration',
+                     'NeedsWork', 'NeedsReview']
+
+    task_params = params.copy()
+    task_params['list_template'] = 'modules/ghop/task/search/search.html'
+    task_params['list_heading'] = 'modules/ghop/task/search/heading.html'
+    task_params['list_row'] = 'modules/ghop/task/search/row.html'
+
+    task_params['list_action'] = (redirects.getPublicRedirect,
+                                  task_params)
+
+    task_params['list_description'] = ugettext(
+       'Search results: ')
+
+    program_entity = ghop_program_logic.logic.getFromKeyFields(kwargs)
+
+    org_fields = {
+        'scope': program_entity,
+        }
+
+    org_entities = ghop_org_logic.logic.getForFields(org_fields)
+    org_names = []
+    for org in org_entities:
+      org_names.append(org.name)
+
+    df_entities = ghop_task_model.TaskDifficultyTag.get_by_scope(
+        program_entity)
+    difficulties = []
+    for df_entity in df_entities:
+      difficulties.append(df_entity.tag)
+
+    tt_entities = ghop_task_model.TaskTypeTag.get_by_scope(program_entity)
+    task_types = []
+    for tt_entity in tt_entities:
+      task_types.append(tt_entity.tag)
+
+    context['org_entities'] = org_names
+    context['public_status'] = public_status
+    context['difficulties'] =  difficulties
+    context['tags'] = task_types
+
+    org_filter = get_params.getlist('Organization')
+    status_filter = get_params.getlist('Status')
+    df_filter = get_params.getlist('Difficulty')
+    tag_filter = get_params.getlist('Tags')
+
+    if org_filter:
+      org_fields = {
+        'scope': program_entity,
+        'name': org_filter,
+      }
+      org_entities = ghop_org_logic.logic.getForFields(org_fields)
+      filter['scope'] = org_entities
+    if status_filter:
+      filter['status']= status_filter
+    else:
+      filter['status'] = public_status
+    if df_filter:
+      filter['difficulty'] = df_filter
+    if tag_filter:
+      filter['task_type'] = tag_filter
+
+    filter['program'] = program_entity
+
+    task_list = lists.getListContent(request, task_params, filter,
+                                     order=order, idx=0, need_content=True)
+
+    if task_list:
+      contents.append(task_list)
+
+    # call the _list method from base to display the list
+    return self._list(request, task_params, contents, page_name, context)
+
+
+view = View()
+
+create = decorators.view(view.create)
+delete = decorators.view(view.delete)
+edit = decorators.view(view.edit)
+list = decorators.view(view.list)
+list_org_tasks = decorators.view(view.listOrgTasks)
+suggest_task = decorators.view(view.suggestTask)
+public = decorators.view(view.public)
+search = decorators.view(view.search)