blob: c5957192c5c9b2612f5c923089b05a9ec3e7928f [file] [log] [blame]
# Copyright 2014 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.
"""Module with slot allocation related views."""
import collections
from django import forms as django_forms
from django import http
from django.utils import html
from django.utils import translation
from melange.request import access
from melange.request import exception
from soc.modules.gsoc.views import base as gsoc_base
from soc.modules.gsoc.views import forms
from soc.modules.gsoc.views.helper import url_patterns as soc_url_patterns
from soc.views import base
from soc.views import template
from soc.views.helper import url_patterns
from summerofcode.logic import proposal as proposal_logic
from summerofcode.logic import slot_transfer as slot_transfer_logic
from summerofcode.models import slot_transfer as slot_transfer_model
from summerofcode.request import error
from summerofcode.request import links
from summerofcode.request import render
from summerofcode.templates import tabs
from summerofcode.views.helper import urls as soc_urls
SLOT_ALLOCATION_DETAILS_PAGE_NAME = translation.ugettext(
'Slot Allocation Details')
SLOT_ALLOCATION_LABEL = translation.ugettext('Slot allocation')
SLOT_USAGE_LABEL = translation.ugettext('Used slots')
SLOT_DIFFERENCE_LABEL = translation.ugettext('Difference')
SLOT_TRANSFER_FORM_NAME = 'slot-transfer'
QUANTITY_LABEL = translation.ugettext('Number of slots')
QUANTITY_HELP_TEXT = translation.ugettext(
'Number of slots your organization is willing to give back to the pool.')
ORG_MESSAGE_LABEL = translation.ugettext('Message')
ORG_MESSAGE_HELP_TEXT = translation.ugettext(
'A brief explanation mentioning the reason for transferring the '
'slots back to the pool. It may also contain recommendation to which '
'organization the slots should be given.')
_SLOT_TRANSFER_SUMMARY_TEMPLATE_PATH = (
'summerofcode/slot_allocation/_slot_transfer_summary.html')
_MESSAGE_SLOT_ALLOCATION_NOT_VISIBLE = translation.ugettext(
'Slot allocation is not visible at this time.')
class SlotTransferForm(forms.GSoCModelForm):
"""Django form to request slot transfer."""
quantity = django_forms.IntegerField(
label=QUANTITY_LABEL, help_text=QUANTITY_HELP_TEXT)
org_message = django_forms.CharField(
widget=django_forms.Textarea(), required=False,
label=ORG_MESSAGE_LABEL, help_text=ORG_MESSAGE_HELP_TEXT)
Meta = object
def _slotTransferForm(data):
"""Returns a Django form to request a slot transfer to the pool.
Returns:
SlotTransferForm adjusted to request a slot transfer to the pool.
"""
form = SlotTransferForm(data=data.POST, name=SLOT_TRANSFER_FORM_NAME)
form.fields['quantity'].widget = django_forms.Select(
choices=[(i, i) for i in range(data.url_ndb_org.slot_allocation + 1)])
return form
_SLOT_TRANSFER_STATUS_ENUM_TO_VERBOSE_MAP = {
slot_transfer_model.Status.ACCEPTED: translation.ugettext('Accepted'),
slot_transfer_model.Status.REJECTED: translation.ugettext('Rejected'),
slot_transfer_model.Status.PENDING: translation.ugettext('Pending'),
}
class SlotTransferSummary(template.Template):
"""Template to render summary for a single slot transfer request."""
def __init__(self, data, template_path, slot_transfer):
"""Initializes a new instance of the template.
Args:
data: request_data.RequestData for the current request.
template_path: The path of the template to be used.
slot_transfer: slot_transfer.SlotTransfer entity to include summary for.
"""
super(SlotTransferSummary, self).__init__(data)
self.template_path = template_path
self.slot_transfer = slot_transfer
def templatePath(self):
"""See template.Template.templatePath for specification."""
return self._template_path
def context(self):
"""See template.Template.context for specification."""
return {
'status': _SLOT_TRANSFER_STATUS_ENUM_TO_VERBOSE_MAP[
self.slot_transfer.status],
'created_on': self.slot_transfer.created_on,
'quantity': self.slot_transfer.quantity,
'org_message': self.slot_transfer.org_message,
}
class SlotAllocationVisibleAccessChecker(access.AccessChecker):
"""AccessChecker that ensures that the access is granted only after the
slot allocation is visible for the program defined in the URL.
"""
def checkAccess(self, data, check):
"""See access.AccessChecker.checkAccess for specification."""
if not data.program.allocations_visible:
raise exception.Forbidden(message=_MESSAGE_SLOT_ALLOCATION_NOT_VISIBLE)
class SlotAllocationDetailsPage(base.RequestHandler):
"""Page for organization administrators to see slot allocation details."""
access_checker = access.ConjuctionAccessChecker([
access.IS_USER_ORG_ADMIN_FOR_NDB_ORG,
SlotAllocationVisibleAccessChecker()])
def __init__(self, initializer, linker, renderer, error_handler,
url_pattern_constructor, url_names, template_path):
"""Initializes a new instance of the request handler for the specified
parameters.
Args:
initializer: Implementation of initialize.Initializer interface.
linker: Instance of links.Linker class.
renderer: Implementation of render.Renderer interface.
error_handler: Implementation of error.ErrorHandler interface.
url_pattern_constructor:
Implementation of url_patterns.UrlPatternConstructor.
url_names: Instance of url_names.UrlNames.
template_path: The path of the template to be used.
"""
super(SlotAllocationDetailsPage, self).__init__(
initializer, linker, renderer, error_handler)
self.url_pattern_constructor = url_pattern_constructor
self.url_names = url_names
self.template_path = template_path
def djangoURLPatterns(self):
"""See base.RequestHandler.djangoURLPatterns for specification."""
return [
self.url_pattern_constructor.construct(
r'slot_allocation/details/%s$' % url_patterns.ORG,
self, name=self.url_names.SLOT_ALLOCATION_DETAILS),
]
def templatePath(self):
"""See base.GCIRequestHandler.templatePath for specification."""
return self.template_path
def context(self, data, check, mutator):
"""See base.RequestHandler.context for specification."""
slot_transfers = [
SlotTransferSummary(
data, _SLOT_TRANSFER_SUMMARY_TEMPLATE_PATH, slot_transfer)
for slot_transfer in slot_transfer_logic.getSlotTransfersForOrg(
data.url_ndb_org.key)]
slot_transfer_form = (
_slotTransferForm(data)
if not slot_transfer_logic.hasPendingSlotTransfer(data.url_ndb_org.key)
else None)
used_slots = proposal_logic.getUsedSlots(
data.url_ndb_org.key, data.program_timeline)
difference = data.url_ndb_org.slot_allocation - used_slots
summary_items = collections.OrderedDict()
summary_items[SLOT_ALLOCATION_LABEL] = data.url_ndb_org.slot_allocation
summary_items[SLOT_USAGE_LABEL] = used_slots
if difference == 0:
summary_items[SLOT_DIFFERENCE_LABEL] = html.mark_safe(
'<font color="green"><strong>%s</strong></font>' % difference)
elif difference > 0:
summary_items[SLOT_DIFFERENCE_LABEL] = html.mark_safe(
'<font color="red"><strong>%s remaining slots</strong></font>' %
difference)
else:
summary_items[SLOT_DIFFERENCE_LABEL] = html.mark_safe(
'<font color="red"><strong>%s too many slots used</strong></font>' %
difference)
return {
'page_name': SLOT_ALLOCATION_DETAILS_PAGE_NAME,
'items': summary_items,
'slot_transfer_form': slot_transfer_form,
'slot_transfers': slot_transfers,
'tabs': tabs.orgTabs(data, selected_tab_id=tabs.ORG_SLOTS_TAB_ID),
}
def post(self, data, check, mutator):
"""See base.RequestHandler.post for specification."""
slot_transfer_form = _slotTransferForm(data)
if slot_transfer_form.is_valid():
result = slot_transfer_logic.createSlotTransfer(
data.url_ndb_org.key, slot_transfer_form.cleaned_data['quantity'],
slot_transfer_form.cleaned_data['org_message'])
if not result:
raise exception.BadRequest(message=result.extra)
else:
return http.HttpResponseRedirect('')
else:
# TODO(nathaniel): problematic self-use.
return self.get(data, check, mutator)
SLOT_ALLOCATION_DETAILS_PAGE = SlotAllocationDetailsPage(
gsoc_base._GSOC_INITIALIZER, links.SOC_LINKER, render.SOC_RENDERER,
error.SOC_ERROR_HANDLER, soc_url_patterns.SOC_URL_PATTERN_CONSTRUCTOR,
soc_urls.UrlNames,
'summerofcode/slot_allocation/slot_allocation_details.html')