blob: 125f90f35b3fd1a2b10ea2c2c33eecd0aa9b08cf [file] [log] [blame]
# Copyright 2013 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 containing the views for Summer Of Code organization application."""
from django import forms as django_forms
from django.utils import translation
from melange.request import links
from melange.templates import readonly
from melange.views import organization as organization_view
from soc.views import forms
from soc.views import survey as survey_view
from soc.views import template
from soc.views.helper import url as url_helper
from soc.views.helper import lists
from soc.modules.gsoc.views import base
from soc.modules.gsoc.views import forms as gsoc_forms
from soc.modules.gsoc.views.helper import url_patterns as soc_url_patterns
from summerofcode.request import error
from summerofcode.request import render
from summerofcode.views.helper import urls
# TODO(daniel): fields should be explicitly included rather than excluded
# List of form fields which are not used by Summer Of Code programs
_UNUSED_FIELD_IDS = [
'notifications_email', organization_view.LOGO_IMAGE_FIELD_ID]
# Field IDs to create organization profiles used by Summer Of Code programs
_CREATE_ORG_PROFILE_FIELD_IDS = set([
'org_id', 'name', 'description', 'tags', 'license',
'ideas_page', 'mailing_list', 'web_page', 'irc_channel',
'feed_url', 'google_plus', 'twitter', 'blog', 'facebook', 'is_veteran',
'backup_admin', 'eligible_country'
])
# Field IDs to edit organization profiles used by Code In programs
_EDIT_ORG_PROFILE_FIELD_IDS = (
_CREATE_ORG_PROFILE_FIELD_IDS -
organization_view.ONLY_CREATE_ORG_PROFILE_FIELD_IDS)
SLOTS_REQUEST_MIN_HELP_TEXT = translation.ugettext(
'Number of amazing proposals submitted to this organization that '
'have a mentor assigned and the organization would <strong>really</strong> '
'like to have a slot for.')
SLOTS_REQUEST_MAX_HELP_TEXT = translation.ugettext(
'Number of slots that this organization would like to be assigned if '
'there was an unlimited amount of slots available.')
MAX_SCORE_HELP_TEXT = translation.ugettext(
'Mentors review proposals when student registration is open. This value '
'specifies the maximum number of points that can be given to a proposal by '
'one mentor. Please keep in mind changing this value does not have impact '
'on the existing scores. In particular, scores, which have been higher '
'than the updated maximum value, are still considered valid.')
CONTRIB_TEMPLATE_HELP_TEXT = translation.ugettext(
'This template can be used by contributors, such as students '
'and other non-member participants, when they apply to contribute '
'to the organization.')
SCORING_ENABLED_HELP_TEXT = translation.ugettext(
'When scoring is not enabled, mentors for the organization are not '
'allowed to score on proposals.')
ACCEPTED_STUDENTS_MESSAGE_HELP_TEXT = translation.ugettext(
'Custom message that is sent to all accepted students for '
'for this organization.')
REJECTED_STUDENTS_MESSAGE_HELP_TEXT = translation.ugettext(
'Custom message that is sent to all rejected students who submitted a '
'proposal to this organization.')
EXTRA_FIELDS_HELP_TEXT = translation.ugettext(
'Additional fields that will be added to proposals for this '
'organization. These fields are displayed as columns in the list of '
'proposals and may be edited by organization members. Field names should '
'appear on lines by themselves. Additionally, each name must be shorter '
'than %s characters')
SLOTS_REQUEST_MIN_LABEL = translation.ugettext('Min slots requested')
SLOTS_REQUEST_MAX_LABEL = translation.ugettext('Max slots requested')
MAX_SCORE_LABEL = translation.ugettext('Max score')
CONTRIB_TEMPLATE_LABEL = translation.ugettext('Application template')
SCORING_ENABLED_LABEL = translation.ugettext('Scoring enabled')
ACCEPTED_STUDENTS_MESSAGE_LABEL = translation.ugettext(
'Message to accepted students')
REJECTED_STUDENTS_MESSAGE_LABEL = translation.ugettext(
'Message to rejected students')
EXTRA_FIELDS_LABEL = translation.ugettext(
'Proposal extra fields')
EXTRA_FIELD_TOO_LONG = translation.ugettext(
'Extra field %s is too long: %s. Please make sure that all names are '
'shorter than %s characters.')
ORGANIZATION_LIST_DESCRIPTION = 'List of organizations'
_ORG_PREFERENCES_PROPERTIES_FIELD_KEYS = [
'max_score', 'slot_request_max', 'slot_request_min', 'contrib_template',
'scoring_enabled', 'extra_fields']
_ORG_MESSAGES_PROPERTIES_FIELD_KEYS = [
'accepted_students_message', 'rejected_students_message']
EXTRA_FIELD_MAX_LENGTH = 25
MAX_SCORE_MIN_VALUE = 1
MAX_SCORE_MAX_VALUE = 12
def cleanExtraFields(text):
"""Cleans extra_fields field.
Args:
text: The submitted value which is an arbitrary string.
Returns:
A list of extra fields.
Raises:
django_forms.ValidationError if at least one of the fields is not valid.
"""
extra_fields = []
for field in [field for field in text.split('\n') if field]:
field = field.strip()
if len(field) > EXTRA_FIELD_MAX_LENGTH:
raise django_forms.ValidationError(
EXTRA_FIELD_TOO_LONG % (field, len(field), EXTRA_FIELD_MAX_LENGTH))
else:
extra_fields.append(field)
return extra_fields
class SOCOrgPreferencesForm(organization_view.OrgPreferencesForm):
"""Form to set preferences of organization by organization administrators."""
slot_request_min = django_forms.IntegerField(
label=SLOTS_REQUEST_MIN_LABEL, help_text=SLOTS_REQUEST_MIN_HELP_TEXT,
required=True)
slot_request_max = django_forms.IntegerField(
label=SLOTS_REQUEST_MAX_LABEL, help_text=SLOTS_REQUEST_MAX_HELP_TEXT,
required=True)
max_score = django_forms.IntegerField(
label=MAX_SCORE_LABEL, help_text=MAX_SCORE_HELP_TEXT,
min_value=MAX_SCORE_MIN_VALUE, max_value=MAX_SCORE_MAX_VALUE)
contrib_template = django_forms.CharField(
widget=django_forms.Textarea, label=CONTRIB_TEMPLATE_LABEL,
help_text=CONTRIB_TEMPLATE_HELP_TEXT, required=False)
scoring_enabled = django_forms.BooleanField(
label=SCORING_ENABLED_LABEL, help_text=SCORING_ENABLED_HELP_TEXT,
required=False)
accepted_students_message = django_forms.CharField(
widget=django_forms.Textarea, label=ACCEPTED_STUDENTS_MESSAGE_LABEL,
help_text=ACCEPTED_STUDENTS_MESSAGE_HELP_TEXT, required=False)
rejected_students_message = django_forms.CharField(
widget=django_forms.Textarea, label=REJECTED_STUDENTS_MESSAGE_LABEL,
help_text=REJECTED_STUDENTS_MESSAGE_HELP_TEXT, required=False)
extra_fields = django_forms.CharField(
widget=django_forms.Textarea, label=EXTRA_FIELDS_LABEL,
help_text=EXTRA_FIELDS_HELP_TEXT % EXTRA_FIELD_MAX_LENGTH, required=False)
def __init__(self, **kwargs):
"""Initializes a new instance of the form for the specified parameters."""
super(SOCOrgPreferencesForm, self).__init__(
gsoc_forms.GSoCBoundField, template_path=gsoc_forms.TEMPLATE_PATH,
org_properties_field_keys=_ORG_PREFERENCES_PROPERTIES_FIELD_KEYS,
org_messages_properties_field_keys=_ORG_MESSAGES_PROPERTIES_FIELD_KEYS,
**kwargs)
def clean_extra_fields(self):
"""Cleans extra_fields field.
Returns:
A list of submitted extra fields.
Raises:
django_forms.ValidationError if at least one of the fields is not valid.
"""
return cleanExtraFields(self.cleaned_data['extra_fields'])
class SOCOrgLogoFormFactory(organization_view.OrgLogoFormFactory):
"""Implementation of organization_view.OrgLogoFormFactory for Summer
Of Code."""
def create(self, **kwargs):
return organization_view.OrgLogoForm(
gsoc_forms.GSoCBoundField, template_path=gsoc_forms.TEMPLATE_PATH,
**kwargs)
SOC_ORG_LOGO_FORM_FACTORY = SOCOrgLogoFormFactory()
class SOCOrgPreferencesFormFactory(organization_view.OrgPreferencesFormFactory):
"""Implementation of organization_view.OrgPreferencesFormFactory to be used
to create forms for organization preferences for Summer Of Code programs.
"""
def create(self, **kwargs):
"""See organization_view.OrgPreferencesFormFactory.create for
specification.
"""
return SOCOrgPreferencesForm(**kwargs)
SOC_ORG_PREFERENCES_FORM_FACTORY = SOCOrgPreferencesFormFactory()
class SOCCreateOrgProfileFormFactory(organization_view.OrgProfileFormFactory):
"""Implementation of organization_view.OrgProfileFormFactory to be used
to create forms to create organization profiles for Summer Of Code programs.
"""
def create(self, request_data, **kwargs):
"""See organization_view.OrgProfileFormFactory.create for specification."""
return organization_view._OrgProfileForm(
gsoc_forms.GSoCBoundField, request_data,
template_path=gsoc_forms.TEMPLATE_PATH,
include_fields=_CREATE_ORG_PROFILE_FIELD_IDS,
**kwargs)
SOC_CREATE_ORG_PROFILE_FORM_FACTORY = SOCCreateOrgProfileFormFactory()
class SOCEditOrgProfileFormFactory(organization_view.OrgProfileFormFactory):
"""Implementation of organization_view.OrgProfileFormFactory to be used
to create forms to edit organization profiles for Summer Of Code programs.
"""
def create(self, request_data, **kwargs):
"""See organization_view.OrgProfileFormFactory.create for specification."""
return organization_view._OrgProfileForm(
gsoc_forms.GSoCBoundField, request_data,
template_path=gsoc_forms.TEMPLATE_PATH,
include_fields=_EDIT_ORG_PROFILE_FIELD_IDS,
**kwargs)
SOC_EDIT_ORG_PROFILE_FORM_FACTORY = SOCEditOrgProfileFormFactory()
class SOCOrgApplicationFormFactory(survey_view.SurveyTakeFormFactory):
"""Implementation of survey_view.SurveyTakeFormFactory to be used to create
forms for organization applications for Summer Of Code programs.
"""
def create(self, survey, **kwargs):
"""See survey_view.SurveyTakeFormFactory.create for specification."""
return forms.SurveyTakeForm(
gsoc_forms.GSoCBoundField, survey=survey,
template_path=gsoc_forms.TEMPLATE_PATH,
**kwargs)
SOC_ORG_APPLICATION_FORM_FACTORY = SOCOrgApplicationFormFactory()
class SOCOrgApplicationReadonlyFactory(
organization_view.OrgApplicationReadonlyFactory):
def create(self, data, app_response):
"""See organization_view.OrgApplicationReadonlyFactory.create for
specification.
"""
builder = readonly.ReadonlyBuilder()
builder.setTemplatePath('summerofcode/_readonly_template.html')
builder.setGroupTemplatePath('summerofcode/readonly/_group.html')
builder.addGroupDescriptor(organization_view.GENERAL_INFO_GROUP_DESCRIPTOR)
builder.addGroupDescriptor(organization_view.CONTACT_GROUP_DESCRIPTOR)
builder.setValues(
organization_view.adaptOrgProfilePropertiesForForm(data.url_ndb_org))
builder.setValue(
organization_view.ADMINS_FIELD_ID,
organization_view.getAdminsValue(data.url_ndb_org))
# descriptor for survey response
if app_response:
builder.addGroupDescriptor(
readonly.SurveyGroupDescriptor(
organization_view.APP_RESPONSE_GROUP_TITLE, data.org_app))
builder.setValues(
survey_view.surveyResponseAsDict(data.org_app, app_response))
# remove unused fields
for field_id in _UNUSED_FIELD_IDS:
builder.removeField(field_id)
return builder.build(data)
SOC_ORG_APPLICATION_READONLY_FACTORY = SOCOrgApplicationReadonlyFactory()
class SOCAppResponseReadonlyFactory(
organization_view.AppResponseReadonlyFactory):
"""Implementation of organization_view.AppResponseReadonlyFactory to be used
for organization application responses for Summer Of Code programs.
"""
def create(self, data, app_response):
"""See organization_view.AppResponseReadonlyFactory.create
for specification.
"""
builder = readonly.ReadonlyBuilder()
builder.setTemplatePath('summerofcode/_readonly_template.html')
builder.setGroupTemplatePath('summerofcode/readonly/_group.html')
builder.addGroupDescriptor(
readonly.SurveyGroupDescriptor(
organization_view.APP_RESPONSE_GROUP_TITLE, data.org_app))
builder.setValues(
survey_view.surveyResponseAsDict(data.org_app, app_response))
return builder.build(data)
SOC_APP_RESPONSE_READONLY_FACTORY = SOCAppResponseReadonlyFactory()
ORG_PROFILE_CREATE_PAGE = organization_view.OrgProfileCreatePage(
base._GSOC_INITIALIZER, links.LINKER, render.SOC_RENDERER,
error.SOC_ERROR_HANDLER, soc_url_patterns.SOC_URL_PATTERN_CONSTRUCTOR,
urls.UrlNames, 'summerofcode/organization/org_profile_edit.html',
SOC_CREATE_ORG_PROFILE_FORM_FACTORY)
ORG_PROFILE_EDIT_PAGE = organization_view.OrgProfileEditPage(
base._GSOC_INITIALIZER, links.LINKER, render.SOC_RENDERER,
error.SOC_ERROR_HANDLER, soc_url_patterns.SOC_URL_PATTERN_CONSTRUCTOR,
urls.UrlNames, 'summerofcode/organization/org_profile_edit.html',
SOC_EDIT_ORG_PROFILE_FORM_FACTORY)
ORG_APPLICATION_LIST_PAGE = organization_view.OrgApplicationListPage(
base._GSOC_INITIALIZER, links.LINKER, render.SOC_RENDERER,
error.SOC_ERROR_HANDLER, soc_url_patterns.SOC_URL_PATTERN_CONSTRUCTOR,
urls.UrlNames, 'soc/org_app/records.html',
'summerofcode/organization/_org_application_list.html')
ORG_APPLICATION_SUBMIT_PAGE = organization_view.OrgApplicationSubmitPage(
base._GSOC_INITIALIZER, links.LINKER, render.SOC_RENDERER,
error.SOC_ERROR_HANDLER, soc_url_patterns.SOC_URL_PATTERN_CONSTRUCTOR,
urls.UrlNames, 'modules/gsoc/org_app/take.html',
SOC_ORG_APPLICATION_FORM_FACTORY)
ORG_APPLICATION_SHOW_PAGE = organization_view.OrgApplicationShowPage(
base._GSOC_INITIALIZER, links.LINKER, render.SOC_RENDERER,
error.SOC_ERROR_HANDLER, soc_url_patterns.SOC_URL_PATTERN_CONSTRUCTOR,
urls.UrlNames, 'modules/gsoc/org_app/show.html',
SOC_ORG_APPLICATION_READONLY_FACTORY)
ORG_APPLICATION_RESPONSE_SHOW_PAGE = (
organization_view.OrgApplicationResponseShowPage(
base._GSOC_INITIALIZER, links.LINKER, render.SOC_RENDERER,
error.SOC_ERROR_HANDLER, soc_url_patterns.SOC_URL_PATTERN_CONSTRUCTOR,
urls.UrlNames, 'modules/gsoc/org_app/show.html',
SOC_APP_RESPONSE_READONLY_FACTORY))
ORG_APPLICATION_RESPONSE_SHOW_PAGE = (
organization_view.OrgApplicationResponseShowPage(
base._GSOC_INITIALIZER, links.LINKER, render.SOC_RENDERER,
error.SOC_ERROR_HANDLER, soc_url_patterns.SOC_URL_PATTERN_CONSTRUCTOR,
urls.UrlNames, 'modules/gsoc/org_app/show.html',
SOC_APP_RESPONSE_READONLY_FACTORY))
ORG_LOGO_EDIT_PAGE = organization_view.OrgLogoEditPage(
base._GSOC_INITIALIZER, links.LINKER, render.SOC_RENDERER,
error.SOC_ERROR_HANDLER, soc_url_patterns.SOC_URL_PATTERN_CONSTRUCTOR,
urls.UrlNames, 'modules/gsoc/org_profile/base.html',
SOC_ORG_LOGO_FORM_FACTORY)
ORG_PREFERENCES_EDIT_PAGE = organization_view.OrgPreferencesEditPage(
base._GSOC_INITIALIZER, links.LINKER, render.SOC_RENDERER,
error.SOC_ERROR_HANDLER, soc_url_patterns.SOC_URL_PATTERN_CONSTRUCTOR,
urls.UrlNames, 'modules/gsoc/org_profile/base.html',
SOC_ORG_PREFERENCES_FORM_FACTORY)
# TODO(daniel): replace this class with new style list
class PublicOrganizationList(template.Template):
"""Public list of organizations participating in a specified program."""
def __init__(self, data):
"""See template.Template.__init__ for specification."""
super(PublicOrganizationList, self).__init__(data)
self._list_config = lists.ListConfiguration()
self._list_config.addPlainTextColumn(
'name', 'Name', lambda e, *args: e.name.strip())
self._list_config.addPlainTextColumn(
'tags', 'Tags', lambda e, *args: ', '.join(e.tags))
self._list_config.addPlainTextColumn('ideas', 'Ideas',
lambda e, *args: url_helper.urlize(e.ideas, name='[ideas page]'),
hidden=True)
self._list_config.setDefaultSort('name', 'asc')
def templatePath(self):
"""See template.Template.templatePath for specification."""
return 'modules/gsoc/admin/_accepted_orgs_list.html'
def context(self):
"""See template.Template.context for specification."""
description = ORGANIZATION_LIST_DESCRIPTION
list_configuration_response = lists.ListConfigurationResponse(
self.data, self._list_config, 0, description)
return {
'lists': [list_configuration_response],
}
class SOCPublicOrganizationListFactory(
organization_view.PublicOrganizationListFactory):
"""Implementation of organization_view.PublicOrganizationListFactory to be
used to create public list of organizations for Summer Of Code programs.
"""
def create(self, data):
"""See organization_view.PublicOrganizationListFactory for specification."""
return PublicOrganizationList(data)
SOC_PUBLIC_ORGANIZATION_LIST_FACTORY = SOCPublicOrganizationListFactory()
PUBLIC_ORGANIZATION_LIST_PAGE = organization_view.PublicOrganizationListPage(
base._GSOC_INITIALIZER, links.LINKER, render.SOC_RENDERER,
error.SOC_ERROR_HANDLER, soc_url_patterns.SOC_URL_PATTERN_CONSTRUCTOR,
urls.UrlNames, 'modules/gsoc/accepted_orgs/base.html',
SOC_PUBLIC_ORGANIZATION_LIST_FACTORY)