blob: b39b2453c5628c2d6290f8947d3f2537ecf3edfe [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 containing the profile related views for Code In."""
import collections
from django.utils import translation
from codein.request import error
from codein.request import render
from codein.views import student_forms as student_forms_view
from codein.views.helper import cookies as cookies_helper
from codein.views.helper import urls as ci_urls
from melange.request import access
from melange.request import exception
from melange.request import links
from melange.templates import readonly
from melange.views import profile as profile_view
from soc.logic import validate
from soc.modules.gci.views import base
from soc.modules.gci.views import forms as gci_forms
from soc.modules.gci.views.helper import url_patterns as ci_url_patterns
# Code-In programs do not collect these fields:
_SKIP_FIELDS = ('blog', 'web_page', 'photo_url', 'major', 'degree', 'grade',
'expected_graduation')
TEE_SIZE_HELP_TEXT = translation.gettext(
'Size of a T-Shirt that may be sent to you upon program completion. '
'(<a href="https://docs.google.com/document/d/'
'1OSRFXJm9ENs8TUpHY9sggdkzC3XiIeSGfNDiMUM7znQ/pub">Sizing Chart</a>)')
# CI uses "Contest Rules" instead of "Terms of Service"
STUDENT_TERMS_OF_SERVICE_LABEL = translation.ugettext(
'Read the Contest Rules and acknowledge by selecting the checkbox '
'at the end.')
STUDENT_TERMS_OF_SERVICE_READONLY_LABEL = translation.ugettext(
'You have already signed the Contest Rules.')
STUDENT_TERMS_OF_SERVICE_GROUP = translation.ugettext('8. Contest Rules')
ORG_MEMBER_TERMS_OF_SERVICE_LABEL = translation.ugettext(
'Read the Mentor Agreement and acknowledge by selecting the checkbox '
'at the end.')
ORG_MEMBER_TERMS_OF_SERVICE_READONLY_LABEL = translation.ugettext(
'You have already signed the Mentor Agreement.')
ORG_MEMBER_TERMS_OF_SERVICE_GROUP = translation.ugettext('8. Mentor Agreement')
SCHOOL_NAME_HELP_TEXT = translation.gettext(
'Enter the English name of your school. Write out the whole school name. '
'Please do not abbreviate.')
# TODO(daniel): improve this as suggested in issue 2444
SCHOOL_WEB_PAGE_HELP_TEXT = translation.ugettext(
'URL to the home page of your school. If you are homeschooled or your '
'school does not have a website please just put google.com.')
def getBirthDateFromCookieOrRedirect(data):
"""Returns birth date from the data which is stored in a cookie or redirects
to "Profile Age Check" page if no value is present.
Args:
data: request_data.RequestData for the current request.
Returns:
datetime.date containing the parsed birth date.
Raises:
expception.Redirect: if no cookie containing birth date is present.
"""
birth_date = cookies_helper.getBirthDateFromCookie(data)
if not birth_date:
raise exception.Redirect(
links.ABSOLUTE_LINKER.program(
data.program, ci_urls.UrlNames.PROFILE_AGE_CHECK, secure=True))
else:
return birth_date
def getTermsOfServiceLabel(is_student=None, is_readonly=None):
"""Returns the appropriate Terms of Service label.
Args:
is_student - bool, is it a student or not?
is_readonly - bool, have they already agreed?
Returns:
a string representing the terms of service
"""
if is_student:
if is_readonly:
return STUDENT_TERMS_OF_SERVICE_READONLY_LABEL
else:
return STUDENT_TERMS_OF_SERVICE_LABEL
else:
if is_readonly:
return ORG_MEMBER_TERMS_OF_SERVICE_READONLY_LABEL
else:
return ORG_MEMBER_TERMS_OF_SERVICE_LABEL
def getTermsOfServiceGroup(is_student=None):
"""Returns the appropriate Terms of Service group name.
Args:
is_student - bool, is it a student or not?
is_readonly - bool, have they already agreed?
Returns:
a string representing the terms of service
"""
if is_student:
return STUDENT_TERMS_OF_SERVICE_GROUP
else:
return ORG_MEMBER_TERMS_OF_SERVICE_GROUP
class _CreateStudentProfileForm(profile_view._UserProfileForm):
"""Form to set profile properties by a user that makes sure that birth date
comes from preinitialized cookie data.
"""
def clean_birth_date(self):
"""Cleans birth_date field.
The data submitted in the POST data is discarded. Instead the value is
taken from the pre-existing cookie.
Returns:
Cleaned value of birth_date field. Specifically, datetime.date object
that represents the submitted birth date.
Raises:
django_forms.ValidationError if the submitted value is not valid.
"""
birth_date = getBirthDateFromCookieOrRedirect(self.request_data)
return profile_view.cleanBirthDate(
birth_date, self.request_data.program, self.has_student_data)
class CIRegisterProfileFormFactory(profile_view.ProfileFormFactory):
"""Implementation of profile_view.ProfileFormFactory to be used to register
profiles for Code In programs.
"""
def create(self, request_data, terms_of_service, include_user_fields=None,
include_student_fields=None, data=None, **kwargs):
"""See profile_view.ProfileFormFactory.create for specification."""
if include_student_fields:
# when a new student profile is registered, the birth date always
# comes from the cookie
if data:
data['birth_date'] = getBirthDateFromCookieOrRedirect(request_data)
initial = None
else:
initial = {'birth_date': getBirthDateFromCookieOrRedirect(request_data)}
form = _CreateStudentProfileForm(
gci_forms.GCIBoundField, request_data,
terms_of_service=terms_of_service,
has_student_data=True,
template_path=gci_forms.TEMPLATE_PATH, skip_fields=_SKIP_FIELDS,
initial=initial, data=data, **kwargs)
# TODO(cleanup): will birth_date _ever_ be set here? If not, we
# can remove this block entirely:
if 'birth_date' in form.fields:
form.fields['birth_date'].widget.attrs['readonly'] = True
else:
# birth date is not collected for organization members
skip_fields = _SKIP_FIELDS + ('birth_date',)
form = profile_view._UserProfileForm(
gci_forms.GCIBoundField, request_data,
terms_of_service=terms_of_service,
has_student_data=False,
template_path=gci_forms.TEMPLATE_PATH, skip_fields=skip_fields,
data=data, **kwargs)
if not include_user_fields:
del form.fields['user_id']
# set CI specific values
form.fields['tee_size'].help_text = TEE_SIZE_HELP_TEXT
if 'terms_of_service' in form.fields:
form.fields['terms_of_service'].group = getTermsOfServiceGroup(
include_student_fields)
form.fields['terms_of_service'].label = getTermsOfServiceLabel(
include_student_fields, False)
if include_student_fields:
form.fields['school_name'].help_text = SCHOOL_NAME_HELP_TEXT
form.fields['school_web_page'].help_text = SCHOOL_WEB_PAGE_HELP_TEXT
return form
CI_REGISTER_PROFILE_FORM_FACTORY = CIRegisterProfileFormFactory()
class CIEditProfileFormFactory(profile_view.ProfileFormFactory):
"""Implementation of profile_view.ProfileFormFactory to be used to edit
profiles for Code In programs.
"""
def create(self, request_data, terms_of_service, include_user_fields=None,
include_student_fields=None, data=None, **kwargs):
"""See profile_view.ProfileFormFactory.create for specification."""
if include_student_fields and data and request_data.ndb_profile.birth_date:
# birth date cannot be updated for students, so override the incoming
# value with the one that exists in the datastore
data['birth_date'] = request_data.ndb_profile.birth_date
skip_fields = _SKIP_FIELDS
else:
# birth date is not collected for organization members or students
# who registered for programs starting from 2014
skip_fields = _SKIP_FIELDS + ('birth_date',)
form = profile_view._UserProfileForm(
gci_forms.GCIBoundField, request_data,
terms_of_service=terms_of_service,
has_student_data=include_student_fields,
template_path=gci_forms.TEMPLATE_PATH, skip_fields=skip_fields,
data=data, **kwargs)
if 'birth_date' in form.fields:
form.fields['birth_date'].widget.attrs['readonly'] = True
if not include_user_fields:
del form.fields['user_id']
# set CI specific values
# TODO(robert): Cleanup duplication between here and the Create
# page code above.
form.fields['tee_size'].help_text = TEE_SIZE_HELP_TEXT
if 'terms_of_service' in form.fields:
form.fields['terms_of_service'].group = getTermsOfServiceGroup(
include_student_fields)
form.fields['terms_of_service'].label = getTermsOfServiceLabel(
include_student_fields, False)
if 'readonly_tos' in form.fields:
form.fields['readonly_tos'].group = getTermsOfServiceGroup(
include_user_fields)
form.fields['readonly_tos'].label = getTermsOfServiceLabel(
include_student_fields, True)
# TODO(robert): This can't reach into the disabled checkbox inside
# the readonly TOS widget, so that still says TOS.
if include_student_fields:
form.fields['school_name'].help_text = SCHOOL_NAME_HELP_TEXT
form.fields['school_web_page'].help_text = SCHOOL_WEB_PAGE_HELP_TEXT
return form
CI_EDIT_PROFILE_FORM_FACTORY = CIEditProfileFormFactory()
class CIProfileReadonlyFactory(profile_view.ProfileReadonlyFactory):
"""Implementation of profile_view.ProfileReadonlyFactory to be used for
Code In programs.
"""
def create(self, data, profile):
"""See profile_view.ProfileReadonlyFactory.create for specification."""
groups = []
fields = collections.OrderedDict()
fields[profile_view.USER_ID_LABEL] = profile.key.parent().id()
fields[profile_view.PUBLIC_NAME_LABEL] = profile.public_name
groups.append(
readonly.Group(
data, 'codein/readonly/_group.html',
profile_view._BASIC_INFORMATION_GROUP, fields))
fields = collections.OrderedDict()
fields[profile_view.FIRST_NAME_LABEL] = profile.first_name
fields[profile_view.LAST_NAME_LABEL] = profile.last_name
fields[profile_view.EMAIL_LABEL] = profile.contact.email
fields[profile_view.PHONE_LABEL] = profile.contact.phone
groups.append(
readonly.Group(
data, 'codein/readonly/_group.html',
profile_view._CONTACT_GROUP, fields))
fields = collections.OrderedDict()
fields[profile_view.RESIDENTIAL_STREET_LABEL] = (
profile_view.streetValue(profile.residential_address))
fields[profile_view.RESIDENTIAL_CITY_LABEL] = (
profile.residential_address.city)
fields[profile_view.RESIDENTIAL_PROVINCE_LABEL] = (
profile.residential_address.province)
fields[profile_view.RESIDENTIAL_POSTAL_CODE_LABEL] = (
profile.residential_address.postal_code)
fields[profile_view.RESIDENTIAL_COUNTRY_LABEL] = (
profile.residential_address.country)
groups.append(
readonly.Group(
data, 'codein/readonly/_group.html',
profile_view._RESIDENTIAL_ADDRESS_GROUP, fields))
if profile.shipping_address:
fields = collections.OrderedDict()
fields[profile_view.SHIPPING_STREET_LABEL] = (
profile_view.streetValue(profile.shipping_address))
fields[profile_view.SHIPPING_CITY_LABEL] = profile.shipping_address.city
fields[profile_view.SHIPPING_PROVINCE_LABEL] = (
profile.shipping_address.province)
fields[profile_view.SHIPPING_POSTAL_CODE_LABEL] = (
profile.shipping_address.postal_code)
fields[profile_view.SHIPPING_COUNTRY_LABEL] = (
profile.shipping_address.country)
groups.append(
readonly.Group(
data, 'codein/readonly/_group.html',
profile_view._SHIPPING_ADDRESS_GROUP, fields))
fields = collections.OrderedDict()
fields[profile_view.TEE_STYLE_LABEL] = profile.tee_style
fields[profile_view.TEE_SIZE_LABEL] = profile.tee_size
fields[profile_view.GENDER_LABEL] = (
profile_view._GENDER_ENUM_TO_VERBOSE_MAP[profile.gender])
fields[profile_view.PROGRAM_KNOWLEDGE_LABEL] = profile.program_knowledge
groups.append(
readonly.Group(
data, 'codein/readonly/_group.html',
profile_view._OTHER_INFORMATION_GROUP, fields))
if profile.is_student:
groups.append(student_forms_view.StudentFormsTemplate(data, profile))
return readonly.Readonly(
data, 'codein/readonly/_readonly_template.html', groups)
CI_PROFILE_READONLY_FACTORY = CIProfileReadonlyFactory()
class EligibleBirthDateInCookie(access.AccessChecker):
"""Access checker that ensures that a birth date, which is eligible for
a student participant, is present in the cookie.
"""
def checkAccess(self, data, check):
"""See access.AccessChecker.checkAccess for specification."""
birth_date = getBirthDateFromCookieOrRedirect(data)
if not validate.isAgeSufficientForStudent(birth_date, data.program):
raise exception.Forbidden(message=profile_view.INSUFFICIENT_AGE)
ELIGIBLE_BIRTH_DATE_IN_COOKIE = EligibleBirthDateInCookie()
PROFILE_REGISTER_AS_STUDENT_ACCESS_CHECKER = access.ConjuctionAccessChecker([
access.STUDENT_SIGNUP_ACTIVE_ACCESS_CHECKER,
ELIGIBLE_BIRTH_DATE_IN_COOKIE,
access.HAS_NO_PROFILE_ACCESS_CHECKER,
])
PROFILE_REGISTER_AS_ORG_MEMBER_PAGE = (
profile_view.ProfileRegisterAsOrgMemberPage(
base._GCI_INITIALIZER, links.LINKER, render.CI_RENDERER,
error.CI_ERROR_HANDLER, ci_url_patterns.CI_URL_PATTERN_CONSTRUCTOR,
ci_urls.UrlNames, 'codein/profile/profile_edit.html',
CI_REGISTER_PROFILE_FORM_FACTORY))
PROFILE_REGISTER_AS_STUDENT_PAGE = profile_view.ProfileRegisterAsStudentPage(
base._GCI_INITIALIZER, links.LINKER, render.CI_RENDERER,
error.CI_ERROR_HANDLER, ci_url_patterns.CI_URL_PATTERN_CONSTRUCTOR,
ci_urls.UrlNames, 'codein/profile/profile_edit.html',
PROFILE_REGISTER_AS_STUDENT_ACCESS_CHECKER,
CI_REGISTER_PROFILE_FORM_FACTORY, persist_age_only=True)
PROFILE_DELETE_PAGE = profile_view.ProfileDeletePage(
base._GCI_INITIALIZER, links.LINKER, render.CI_RENDERER,
error.CI_ERROR_HANDLER, ci_url_patterns.CI_URL_PATTERN_CONSTRUCTOR,
ci_urls.UrlNames, 'codein/profile/profile_delete.html')
PROFILE_EDIT_PAGE = profile_view.ProfileEditPage(
base._GCI_INITIALIZER, links.LINKER, render.CI_RENDERER,
error.CI_ERROR_HANDLER, ci_url_patterns.CI_URL_PATTERN_CONSTRUCTOR,
ci_urls.UrlNames, 'codein/profile/profile_edit.html',
CI_EDIT_PROFILE_FORM_FACTORY)
PROFILE_SHOW_PAGE = profile_view.ProfileShowPage(
base._GCI_INITIALIZER, links.LINKER, render.CI_RENDERER,
error.CI_ERROR_HANDLER, ci_url_patterns.CI_URL_PATTERN_CONSTRUCTOR,
ci_urls.UrlNames, 'codein/profile/profile_show.html',
CI_PROFILE_READONLY_FACTORY)
PROFILE_ADMIN_PAGE = profile_view.ProfileAdminPage(
base._GCI_INITIALIZER, links.LINKER, render.CI_RENDERER,
error.CI_ERROR_HANDLER, ci_url_patterns.CI_URL_PATTERN_CONSTRUCTOR,
ci_urls.UrlNames, 'codein/profile/profile_show.html',
CI_PROFILE_READONLY_FACTORY)
PROFILE_AGE_CONFIRMATION_HANDLER = profile_view.ProfileAgeConfirmationHandler(
base._GCI_INITIALIZER, links.LINKER, render.CI_RENDERER,
error.CI_ERROR_HANDLER, ci_url_patterns.CI_URL_PATTERN_CONSTRUCTOR,
ci_urls.UrlNames)