app/soc/modules/ghop/views/models/task.py
changeset 2885 f064654837f7
child 2893 1adc6a815c71
equal deleted inserted replaced
2884:2be8f6b8379e 2885:f064654837f7
       
     1 #!/usr/bin/python2.5
       
     2 #
       
     3 # Copyright 2009 the Melange authors.
       
     4 #
       
     5 # Licensed under the Apache License, Version 2.0 (the "License");
       
     6 # you may not use this file except in compliance with the License.
       
     7 # You may obtain a copy of the License at
       
     8 #
       
     9 #   http://www.apache.org/licenses/LICENSE-2.0
       
    10 #
       
    11 # Unless required by applicable law or agreed to in writing, software
       
    12 # distributed under the License is distributed on an "AS IS" BASIS,
       
    13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
    14 # See the License for the specific language governing permissions and
       
    15 # limitations under the License.
       
    16 
       
    17 """Views for Tasks.
       
    18 """
       
    19 
       
    20 __authors__ = [
       
    21     '"Madhusudan.C.S" <madhusudancs@gmail.com>'
       
    22   ]
       
    23 
       
    24 
       
    25 import datetime
       
    26 import time
       
    27 
       
    28 from google.appengine.ext import db
       
    29 
       
    30 from django import forms
       
    31 from django import http
       
    32 from django.utils.translation import ugettext
       
    33 
       
    34 from soc.logic import cleaning
       
    35 from soc.logic import dicts
       
    36 from soc.logic.models import host as host_logic 
       
    37 from soc.logic.models import student as student_logic
       
    38 from soc.logic.models import user as user_logic
       
    39 from soc.views import helper
       
    40 from soc.views import out_of_band
       
    41 from soc.views.helper import decorators
       
    42 from soc.views.helper import dynaform
       
    43 from soc.views.helper import lists
       
    44 from soc.views.helper import params as params_helper
       
    45 from soc.views.helper import requests
       
    46 from soc.views.helper import redirects
       
    47 from soc.views.helper import responses
       
    48 from soc.views.helper import widgets
       
    49 from soc.views.models import base
       
    50 
       
    51 from soc.modules.ghop.logic import cleaning as ghop_cleaning
       
    52 from soc.modules.ghop.logic.models import comment as ghop_comment_logic
       
    53 from soc.modules.ghop.logic.models import mentor as ghop_mentor_logic
       
    54 from soc.modules.ghop.logic.models import organization as ghop_org_logic
       
    55 from soc.modules.ghop.logic.models import org_admin as ghop_org_admin_logic
       
    56 from soc.modules.ghop.logic.models import program as ghop_program_logic
       
    57 from soc.modules.ghop.logic.models import task as ghop_task_logic
       
    58 from soc.modules.ghop.models import task as ghop_task_model
       
    59 from soc.modules.ghop.views.helper import access
       
    60 from soc.modules.ghop.views.helper import redirects as ghop_redirects
       
    61 from soc.modules.ghop.views.models import organization as ghop_org_view
       
    62 from soc.modules.ghop.tasks import task_update
       
    63 
       
    64 import soc.modules.ghop.logic.models.task
       
    65 
       
    66 
       
    67 class View(base.View):
       
    68   """View methods for the Tasks.
       
    69   """
       
    70 
       
    71   DEF_AWAITING_REG_MSG = ugettext(
       
    72       'The task is open but you cannot claim this task since you '
       
    73       'have not completed the student signup after finishing your '
       
    74       'first task.')
       
    75 
       
    76   DEF_CAN_EDIT_TASK_MSG = ugettext(
       
    77       'The task can be edited by clicking on the edit link '
       
    78       'next to the title above.')
       
    79 
       
    80   DEF_MAX_TASK_LIMIT_MSG_FMT = ugettext(
       
    81       'The task is open but you cannot claim this task since you '
       
    82       'either have already claimed %d task.')
       
    83 
       
    84   DEF_SIGNIN_TO_COMMENT_MSG = ugettext(
       
    85       '<a href=%s>Sign in</a> to perform any action or comment on '
       
    86       'this task.')
       
    87 
       
    88   DEF_STUDENT_SIGNUP_MSG = ugettext(
       
    89       'You have successfully completed this task. Sign up as a student '
       
    90       'before you proceed further.')
       
    91 
       
    92   DEF_TASK_CLAIMED_BY_YOU_MSG = ugettext( 
       
    93       'This task has been claimed by you!')
       
    94 
       
    95   DEF_TASK_CLAIMED_BY_STUDENT_MSG = ugettext(
       
    96       'This task has been claimed by a student!')
       
    97 
       
    98   DEF_TASK_CLAIMED_MSG = ugettext(
       
    99       'The task is already claimed and the work is in progress.')
       
   100 
       
   101   DEF_TASK_CLAIM_REQUESTED_MSG = ugettext(
       
   102       'A student has requested to claim this task. Accept or '
       
   103       'Reject the request.')
       
   104 
       
   105   DEF_TASK_CLOSED_MSG = ugettext('The task is closed.')
       
   106 
       
   107   DEF_TASK_CMPLTD_BY_YOU_MSG = ugettext(
       
   108       'You have successfully completed this task!')
       
   109 
       
   110   DEF_TASK_NO_MORE_SUBMIT_MSG = ugettext(
       
   111       'You have submitted the work to this task, but deadline has passed '
       
   112       'You cannot submit any more work until your mentor extends the '
       
   113       'deadline.')
       
   114 
       
   115   DEF_TASK_MENTOR_REOPENED_MSG = ugettext(
       
   116       'The task has been reopened.')
       
   117 
       
   118   DEF_TASK_NEEDS_REVIEW_MSG = ugettext(
       
   119       'Student has submitted his work for this task! It needs review.')
       
   120 
       
   121   DEF_TASK_OPEN_MSG = ugettext(
       
   122       'This task is open. If you are GHOP student, you can claim it!')
       
   123 
       
   124   DEF_TASK_REOPENED_MSG = ugettext(
       
   125       'This task has been reopened. If you are a GHOP student, '
       
   126       'you can clam it!')
       
   127 
       
   128   DEF_TASK_REQ_CLAIMED_BY_YOU_MSG = ugettext(
       
   129       'You have requested to claim this task!')
       
   130 
       
   131   DEF_TASK_UNPUBLISHED_MSG = ugettext(
       
   132       'The task is not yet published. It can be edited by clicking on '
       
   133       'the edit button below.')
       
   134 
       
   135   DEF_WS_MSG_FMT = ugettext(
       
   136       '(To see the work submitted <a href=#ws%d>click here</a>.)')
       
   137 
       
   138   def __init__(self, params=None):
       
   139     """Defines the fields and methods required for the task View class
       
   140     to provide the user with the necessary views.
       
   141 
       
   142     Params:
       
   143       params: a dict with params for this View
       
   144     """
       
   145 
       
   146     rights = access.GHOPChecker(params)
       
   147     # TODO: create and suggest_task don't need access checks which use state
       
   148     # also feels like roles are being checked twice?
       
   149     rights['create'] = [
       
   150         ('checkCanOrgAdminOrMentorEdit', ['scope_path', True]),
       
   151         ('checkRoleAndStatusForTask',
       
   152             [['ghop/org_admin'], ['active'], 
       
   153             []])]
       
   154     rights['edit'] = [
       
   155         ('checkCanOrgAdminOrMentorEdit', ['scope_path', False]),
       
   156         ('checkRoleAndStatusForTask',
       
   157             [['ghop/org_admin'], ['active'], 
       
   158             ['Unapproved', 'Unpublished', 'Open']])]
       
   159     rights['delete'] = ['checkIsDeveloper']
       
   160     rights['show'] = ['checkStatusForTask']
       
   161     rights['list_org_tasks'] = [
       
   162         ('checkCanOrgAdminOrMentorEdit', ['scope_path', False])]
       
   163     rights['suggest_task'] = [
       
   164         ('checkCanOrgAdminOrMentorEdit', ['scope_path', True]),
       
   165         ('checkRoleAndStatusForTask',
       
   166             [['ghop/org_admin', 'ghop/mentor'], ['active'], 
       
   167             []])]
       
   168     rights['search'] = ['allow']
       
   169 
       
   170     new_params = {}
       
   171     new_params['logic'] = soc.modules.ghop.logic.models.task.logic
       
   172     new_params['rights'] = rights
       
   173 
       
   174     new_params['name'] = "Task"
       
   175     new_params['module_name'] = "task"
       
   176     new_params['sidebar_grouping'] = 'Tasks'
       
   177 
       
   178     new_params['module_package'] = 'soc.modules.ghop.views.models'
       
   179     new_params['url_name'] = 'ghop/task'
       
   180 
       
   181     new_params['scope_view'] = ghop_org_view
       
   182     new_params['scope_redirect'] = redirects.getCreateRedirect
       
   183 
       
   184     new_params['list_heading'] = 'modules/ghop/task/list/heading.html' 
       
   185     new_params['list_row'] = 'modules/ghop/task/list/row.html'
       
   186 
       
   187     new_params['extra_dynaexclude'] = ['task_type', 'mentors', 'user',
       
   188                                        'student', 'program', 'status', 
       
   189                                        'deadline', 'created_by',
       
   190                                        'created_on', 'modified_by',
       
   191                                        'modified_on', 'history',
       
   192                                        'link_id', 'difficulty']
       
   193 
       
   194     patterns = []
       
   195     patterns += [
       
   196         (r'^%(url_name)s/(?P<access_type>suggest_task)/%(scope)s$',
       
   197         '%(module_package)s.%(module_name)s.suggest_task',
       
   198         'Mentors suggest %(name)s'),
       
   199         (r'^%(url_name)s/(?P<access_type>suggest_task)/%(key_fields)s$',
       
   200         '%(module_package)s.%(module_name)s.suggest_task',
       
   201         'Mentors edit a %(name)s'),
       
   202         (r'^%(url_name)s/(?P<access_type>list_org_tasks)/%(scope)s$',
       
   203         '%(module_package)s.%(module_name)s.list_org_tasks',
       
   204         'List Organization %(name)s'),
       
   205         (r'^%(url_name)s/(?P<access_type>search)/'
       
   206          '(?P<scope_path>%(ulnp)s)/%(lnp)s$',
       
   207         '%(module_package)s.%(module_name)s.search',
       
   208         'Search for %(name)ss'),
       
   209         ]
       
   210 
       
   211     new_params['extra_django_patterns'] = patterns
       
   212 
       
   213     new_params['create_dynafields'] = [
       
   214         {'name': 'arbit_tags',
       
   215          'base': forms.fields.CharField,
       
   216          'label': 'Arbitrary Tags',
       
   217          'required': False,
       
   218          'group': ugettext('Tags'),
       
   219          'help_text': ugettext(
       
   220              'Enter arbitrary Tags for this task separated by comma.\n'
       
   221              '<b>Note:</b> Tag names are case sensitive. If the tag is same '
       
   222              'as the program mandated tag, it will be considered as a '
       
   223              'mandatory tag.')
       
   224          },
       
   225         ]
       
   226 
       
   227     new_params['create_extra_dynaproperties'] = {
       
   228         'description': forms.fields.CharField(required=True,
       
   229             widget=widgets.FullTinyMCE(attrs={'rows': 25, 'cols': 100})),
       
   230         'scope_path': forms.CharField(widget=forms.HiddenInput,
       
   231             required=True),
       
   232         'time_to_complete': forms.IntegerField(min_value=1,
       
   233                                                required=True),
       
   234         'clean_description': cleaning.clean_html_content('description'),
       
   235         'clean_arbit_tags': cleaning.str2set('arbit_tags'),
       
   236         }
       
   237 
       
   238     new_params['edit_extra_dynaproperties'] = {
       
   239         'link_id': forms.CharField(widget=forms.HiddenInput)
       
   240         }
       
   241 
       
   242     new_params['public_template'] = 'modules/ghop/task/public.html'
       
   243 
       
   244     params = dicts.merge(params, new_params, sub_merge=True)
       
   245 
       
   246     super(View, self).__init__(params=params)
       
   247 
       
   248     # holds the base form for the task creation and editing
       
   249     self._params['base_create_form'] = self._params['create_form']
       
   250     self._params['base_edit_form'] = self._params['edit_form'] 
       
   251 
       
   252     # extend create and edit form for org admins
       
   253     dynafields = [
       
   254         {'name': 'mentors_list',
       
   255          'required': True,
       
   256          'base': forms.fields.CharField,
       
   257          'label': 'Assign mentors',
       
   258          'help_text': 'Assign mentors to this task by '
       
   259              'giving their link_ids separated by comma.',
       
   260          },
       
   261         {'name': 'published',
       
   262          'required': False,
       
   263          'initial': False,
       
   264          'base': forms.fields.BooleanField,
       
   265          'label': 'Publish the task',
       
   266          'help_text': ugettext('By ticking this box, the task will be'
       
   267                                'made public and can be claimed by students.'),
       
   268          }
       
   269         ]
       
   270 
       
   271     dynaproperties = params_helper.getDynaFields(dynafields)
       
   272 
       
   273     dynaproperties['clean_mentors_list'] = ghop_cleaning.cleanMentorsList(
       
   274         'mentors_list')
       
   275 
       
   276     create_form = dynaform.extendDynaForm(
       
   277         dynaform=self._params['create_form'],
       
   278         dynaproperties=dynaproperties)
       
   279 
       
   280     self._params['create_form'] = create_form
       
   281 
       
   282     edit_form = dynaform.extendDynaForm(
       
   283         dynaform=self._params['edit_form'],
       
   284         dynaproperties=dynaproperties)
       
   285 
       
   286     self._params['edit_form'] = edit_form
       
   287 
       
   288     # create the comment form
       
   289     dynafields = [
       
   290         {'name': 'comment',
       
   291          'base': forms.CharField,
       
   292          'widget': widgets.FullTinyMCE(attrs={'rows': 10, 'cols': 40}),
       
   293          'label': 'Comment',
       
   294          'required': False,
       
   295          'example_text': 'Comment is optional.<br/>'
       
   296              'Choose an appropriate Action below and Save your '
       
   297              'changes.<br /><br />Caution, you will not be able '
       
   298              'to edit your comment!',
       
   299          },
       
   300          ]
       
   301 
       
   302     dynaproperties = params_helper.getDynaFields(dynafields)
       
   303     dynaproperties['clean_comment'] = cleaning.clean_html_content('comment')
       
   304     dynaproperties['clean'] = ghop_cleaning.clean_comment(
       
   305         'comment', 'action', 'work_submission')
       
   306 
       
   307     comment_form = dynaform.newDynaForm(dynamodel=None, 
       
   308         dynabase=helper.forms.BaseForm, dynainclude=None, 
       
   309         dynaexclude=None, dynaproperties=dynaproperties)
       
   310     self._params['comment_form'] = comment_form
       
   311 
       
   312   def _getTagsForProgram(self, form_name, params, **kwargs):
       
   313     """Extends form dynamically from difficulty levels in program entity.
       
   314 
       
   315     Args:
       
   316      form_name: the Form entry in params to extend
       
   317      params: the params for the view
       
   318     """
       
   319 
       
   320     # obtain program_entity using scope_path which holds 
       
   321     # the org_entity key_name
       
   322     org_entity = ghop_org_logic.logic.getFromKeyName(kwargs['scope_path'])
       
   323     program_entity = ghop_program_logic.logic.getFromKeyName(
       
   324         org_entity.scope_path)
       
   325 
       
   326     # get a list difficulty levels stored for the program entity
       
   327     tds = ghop_task_model.TaskDifficultyTag.get_by_scope(
       
   328         program_entity) 
       
   329 
       
   330     difficulties = []
       
   331     for td in tds:
       
   332       difficulties.append((td.tag, td.tag))
       
   333 
       
   334     # get a list of task type tags stored for the program entity
       
   335     tts = ghop_task_model.TaskTypeTag.get_by_scope(program_entity) 
       
   336 
       
   337     type_tags = []
       
   338     for tt in tts:
       
   339       type_tags.append((tt.tag, tt.tag))
       
   340 
       
   341     # create the difficultly level field containing the choices 
       
   342     # defined in the program entity
       
   343     dynafields = [
       
   344         {'name': 'difficulty',
       
   345          'base': forms.ChoiceField,
       
   346          'label': 'Difficulty level',
       
   347          'required': True,
       
   348          'passthrough': ['initial', 'required', 'choices',
       
   349                          'label', 'help_text'],
       
   350          'choices': difficulties,
       
   351          'group': ugettext('Tags'),
       
   352          'help_text': ugettext('Difficulty Level of this task.'),
       
   353          },
       
   354          {'name': 'type_tags',
       
   355          'base': forms.MultipleChoiceField,
       
   356          'label': 'Task Types',
       
   357          'required': True,
       
   358          'passthrough': ['initial', 'required', 'choices',
       
   359                          'label', 'help_text'],
       
   360          'choices': type_tags,
       
   361          'group': ugettext('Tags'),
       
   362          'help_text': ugettext('Task Type tags mandated by the program. You '
       
   363                                'must select one or more of them.'),
       
   364          },
       
   365        ]
       
   366 
       
   367     dynaproperties = params_helper.getDynaFields(dynafields)
       
   368 
       
   369     extended_form = dynaform.extendDynaForm(
       
   370         dynaform=params[form_name],
       
   371         dynaproperties=dynaproperties)
       
   372 
       
   373     return extended_form
       
   374 
       
   375   @decorators.check_access
       
   376   def create(self, request, access_type,
       
   377              page_name=None, params=None, **kwargs):
       
   378     """Replaces the create Form with the dynamic one.
       
   379 
       
   380     For args see base.View.create().
       
   381     """
       
   382 
       
   383     params = dicts.merge(params, self._params)
       
   384 
       
   385     # extend create_form to include difficulty levels
       
   386     params['create_form'] = self._getTagsForProgram(
       
   387         'create_form', params, **kwargs)
       
   388 
       
   389     return super(View, self).create(request, 'allow', page_name=page_name,
       
   390                                     params=params, **kwargs)
       
   391 
       
   392   @decorators.merge_params
       
   393   @decorators.check_access
       
   394   def edit(self, request, access_type,
       
   395            page_name=None, params=None, seed=None, **kwargs):
       
   396     """See base.View.edit().
       
   397     """
       
   398 
       
   399     logic = params['logic']
       
   400 
       
   401     context = helper.responses.getUniversalContext(request)
       
   402     helper.responses.useJavaScript(context, params['js_uses_all'])
       
   403     context['page_name'] = page_name
       
   404 
       
   405     try:
       
   406       entity = logic.getFromKeyFieldsOr404(kwargs)
       
   407     except out_of_band.Error, error:
       
   408       msg = self.DEF_CREATE_NEW_ENTITY_MSG_FMT % {
       
   409           'entity_type_lower' : params['name'].lower(),
       
   410           'entity_type' : params['name'],
       
   411           'create' : params['missing_redirect']
       
   412           }
       
   413       error.message_fmt = error.message_fmt + msg
       
   414       return helper.responses.errorResponse(
       
   415           error, request, context=context)
       
   416 
       
   417     user_account = user_logic.logic.getForCurrentAccount()
       
   418 
       
   419     filter = {
       
   420         'user': user_account,
       
   421         }
       
   422 
       
   423     # extend edit_form to include difficulty levels
       
   424     params['edit_form'] = self._getTagsForProgram(
       
   425         'edit_form', params, **kwargs)
       
   426 
       
   427     org_admin_entities = ghop_org_admin_logic.logic.getForFields(filter) 
       
   428 
       
   429     if entity and entity.status == 'Unapproved':
       
   430       approval_req = True 
       
   431       for org_admin_entity in org_admin_entities:
       
   432         if org_admin_entity.key().name() == entity.created_by.key().name():
       
   433           approval_req = False
       
   434           break
       
   435 
       
   436       if approval_req:
       
   437         dynafields = [
       
   438             {'name': 'approved',
       
   439              'required': False,
       
   440              'initial': False,
       
   441              'base': forms.fields.BooleanField,
       
   442              'label': 'Approve the task',
       
   443              'help_text': 'By ticking this box, the task will be'
       
   444                  'will be approved for publishing.',
       
   445             }
       
   446             ]
       
   447 
       
   448         dynaproperties = params_helper.getDynaFields(dynafields)
       
   449 
       
   450         edit_form = dynaform.extendDynaForm(
       
   451             dynaform=params['edit_form'],
       
   452             dynaproperties=dynaproperties)
       
   453 
       
   454         params['edit_form'] = edit_form
       
   455 
       
   456     if request.method == 'POST':
       
   457       return self.editPost(request, entity, context, params=params)
       
   458     else:
       
   459       return self.editGet(request, entity, context, params=params)
       
   460 
       
   461   def _editGet(self, request, entity, form):
       
   462     """See base.View._editGet().
       
   463     """
       
   464 
       
   465     if entity.task_type:
       
   466       form.fields['type_tags'].initial = entity.tags_string(
       
   467           entity.task_type, ret_list=True)
       
   468     if entity.arbit_tag:
       
   469       form.fields['arbit_tags'].initial = entity.tags_string(
       
   470           entity.arbit_tag)
       
   471 
       
   472     if entity.difficulty:
       
   473       form.fields['difficulty'].initial = entity.tags_string( 
       
   474           entity.difficulty)
       
   475 
       
   476     if entity.mentors and 'mentors_list' in form.fields:
       
   477       mentor_entities = db.get(entity.mentors)
       
   478       mentors_list = []
       
   479       for mentor in mentor_entities:
       
   480         mentors_list.append(mentor.link_id)
       
   481       form.fields['mentors_list'].initial = ', '.join(mentors_list)
       
   482 
       
   483     form.fields['link_id'].initial = entity.link_id
       
   484 
       
   485     # checks if the task is already approved or not and sets
       
   486     # the form approved field
       
   487     if 'approved' in form.fields:
       
   488       if entity.status == 'Unapproved':
       
   489         form.fields['approved'].initial = False
       
   490       else:
       
   491         form.fields['approved'].initial = True
       
   492  
       
   493     # checks if the task is already published or not and sets
       
   494     # the form published field 
       
   495     if 'published' in form.fields:
       
   496       if entity.status == 'Unapproved' or entity.status == 'Unpublished':
       
   497         form.fields['published'].initial = False
       
   498       else:
       
   499         form.fields['published'].initial = True
       
   500 
       
   501     return super(View, self)._editGet(request, entity, form)
       
   502 
       
   503   def _editPost(self, request, entity, fields):
       
   504     """See base._editPost().
       
   505     """
       
   506 
       
   507     super(View, self)._editPost(request, entity, fields)
       
   508 
       
   509     # TODO: this method can be made more clear, it seems a bit of a mess
       
   510 
       
   511     if not entity:
       
   512       program_entity = fields['scope'].scope
       
   513       fields['program'] = program_entity
       
   514     else:
       
   515       program_entity = entity.program 
       
   516 
       
   517     user_account = user_logic.logic.getForCurrentAccount()
       
   518 
       
   519     filter = {
       
   520         'user': user_account,
       
   521         'scope': fields['scope'],
       
   522         'status': 'active'
       
   523         }
       
   524 
       
   525     role_entity = ghop_org_admin_logic.logic.getForFields(
       
   526         filter, unique=True)
       
   527 
       
   528     if role_entity:
       
   529       # this user can publish/approve the task
       
   530       if fields.get('approved') and fields.get('published'):
       
   531         fields['status'] = 'Open'
       
   532       elif not fields.get('approved'):
       
   533         fields['status'] = 'Unapproved'
       
   534       else:
       
   535         fields['status'] = 'Unpublished'
       
   536 
       
   537       fields['mentors'] = []
       
   538       if fields.get('mentors_list'):
       
   539 
       
   540         for mentor_link_id in fields['mentors_list']:
       
   541           properties = {
       
   542               'scope_path': fields['scope_path'],
       
   543               'link_id': mentor_link_id,
       
   544               }
       
   545 
       
   546           mentor_entity = ghop_mentor_logic.logic.getFromKeyFields(properties)
       
   547           fields['mentors'].append(mentor_entity.key())
       
   548     else:
       
   549       role_entity = ghop_mentor_logic.logic.getForFields(
       
   550           filter, unique=True)
       
   551       if not entity:
       
   552         # creating a new task
       
   553         fields['status'] = 'Unapproved'
       
   554 
       
   555     # explicitly change the last_modified_on since the content has been edited
       
   556     fields['modified_on'] = datetime.datetime.now()
       
   557 
       
   558     if not entity:
       
   559       fields['link_id'] = 't%i' % (int(time.time()*100))
       
   560       fields['modified_by'] = role_entity
       
   561       fields['created_by'] = role_entity 
       
   562       fields['created_on'] = datetime.datetime.now()
       
   563     else:
       
   564       fields['link_id'] = entity.link_id
       
   565       fields['modified_by'] = role_entity
       
   566       if 'status' not in fields:
       
   567         fields['status'] = entity.status
       
   568 
       
   569     fields['difficulty'] = {
       
   570         'tags': fields['difficulty'],
       
   571         'scope': program_entity
       
   572         }
       
   573 
       
   574     fields['task_type'] = {
       
   575         'tags': fields['type_tags'],
       
   576         'scope': program_entity
       
   577         }
       
   578 
       
   579     fields['arbit_tag'] = {
       
   580         'tags': fields['arbit_tags'],
       
   581         'scope': program_entity
       
   582         }
       
   583 
       
   584     return
       
   585 
       
   586   @decorators.merge_params
       
   587   @decorators.check_access
       
   588   def suggestTask(self, request, access_type, page_name=None, 
       
   589                   params=None, **kwargs):
       
   590     """View used to allow mentors to create or edit a task. 
       
   591 
       
   592     Tasks created by mentors must be approved by org admins
       
   593     before they are published.
       
   594     """
       
   595 
       
   596     params = dicts.merge(params, self._params)
       
   597 
       
   598     if 'link_id' in kwargs:
       
   599       # extend create_form to include difficulty levels
       
   600       params['mentor_form'] = self._getTagsForProgram(
       
   601           'base_edit_form', params, **kwargs)
       
   602       try:
       
   603         entity = self._logic.getFromKeyFieldsOr404(kwargs)
       
   604       except out_of_band.Error, error:
       
   605         return helper.responses.errorResponse(
       
   606             error, request, template=params['error_public'])
       
   607     else:
       
   608       # extend create_form to include difficulty levels
       
   609       params['mentor_form'] = self._getTagsForProgram(
       
   610           'base_create_form', params, **kwargs)
       
   611       entity = None
       
   612 
       
   613     context = helper.responses.getUniversalContext(request)
       
   614     helper.responses.useJavaScript(context, params['js_uses_all'])
       
   615     context['page_name'] = page_name
       
   616 
       
   617     if request.method == 'POST':
       
   618       return self.suggestTaskPost(request, context,
       
   619                                   params, entity)
       
   620     else:
       
   621       return self.suggestTaskGet(request, context,
       
   622                                  params, entity, **kwargs)
       
   623 
       
   624   def suggestTaskPost(self, request, context, params, entity):
       
   625     """Handles the POST request for the suggest task view.
       
   626     """
       
   627 
       
   628     form = params['mentor_form'](request.POST)
       
   629 
       
   630     if not form.is_valid():
       
   631       return self._constructResponse(request, None, context, form, params)
       
   632 
       
   633     _, fields = helper.forms.collectCleanedFields(form)
       
   634     # TODO: this non-standard view shouldn't call _editPost but its own method
       
   635     self._editPost(request, entity, fields)
       
   636 
       
   637     logic = params['logic']
       
   638     if entity:
       
   639       entity = logic.updateEntityProperties(entity, fields)
       
   640     else:
       
   641       entity = logic.updateOrCreateFromFields(fields)
       
   642 
       
   643     page_params = params['edit_params']
       
   644 
       
   645     redirect = ghop_redirects.getSuggestTaskRedirect(
       
   646         entity, params)
       
   647 
       
   648     return http.HttpResponseRedirect(redirect)
       
   649 
       
   650   def suggestTaskGet(self, request, context, params, entity, **kwargs):
       
   651     """Handles the GET request for the suggest task view.
       
   652     """
       
   653 
       
   654     if entity:
       
   655       # populate form with the existing entity
       
   656       form = params['mentor_form'](instance=entity)
       
   657 
       
   658       self._editGet(request, entity, form)
       
   659     else:
       
   660       form = params['mentor_form']()
       
   661 
       
   662     return self._constructResponse(request, entity, context, form, params)
       
   663 
       
   664   @decorators.merge_params
       
   665   @decorators.check_access
       
   666   def listOrgTasks(self, request, access_type,
       
   667                    page_name=None, params=None, **kwargs):
       
   668     """See base.View.list()
       
   669     """
       
   670     if request.method == 'POST':
       
   671       return self.listOrgTasksPost(request, params, **kwargs)
       
   672     else: # request.method == 'GET'
       
   673       return self.listOrgTasksGet(request, page_name, params, **kwargs)
       
   674 
       
   675   def listOrgTasksPost(self, request, params, **kwargs):
       
   676     """Handles the POST request for the list tasks view.
       
   677     """
       
   678 
       
   679     # update the status of task entities that have been approved
       
   680     # and published
       
   681     task_entities = []
       
   682     for key_name, published in request.POST.items():
       
   683       task_entity = ghop_task_logic.logic.getFromKeyName(key_name)
       
   684       if task_entity:
       
   685         task_entity.status = 'Open'
       
   686 
       
   687         task_entities.append(task_entity)
       
   688 
       
   689     # bulk update the task_entities
       
   690     # TODO: Have to be replaced by Task Queue APIs later
       
   691     db.put(task_entities)
       
   692 
       
   693     # redirect to the same page
       
   694     return http.HttpResponseRedirect('')
       
   695 
       
   696   def listOrgTasksGet(self, request, page_name, params, **kwargs):
       
   697     """Handles the GET request for the list tasks view.
       
   698     """
       
   699 
       
   700     from soc.modules.ghop.views.helper import list_info as list_info_helper
       
   701 
       
   702     org_entity =  ghop_org_logic.logic.getFromKeyNameOr404(
       
   703         kwargs['scope_path'])
       
   704 
       
   705     contents = []
       
   706     context = {}
       
   707 
       
   708     user_account = user_logic.logic.getForCurrentAccount()
       
   709 
       
   710     fields = {
       
   711         'user': user_account,
       
   712         'scope': org_entity,
       
   713         'status': 'active'
       
   714         }
       
   715 
       
   716     up_params = params.copy()
       
   717     # give the capability to approve tasks for the org_admins
       
   718     if ghop_org_admin_logic.logic.getForFields(fields, unique=True):
       
   719       up_params['list_template'] = 'modules/ghop/task/approve/approve.html'
       
   720       up_params['list_heading'] = 'modules/ghop/task/approve/heading.html'
       
   721       up_params['list_row'] = 'modules/ghop/task/approve/row.html'
       
   722 
       
   723     up_params['list_action'] = (redirects.getPublicRedirect,
       
   724                                 up_params)
       
   725 
       
   726     up_params['list_description'] = ugettext(
       
   727        'List of Unapproved or Unpublished tasks')
       
   728 
       
   729     filter = {
       
   730         'scope': org_entity,
       
   731         'status': ['Unapproved', 'Unpublished'],
       
   732         }
       
   733 
       
   734     up_list = lists.getListContent(request, up_params, filter,
       
   735                                    order=order, idx=0, 
       
   736                                    need_content=True)
       
   737 
       
   738     if up_list:
       
   739       up_mentors_list = {}
       
   740       for task_entity in up_list['data']:
       
   741         up_mentors_list[task_entity.key()] = db.get(task_entity.mentors)
       
   742 
       
   743       up_list['info'] = (list_info_helper.getTasksInfo(up_mentors_list), None)
       
   744 
       
   745       contents.append(up_list)
       
   746       context['up_list'] = True
       
   747 
       
   748     ap_params = up_params.copy()
       
   749     ap_params['list_template'] = 'soc/models/list.html'
       
   750     ap_params['list_heading'] = 'modules/ghop/task/list/heading.html'
       
   751     ap_params['list_row'] = 'modules/ghop/task/list/row.html'
       
   752 
       
   753     ap_params['list_action'] = (redirects.getPublicRedirect,
       
   754                                 ap_params)
       
   755 
       
   756     ap_params['list_description'] = ugettext(
       
   757        'List of published tasks')
       
   758 
       
   759     filter = {
       
   760         'scope': org_entity,
       
   761         'status': ['Open', 'Reopened', 'ClaimRequested', 'Claimed',
       
   762             'ActionNeeded', 'Closed', 'AwaitingRegistration', 
       
   763             'NeedsWork', 'NeedsReview'],
       
   764         }
       
   765 
       
   766     ap_list = lists.getListContent(request, ap_params, filter,
       
   767                                    order=order, idx=1, 
       
   768                                    need_content=True)
       
   769 
       
   770     if ap_list:
       
   771       ap_mentors_list = {}
       
   772       for task_entity in ap_list['data']:
       
   773         ap_mentors_list[task_entity.key()] = db.get(task_entity.mentors)
       
   774 
       
   775       ap_list['info'] = (list_info_helper.getTasksInfo(ap_mentors_list), None)
       
   776 
       
   777       contents.append(ap_list)
       
   778 
       
   779     # call the _list method from base to display the list
       
   780     return self._list(request, up_params, contents, page_name, context)
       
   781 
       
   782   @decorators.merge_params
       
   783   @decorators.check_access
       
   784   def public(self, request, access_type,
       
   785              page_name=None, params=None, **kwargs):
       
   786     """See base.View.public().
       
   787     """
       
   788 
       
   789     # create default template context for use with any templates
       
   790     context = helper.responses.getUniversalContext(request)
       
   791     helper.responses.useJavaScript(context, params['js_uses_all'])
       
   792     context['page_name'] = page_name
       
   793     entity = None
       
   794     logic = params['logic']
       
   795 
       
   796     try:
       
   797       entity, comment_entities, ws_entities = (
       
   798           logic.getFromKeyFieldsWithCWSOr404(kwargs))
       
   799     except out_of_band.Error, error:
       
   800       return helper.responses.errorResponse(
       
   801           error, request, template=params['error_public'], context=context)
       
   802 
       
   803     if entity.status in ['Claimed', 'NeedsReview',
       
   804                          'ActionNeeded', 'NeedsWork']:
       
   805       entity, comment_entity, ws_entity = (
       
   806           ghop_task_logic.logic.updateTaskStatus(entity))
       
   807       if comment_entity:
       
   808         comment_entities.append(comment_entity)
       
   809       if ws_entity:
       
   810         ws_entities.append(ws_entity)
       
   811 
       
   812     context['entity'] = entity
       
   813     context['entity_key_name'] = entity.key().id_or_name() 
       
   814     context['entity_type'] = params['name']
       
   815     context['entity_type_url'] = params['url_name']
       
   816 
       
   817     user_account = user_logic.logic.getForCurrentAccount()
       
   818 
       
   819     # get some entity specific context
       
   820     self.updatePublicContext(context, entity, comment_entities,
       
   821                              ws_entities, user_account, params)
       
   822 
       
   823     validation = self._constructActionsList(context, entity, 
       
   824                                             user_account, params)
       
   825 
       
   826     context = dicts.merge(params['context'], context)
       
   827 
       
   828     if request.method == 'POST':
       
   829       return self.publicPost(request, context, params, entity, 
       
   830                              user_account, validation, **kwargs)
       
   831     else: # request.method == 'GET'
       
   832       return self.publicGet(request, context, params, entity,
       
   833                             user_account, **kwargs)
       
   834 
       
   835   def publicPost(self, request, context, params, entity,
       
   836                  user_account=None, validation=None, **kwargs):
       
   837     """Handles the POST request for the entity's public page.
       
   838 
       
   839     Args:
       
   840         entity: the task entity
       
   841         rest: see base.View.public()
       
   842     """
       
   843 
       
   844     from soc.modules.ghop.logic.models import student as ghop_student_logic
       
   845 
       
   846     form = params['comment_form'](request.POST)
       
   847 
       
   848     if not form.is_valid():
       
   849       template = params['public_template']
       
   850       context['comment_form'] = form
       
   851       return self._constructResponse(request, entity, context,
       
   852                                      form, params, template=template)
       
   853 
       
   854     _, fields = helper.forms.collectCleanedFields(form)
       
   855 
       
   856     changes = []
       
   857 
       
   858     action = fields['action']
       
   859 
       
   860     properties = None
       
   861     ws_properties = None
       
   862 
       
   863     # TODO: this can be separated into several methods that handle the changes
       
   864     if validation == 'claim_request' and action == 'request':
       
   865       deadline = datetime.datetime.now() + datetime.timedelta(
       
   866             hours=entity.time_to_complete)
       
   867 
       
   868       properties = {
       
   869           'status': 'ClaimRequested',
       
   870           'user': user_account,
       
   871           'deadline': deadline,
       
   872           }
       
   873 
       
   874       st_filter = {
       
   875           'user': user_account,
       
   876           'scope': entity.program,
       
   877           'status': 'active'
       
   878           }
       
   879       student_entity = ghop_student_logic.logic.getForFields(
       
   880           st_filter, unique=True)
       
   881 
       
   882       if student_entity:
       
   883         properties['student'] = student_entity
       
   884 
       
   885       changes.extend([ugettext('User-Student'), 
       
   886                       ugettext('Action-Claim Requested'), 
       
   887                       ugettext('Status-%s' % (properties['status']))
       
   888                       ])
       
   889     elif (validation == 'claim_withdraw' or 
       
   890         validation == 'needs_review') and action == 'withdraw':
       
   891       properties = {
       
   892           'user': None,
       
   893           'student': None,
       
   894           'status': 'Reopened',
       
   895           'deadline': None,
       
   896           }
       
   897 
       
   898       changes.extend([ugettext('User-Student'),
       
   899                       ugettext('Action-Withdrawn'),
       
   900                       ugettext('Status-%s' % (properties['status']))
       
   901                       ])
       
   902     elif validation == 'needs_review' and action == 'needs_review':
       
   903       properties = {
       
   904           'status': 'NeedsReview',
       
   905           }
       
   906 
       
   907       changes.extend([
       
   908           ugettext('User-Student'),
       
   909           ugettext('Action-Submitted work and Requested for review'),
       
   910           ugettext('Status-%s' % (properties['status']))])
       
   911 
       
   912       ws_properties = {
       
   913           'parent': entity,
       
   914           'link_id': 't%i' % (int(time.time()*100)),
       
   915           'scope_path': entity.key().name(),
       
   916           'scope': entity.scope,
       
   917           'user': user_account,
       
   918           'information': fields['comment'],
       
   919           'url_to_work': fields['work_submission'],
       
   920           'submitted_on': datetime.datetime.now(),
       
   921           }
       
   922     elif validation == 'accept_claim':
       
   923       if action == 'accept':
       
   924         deadline = datetime.datetime.now() + datetime.timedelta(
       
   925             hours=entity.time_to_complete)
       
   926 
       
   927         properties = {
       
   928             'status': 'Claimed',
       
   929             'deadline': deadline,
       
   930             }
       
   931 
       
   932         changes.extend([ugettext('User-Mentor'),
       
   933                         ugettext('Action-Claim Accepted'),
       
   934                         ugettext('Status-%s' % (properties['status']))
       
   935                         ])
       
   936 
       
   937         task_update.spawnUpdateTask(entity)
       
   938       if action == 'reject':
       
   939         properties = {
       
   940             'user': None,
       
   941             'student': None,
       
   942             'status': 'Reopened',
       
   943             'deadline': None,
       
   944             }
       
   945 
       
   946         changes.extend([ugettext('User-Mentor'),
       
   947                         ugettext('Action-Claim Rejected'),
       
   948                         ugettext('Status-%s' % (properties['status']))
       
   949                         ])
       
   950     elif validation == 'close':
       
   951       if action == 'needs_work':
       
   952         properties = {
       
   953             'status': 'NeedsWork',
       
   954             }
       
   955 
       
   956         changes.extend([ugettext('User-Mentor'),
       
   957                         ugettext('Action-Requested more work'),
       
   958                         ugettext('Status-%s' % (properties['status']))
       
   959                         ])
       
   960 
       
   961         if fields['extended_deadline'] > 0:
       
   962           deadline = entity.deadline + datetime.timedelta(
       
   963               hours=fields['extended_deadline'])
       
   964 
       
   965           properties['deadline'] = deadline
       
   966 
       
   967           changes.append(ugettext('DeadlineExtendedBy-%d hrs to %s' % (
       
   968               fields['extended_deadline'], deadline.strftime(
       
   969                   '%d %B %Y, %H :%M'))))
       
   970 
       
   971           task_update.spawnUpdateTask(entity)
       
   972         else:
       
   973           changes.append(ugettext('NoDeadlineExtensionGiven'))
       
   974       elif action == 'reopened':
       
   975         properties = {
       
   976           'user': None,
       
   977           'student': None,
       
   978           'status': 'Reopened',
       
   979           'deadline': None,
       
   980           }
       
   981 
       
   982         changes.extend([ugettext('User-Mentor'),
       
   983                         ugettext('Action-Reopened'),
       
   984                         ugettext('Status-%s' % (properties['status']))
       
   985                         ])
       
   986       elif action == 'closed':
       
   987         properties = {
       
   988             'deadline': None,
       
   989             }
       
   990 
       
   991         if entity.student:
       
   992           properties['status'] = 'Closed'
       
   993         else:
       
   994           properties['status'] = 'AwaitingRegistration'
       
   995 
       
   996         changes.extend([ugettext('User-Mentor'),
       
   997                         ugettext('Action-Closed the task'),
       
   998                         ugettext('Status-%s' % (properties['status']))
       
   999                         ])
       
  1000 
       
  1001     comment_properties = {
       
  1002         'parent': entity,
       
  1003         'scope_path': entity.key().name(),
       
  1004         'created_by': user_account,
       
  1005         'changes': changes,
       
  1006         }
       
  1007 
       
  1008     if ws_properties:
       
  1009       comment_properties['content'] = self.DEF_WS_MSG_FMT
       
  1010     else:
       
  1011       comment_properties['content'] = fields['comment']
       
  1012 
       
  1013     ghop_task_logic.logic.updateEntityPropertiesWithCWS(
       
  1014         entity, properties, comment_properties, ws_properties)
       
  1015 
       
  1016     # redirect to the same page
       
  1017     return http.HttpResponseRedirect('')
       
  1018 
       
  1019   def publicGet(self, request, context, params, entity,
       
  1020                 user_account, **kwargs):
       
  1021     """Handles the GET request for the entity's public page.
       
  1022 
       
  1023     Args:
       
  1024         entity: the task entity
       
  1025         rest see base.View.public()
       
  1026     """
       
  1027 
       
  1028     context['comment_form'] = params['comment_form']()
       
  1029 
       
  1030     template = params['public_template']
       
  1031 
       
  1032     return responses.respond(request, template, context=context)
       
  1033 
       
  1034   def updatePublicContext(self, context, entity, comment_entities,
       
  1035                           ws_entities, user_account, params):
       
  1036     """Updates the context for the public page with information.
       
  1037 
       
  1038     Args:
       
  1039       context: the context that should be updated
       
  1040       entity: a task used to set context
       
  1041       user_account: user entity of the logged in user
       
  1042       params: dict with params for the view using this context
       
  1043     """
       
  1044 
       
  1045     mentor_entities = db.get(entity.mentors)
       
  1046     mentors_str = ""
       
  1047     for mentor in mentor_entities:
       
  1048       mentors_str += mentor.name() + ", "
       
  1049 
       
  1050     if mentors_str:
       
  1051       context['mentors_str'] = mentors_str[:-2]
       
  1052     else:
       
  1053       context['mentors_str'] = "Not Assigned" 
       
  1054 
       
  1055     context['difficulty_str'] = entity.tags_string(entity.difficulty)
       
  1056 
       
  1057     context['task_type_str'] = entity.tags_string(entity.task_type)
       
  1058 
       
  1059     if entity.deadline:
       
  1060       # TODO: it should be able to abuse Django functionality for this
       
  1061       ttc = entity.deadline - datetime.datetime.now()
       
  1062       (ttc_min, ttc_hour) = ((ttc.seconds / 60), (ttc.seconds / 3600))
       
  1063       if ttc_min >= 60:
       
  1064         ttc_min = ttc_min % 60
       
  1065       if ttc_hour > 1:
       
  1066         if ttc_min == 0:
       
  1067           ttc_str = '%d hours' % (ttc_hour)
       
  1068         else:
       
  1069           ttc_str = '%d:%02d hours' % (ttc_hour, ttc_min)
       
  1070         if ttc.days == 1:
       
  1071           ttc_str = '%d day, %s' % (ttc.days, ttc_str)
       
  1072         elif ttc.days > 1:
       
  1073           ttc_str = '%d days, %s' % (ttc.days, ttc_str)
       
  1074       else:
       
  1075         ttc_str = '%d mins' % (ttc_min)
       
  1076       context['time_to_complete'] = ttc_str
       
  1077     else:
       
  1078       if entity.status == 'NeedsReview':
       
  1079         context['time_to_complete'] = 'No Time Left'
       
  1080       else:
       
  1081         context['time_to_complete'] = '%d hours' % (entity.time_to_complete)
       
  1082 
       
  1083     context['comments'] = comment_entities
       
  1084 
       
  1085     context['work_submissions'] = ws_entities
       
  1086 
       
  1087   def _constructActionsList(self, context, entity,
       
  1088                             user_account, params):
       
  1089     """Constructs a list of actions for the task page and extends
       
  1090     the comment form with this list.
       
  1091 
       
  1092     This method also returns the validation used by POST method to 
       
  1093     validate the user input data.
       
  1094 
       
  1095     Args:
       
  1096       context: the context that should be updated
       
  1097       entity: a task used to set context
       
  1098       user_account: user entity of the logged in user
       
  1099       params: dict with params for the view using this context
       
  1100     """
       
  1101 
       
  1102     # variable that holds what kind of validation this user
       
  1103     # and task combination pass.
       
  1104     validation = None
       
  1105 
       
  1106     # The following header messages are shown for non-logged in
       
  1107     # general public, logged in public and the student.
       
  1108     if entity.status is 'Closed':
       
  1109       context['header_msg'] = self.DEF_TASK_CLOSED_MSG
       
  1110       validation = 'closed'
       
  1111 
       
  1112     if entity.status == 'Open':
       
  1113       context['header_msg'] = self.DEF_TASK_OPEN_MSG
       
  1114     elif entity.status == 'Reopened':
       
  1115       context['header_msg'] = self.DEF_TASK_REOPENED_MSG
       
  1116 
       
  1117     if user_account:
       
  1118       actions = [('noaction', 'Comment without action')]
       
  1119 
       
  1120       # if the user is logged give him the permission to claim
       
  1121       # the task only if he none of program host, org admin or mentor
       
  1122       filter = {
       
  1123           'user': user_account,
       
  1124           }
       
  1125 
       
  1126       host_entity = host_logic.logic.getForFields(filter, unique=True)
       
  1127 
       
  1128       filter['scope_path'] = entity.scope_path
       
  1129       org_admin_entity = ghop_org_admin_logic.logic.getForFields(
       
  1130           filter, unique=True)
       
  1131       mentor_entity = ghop_mentor_logic.logic.getForFields(
       
  1132           filter, unique=True)
       
  1133 
       
  1134       if host_entity or org_admin_entity or mentor_entity:
       
  1135         validation, mentor_actions = self._constructMentorActions(
       
  1136             context, entity)
       
  1137         actions += mentor_actions
       
  1138         if entity.status in ['Unapproved', 'Unpublished', 'Open']:
       
  1139           if host_entity or org_admin_entity:
       
  1140             context['edit_link'] = redirects.getEditRedirect(entity, params)
       
  1141           elif mentor_entity:
       
  1142             context['suggest_link'] = ghop_redirects.getSuggestTaskRedirect(
       
  1143                 entity, params)
       
  1144       else:
       
  1145         validation, student_actions = self._constructStudentActions(
       
  1146             context, entity, user_account)
       
  1147         actions += student_actions
       
  1148 
       
  1149       # create the difficultly level field containing the choices 
       
  1150       # defined in the program entity
       
  1151       dynafields = [
       
  1152           {'name': 'action',
       
  1153            'base': forms.ChoiceField,
       
  1154            'label': 'Action',
       
  1155            'required': False,
       
  1156            'passthrough': ['initial', 'required', 'choices'],
       
  1157            'choices': actions,
       
  1158            },
       
  1159          ]
       
  1160 
       
  1161       if validation == 'needs_review':
       
  1162         dynafields.append(
       
  1163             {'name': 'work_submission',
       
  1164              'base': forms.URLField,
       
  1165              'label': 'Submit Work',
       
  1166              'required': False,
       
  1167              'help_text': 'Provide a link to your work in this box. '
       
  1168                  'Please use the comment box if you need to explain '
       
  1169                  'of your work.',
       
  1170              })
       
  1171 
       
  1172       if validation == 'close':
       
  1173         dynafields.append(
       
  1174             {'name': 'extended_deadline',
       
  1175              'base': forms.IntegerField,
       
  1176              'min_value': 1,
       
  1177              'label': 'Extend deadline by',
       
  1178              'required': False,
       
  1179              'passthrough': ['min_value', 'required', 'help_text'],
       
  1180              'help_text': 'Optional: Specify the number of hours by '
       
  1181                  'which you want to extend the deadline for the task '
       
  1182                  'for this student. ',
       
  1183              })
       
  1184 
       
  1185       dynaproperties = params_helper.getDynaFields(dynafields)
       
  1186       if validation == 'needs_review':
       
  1187         dynaproperties['clean_work_submission'] = cleaning.clean_url(
       
  1188             'work_submission')
       
  1189 
       
  1190       extended_comment_form = dynaform.extendDynaForm(
       
  1191           dynaform=params['comment_form'],
       
  1192           dynaproperties=dynaproperties)
       
  1193 
       
  1194       params['comment_form'] = extended_comment_form
       
  1195     else:
       
  1196       # list of statuses a task can be in after it is requested to be
       
  1197       # claimed before closing or re-opening
       
  1198       claim_status = ['ClaimRequested', 'Claimed', 'ActionNeeded',
       
  1199                       'NeedsWork', 'NeedsReview']
       
  1200       if entity.status in claim_status:
       
  1201         context['header_msg'] = self.DEF_TASK_CLAIMED_MSG
       
  1202       elif entity.status in ['AwaitingRegistration', 'Closed']:
       
  1203         context['header_msg'] = self.DEF_TASK_CLOSED_MSG
       
  1204 
       
  1205       context['signin_comment_msg'] = self.DEF_SIGNIN_TO_COMMENT_MSG % (
       
  1206           context['sign_in'])
       
  1207 
       
  1208     return validation
       
  1209 
       
  1210   def _constructMentorActions(self, context, entity):
       
  1211     """Constructs the list of actions for mentors, org admins and
       
  1212     hosts.
       
  1213     """
       
  1214 
       
  1215     # variable that holds what kind of validation this user
       
  1216     # and task combination pass.
       
  1217     validation = None
       
  1218 
       
  1219     actions = []
       
  1220 
       
  1221     if entity.status in ['Unapproved', 'Unpublished']:
       
  1222       context['header_msg'] = self.DEF_TASK_UNPUBLISHED_MSG
       
  1223       context['comment_disabled'] = True
       
  1224     elif entity.status == 'Open':
       
  1225       context['header_msg'] = self.DEF_CAN_EDIT_TASK_MSG
       
  1226     elif entity.status == 'Reopened':
       
  1227       context['header_msg'] = self.DEF_TASK_MENTOR_REOPENED_MSG
       
  1228     elif entity.status == 'ClaimRequested':
       
  1229       actions.extend([('accept', 'Accept claim request'),
       
  1230                       ('reject', 'Reject claim request')])
       
  1231       context['header_msg'] = self.DEF_TASK_CLAIM_REQUESTED_MSG
       
  1232       validation = 'accept_claim'
       
  1233     elif entity.status == 'Claimed':
       
  1234       context['header_msg'] = self.DEF_TASK_CLAIMED_BY_STUDENT_MSG
       
  1235     elif entity.status == 'NeedsReview':
       
  1236       context['header_msg'] = self.DEF_TASK_NEEDS_REVIEW_MSG
       
  1237       actions.extend([('needs_work', 'Needs More Work'),
       
  1238                       ('reopened', 'Reopen the task'),
       
  1239                       ('closed', 'Close the task')])
       
  1240       validation = 'close'
       
  1241     elif entity.status in ['AwaitingRegistration', 'Closed']:
       
  1242       context['header_msg'] = self.DEF_TASK_CLOSED_MSG
       
  1243 
       
  1244     return validation, actions
       
  1245 
       
  1246   def _constructStudentActions(self, context, entity, user_account):
       
  1247     """Constructs the list of actions for students.
       
  1248     """
       
  1249 
       
  1250     # variable that holds what kind of validation this user
       
  1251     # and task combination pass.
       
  1252     validation = None
       
  1253 
       
  1254     actions = []
       
  1255 
       
  1256     if entity.status in ['Open', 'Reopened']:
       
  1257       task_filter = {
       
  1258           'user': user_account,
       
  1259           'status': ['ClaimRequested', 'Claimed', 'ActionNeeded', 
       
  1260                      'NeedsWork', 'NeedsReview']
       
  1261           }
       
  1262       task_entities = ghop_task_logic.logic.getForFields(task_filter)
       
  1263 
       
  1264       if len(task_entities) >= entity.program.nr_simultaneous_tasks:
       
  1265         context['header_msg'] = self.DEF_MAX_TASK_LIMIT_MSG_FMT % (
       
  1266             entity.program.nr_simultaneous_tasks)
       
  1267         validation = 'claim_ineligible'
       
  1268         return validation, actions
       
  1269 
       
  1270       task_filter['status'] = 'AwaitingRegistration'
       
  1271       task_entities = ghop_task_logic.logic.getForFields(task_filter)
       
  1272 
       
  1273       if task_entities:
       
  1274         context['header_msg'] = self.DEF_AWAITING_REG_MSG
       
  1275         validation = 'claim_ineligible'
       
  1276       else:
       
  1277         actions.append(('request', 'Request to claim the task'))
       
  1278         validation = 'claim_request'
       
  1279 
       
  1280     # TODO: lot of double information here that can be simplified
       
  1281     if entity.user and user_account.key() == entity.user.key():
       
  1282       if entity.status  == 'ClaimRequested':
       
  1283         context['header_msg'] = self.DEF_TASK_REQ_CLAIMED_BY_YOU_MSG
       
  1284         actions.append(('withdraw', 'Withdraw from the task'))
       
  1285         validation = 'claim_withdraw'
       
  1286       elif entity.status in ['Claimed', 'NeedsWork', 
       
  1287                              'NeedsReview', 'ActionNeeded']:
       
  1288         context['header_msg'] = self.DEF_TASK_CLAIMED_BY_YOU_MSG
       
  1289         actions.extend([
       
  1290             ('withdraw', 'Withdraw from the task'),
       
  1291             ('needs_review', 'Submit work and Request for review')])
       
  1292         validation = 'needs_review'
       
  1293       elif entity.status == 'NeedsReview':
       
  1294         context['header_msg'] = self.DEF_TASK_NO_MORE_SUBMIT_MSG
       
  1295         actions.append(('withdraw', 'Withdraw from the task'))
       
  1296         if datetime.datetime.now < entity.deadline:
       
  1297           actions.append(
       
  1298               ('needs_review', 'Submit work and Request for review'))
       
  1299         validation = 'needs_review'
       
  1300       elif entity.status == 'AwaitingRegistration':
       
  1301         context['header_msg'] = self.DEF_STUDENT_SIGNUP_MSG
       
  1302       elif entity.status == 'Closed':
       
  1303         context['header_msg'] = self.DEF_TASK_CMPLTD_BY_YOU_MSG
       
  1304     else:
       
  1305       if entity.status in ['ClaimRequested', 'Claimed', 
       
  1306                            'ActionNeeded', 'NeedsWork',
       
  1307                            'NeedsReview']:
       
  1308         context['header_msg'] = self.DEF_TASK_CLAIMED_MSG
       
  1309       if entity.status in ['AwaitingRegistration', 'Closed']:
       
  1310         context['header_msg'] = self.DEF_TASK_CLOSED_MSG
       
  1311 
       
  1312     return validation, actions
       
  1313 
       
  1314   @decorators.merge_params
       
  1315   @decorators.check_access
       
  1316   def search(self, request, access_type, page_name=None,
       
  1317              params=None, filter=None, order=None,**kwargs):
       
  1318     """View method to search for GHOP Tasks.
       
  1319 
       
  1320     Args:
       
  1321       request: the standard Django HTTP request object
       
  1322       access_type : the name of the access type which should be checked
       
  1323       page_name: the page name displayed in templates as page and header title
       
  1324       params: a dict with params for this View
       
  1325       kwargs: the Key Fields for the specified entity
       
  1326     """
       
  1327 
       
  1328     from soc.modules.ghop.views.helper import list_info as list_info_helper
       
  1329 
       
  1330     get_params = request.GET
       
  1331 
       
  1332     contents = []
       
  1333     context = {}
       
  1334     if not filter:
       
  1335       filter = {}
       
  1336 
       
  1337     public_status = ['Open', 'Reopened', 'ClaimRequested', 'Claimed',
       
  1338                      'ActionNeeded', 'Closed', 'AwaitingRegistration',
       
  1339                      'NeedsWork', 'NeedsReview']
       
  1340 
       
  1341     task_params = params.copy()
       
  1342     task_params['list_template'] = 'modules/ghop/task/search/search.html'
       
  1343     task_params['list_heading'] = 'modules/ghop/task/search/heading.html'
       
  1344     task_params['list_row'] = 'modules/ghop/task/search/row.html'
       
  1345 
       
  1346     task_params['list_action'] = (redirects.getPublicRedirect,
       
  1347                                   task_params)
       
  1348 
       
  1349     task_params['list_description'] = ugettext(
       
  1350        'Search results: ')
       
  1351 
       
  1352     program_entity = ghop_program_logic.logic.getFromKeyFields(kwargs)
       
  1353 
       
  1354     org_fields = {
       
  1355         'scope': program_entity,
       
  1356         }
       
  1357 
       
  1358     org_entities = ghop_org_logic.logic.getForFields(org_fields)
       
  1359     org_names = []
       
  1360     for org in org_entities:
       
  1361       org_names.append(org.name)
       
  1362 
       
  1363     df_entities = ghop_task_model.TaskDifficultyTag.get_by_scope(
       
  1364         program_entity)
       
  1365     difficulties = []
       
  1366     for df_entity in df_entities:
       
  1367       difficulties.append(df_entity.tag)
       
  1368 
       
  1369     tt_entities = ghop_task_model.TaskTypeTag.get_by_scope(program_entity)
       
  1370     task_types = []
       
  1371     for tt_entity in tt_entities:
       
  1372       task_types.append(tt_entity.tag)
       
  1373 
       
  1374     context['org_entities'] = org_names
       
  1375     context['public_status'] = public_status
       
  1376     context['difficulties'] =  difficulties
       
  1377     context['tags'] = task_types
       
  1378 
       
  1379     org_filter = get_params.getlist('Organization')
       
  1380     status_filter = get_params.getlist('Status')
       
  1381     df_filter = get_params.getlist('Difficulty')
       
  1382     tag_filter = get_params.getlist('Tags')
       
  1383 
       
  1384     if org_filter:
       
  1385       org_fields = {
       
  1386         'scope': program_entity,
       
  1387         'name': org_filter,
       
  1388       }
       
  1389       org_entities = ghop_org_logic.logic.getForFields(org_fields)
       
  1390       filter['scope'] = org_entities
       
  1391     if status_filter:
       
  1392       filter['status']= status_filter
       
  1393     else:
       
  1394       filter['status'] = public_status
       
  1395     if df_filter:
       
  1396       filter['difficulty'] = df_filter
       
  1397     if tag_filter:
       
  1398       filter['task_type'] = tag_filter
       
  1399 
       
  1400     filter['program'] = program_entity
       
  1401 
       
  1402     task_list = lists.getListContent(request, task_params, filter,
       
  1403                                      order=order, idx=0, need_content=True)
       
  1404 
       
  1405     if task_list:
       
  1406       contents.append(task_list)
       
  1407 
       
  1408     # call the _list method from base to display the list
       
  1409     return self._list(request, task_params, contents, page_name, context)
       
  1410 
       
  1411 
       
  1412 view = View()
       
  1413 
       
  1414 create = decorators.view(view.create)
       
  1415 delete = decorators.view(view.delete)
       
  1416 edit = decorators.view(view.edit)
       
  1417 list = decorators.view(view.list)
       
  1418 list_org_tasks = decorators.view(view.listOrgTasks)
       
  1419 suggest_task = decorators.view(view.suggestTask)
       
  1420 public = decorators.view(view.public)
       
  1421 search = decorators.view(view.search)