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