blob: 1d55e6fa1916f6a8f6c98a5e274087e1685283a5 [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.
"""This module contains the profile related models."""
from google.appengine.ext import ndb
from google.appengine.ext.ndb import msgprop
from protorpc import messages
from melange.appengine import db
from melange.models import address as address_model
from melange.models import contact as contact_model
from melange.models import education as education_model
class TeeStyle(messages.Enum):
"""Class that enumerates possible styles for T-Shirts."""
#: Female style T-Shirt.
FEMALE = 1
#: Male style T-Shirt.
MALE = 2
#: The user opts not to receive any T-shirts.
NO_TEE = 3
class TeeSize(messages.Enum):
"""Class that enumerates possible sizes for T-Shirts."""
#: XXS size.
XXS = 1
#: XS size.
XS = 2
#: S size.
S = 3
#: M size.
M = 4
#: L size.
L = 5
#: XL size.
XL = 6
#: XXL size.
XXL = 7
#: XXXL size.
XXXL = 8
#: The user opts not to receive any T-shirts.
NO_TEE = 9
class Gender(messages.Enum):
"""Class that enumerates possible gender choices."""
#: Female gender.
FEMALE = 1
#: Male gender.
MALE = 2
#: Other gender.
OTHER = 3
#: The user does not disclose their gender.
NOT_DISCLOSED = 4
class Status(messages.Enum):
"""Class that enumerates possible statuses of profiles."""
#: The profile is active and participates in the program.
ACTIVE = 1
#: The profile has been expelled from the program by program administrators.
BANNED = 2
class MessageType(messages.Enum):
"""Class that enumerates types of messages to be sent to the profile."""
#: The welcome message that is sent to all organization members
ORG_MEMBER_WELCOME_MSG = 1
class StudentData(ndb.Model):
"""Model that represents student information to be associated with
the specified profile.
"""
#: Education information of the student.
education = ndb.StructuredProperty(education_model.Education, required=True)
#: Number of proposals which have been submitted by the student.
number_of_proposals = ndb.IntegerProperty(required=True, default=0)
#: Stores a list of proposal keys in the order of preference that the
#: student has chosen to work on.
preference_of_proposals = ndb.KeyProperty(repeated=True)
#: Number of projects which have been assigned to the student.
#: Note that right now at most one project per student is supported.
number_of_projects = ndb.IntegerProperty(required=True, default=0)
#: Total number of project evaluations that have been passed by the student
#: for all the projects that have been assigned to him or her.
number_of_passed_evaluations = ndb.IntegerProperty(required=True, default=0)
#: Total number of project evaluations that have been failed by the student
#: for all the projects that have been assigned to him or her.
number_of_failed_evaluations = ndb.IntegerProperty(required=True, default=0)
#: Proposals which are to-be-accepted for the student. Please note that this
#: property is set by a background script and may not contain up-to-date
#: information periodically.
to_be_accepted_proposals = ndb.KeyProperty(repeated=True)
#: If set to True, the student has more than one proposals that would
#: be accepted into the program.
has_duplicates = ndb.ComputedProperty(
lambda self: len(self.to_be_accepted_proposals) > 1)
#: List of organizations for which the student have been assigned a project.
project_for_orgs = ndb.KeyProperty(repeated=True)
#: Property pointing to the Blob storing student's tax form.
tax_form = ndb.BlobKeyProperty()
#: Property pointing to the Blob storing student's enrollment form.
enrollment_form = ndb.BlobKeyProperty()
#: Property telling whether the enrollment form is verified by
#: a program administrator.
is_enrollment_form_verified = ndb.BooleanProperty(default=False)
#: Number of tasks completed by the student.
number_of_completed_tasks = ndb.IntegerProperty(required=True, default=0)
#: Property telling whether the student has completed at least one task.
completed_task = ndb.ComputedProperty(
lambda self: bool(self.number_of_completed_tasks))
#: Property pointing to the Blob storing student's parental consent form.
consent_form = ndb.BlobKeyProperty()
#: Property telling whether the consent form is verified by
#: a program administrator.
is_consent_form_verified = ndb.BooleanProperty(default=False)
#: Organization for which the student is a winner.
winner_for = ndb.KeyProperty()
#: Property telling thether the student is a winner of the program.
is_winner = ndb.ComputedProperty(lambda self: bool(self.winner_for))
class NotificationSettings(ndb.Model):
"""Model that represents what type of notifications should be delivered
to the specified profile.
"""
#: The user is notified of new connections started with one of
#: the organizations that are managed by the user and of any updates to
#: existing connections with these organizations.
org_connections = ndb.BooleanProperty(default=True)
#: The user is notified of new connections started with him or her and
#: of any updates to existing connections.
user_connections = ndb.BooleanProperty(default=True)
#: The user is notified of new proposals when they are submitted to one
#: the organizations for which he or she is a mentor.
org_new_proposals = ndb.BooleanProperty(default=True)
#: The user is notified of any updates to proposals which are submitted
#: the organizations for which he or she is a mentor.
org_update_proposals = ndb.BooleanProperty(default=True)
#: The user is notified of any new comments which are posted to proposals
#: for organizations for which he or she is a mentor.
org_proposal_comments = ndb.BooleanProperty(default=True)
class Profile(ndb.Model):
"""Model that represents profile that is registered on per-program basis
for a user.
Parent:
melange.models.user.User
"""
#: A reference to program entity to which the profile corresponds.
#: Each profile is created for exactly one program. If the same
#: user participates in more of them, a separate profile must be created
#: for each.
program = ndb.KeyProperty(required=True)
#: Required field storing a name that is to be displayed publicly.
# Can be a real name or a nick name or some other public alias.
# Public names can be any valid UTF-8 text.
public_name = ndb.StringProperty(required=True)
#: Required field storing first name of the profile. Can only be ASCII,
#: not UTF-8 text, because it may be used as a shipping address
#: and such characters may not be printable.
first_name = ndb.StringProperty(required=True)
#: Required field storing last name of the profile. Can only be ASCII,
#: not UTF-8 text, because it may be used as a shipping address
#: and such characters may not be printable.
last_name = ndb.StringProperty(required=True)
#: Optional field storing a URL to an image, for example a personal photo
#: or a cartoon avatar. May be displayed publicly.
photo_url = ndb.StringProperty(validator=db.link_validator)
#: Optional field storing an identifier of Avatar figure which has been
#: chosen by the profile.
avatar = ndb.StringProperty(required=False)
#: Contact options to the profile.
contact = ndb.LocalStructuredProperty(
contact_model.Contact, default=contact_model.Contact())
#: Residential address of the registered profile. It is assumed that
#: the person resides at this address.
residential_address = ndb.StructuredProperty(
address_model.Address, required=True)
#: Shipping address of the registered profile. All possible program related
#: packages will be sent to this address.
shipping_address = ndb.StructuredProperty(address_model.Address)
#: Birth date of the registered profile.
birth_date = ndb.DateProperty(required=True)
#: Field storing chosen T-Shirt style.
tee_style = msgprop.EnumProperty(TeeStyle)
#: Field storing chosen T-Shirt size.
tee_size = msgprop.EnumProperty(TeeSize)
#: Field storing gender of the registered profile.
gender = msgprop.EnumProperty(
Gender, required=True, default=Gender.NOT_DISCLOSED)
#: Field storing answers to the question how the registered profile heard
#: about the program.
program_knowledge = ndb.TextProperty()
#: Field storing student specific information which is relevant and set only
#: if the registered profile has a student role for the program.
student_data = ndb.StructuredProperty(StudentData)
#: Field storing whether the registered profile has
#: a student role for the program
is_student = ndb.ComputedProperty(lambda self: bool(self.student_data))
#: Field storing keys of rejected organizations on behalf of which
#: the registered profile applied to the program. It indicates that the
#: user administers such an organization, but it has not been accepted
#: into the program.
#: This field is used to distinguish between organization administrators
#: for accepted organizations and users whose organizations unsuccessfully
#: applied. The second groups may still have some privileges.
rejected_for = ndb.KeyProperty(repeated=True)
#: Field storing keys of organizations for which the registered profile
#: has a mentor role.
#: This information is also stored in a connection entity between the
#: specified organization and this profile.
mentor_for = ndb.KeyProperty(repeated=True)
#: Field storing whether the registered profile has a mentor
#: role for at least one organization in the program.
is_mentor = ndb.ComputedProperty(lambda self: bool(self.mentor_for))
#: Field storing keys of organizations for which the registered profile
#: has an organization administrator role.
#: This information is also stored in a connection entity between the
#: specified organization and this profile.
#: Please note that organization administrator is considered a mentor as well.
#: Therefore, each key, which is present in this field, can be also found
#: in mentor_for field.
admin_for = ndb.KeyProperty(repeated=True)
#: Field storing whether the registered profile has an organization
#: administrator role for at least one organization in the program.
is_admin = ndb.ComputedProperty(lambda self: bool(self.admin_for))
#: Field storing type of program wide messages that have been sent to
#: the profile so far.
sent_messages = msgprop.EnumProperty(MessageType, repeated=True)
#: Field storing the status of the registered profile.
status = msgprop.EnumProperty(Status, default=Status.ACTIVE)
#: Field storing keys of Terms Of Service documents that have been accepted
#: by the registered profile.
accepted_tos = ndb.KeyProperty(repeated=True)
#: Notification settings for the registered profile.
notification_settings = ndb.StructuredProperty(
NotificationSettings, required=True, default=NotificationSettings())
@property
def profile_id(self):
"""Unique identifier of the registered profile on per program basis.
It is the same as the identifier of the underlying user entity. It means
that all profiles for the same user for different programs hold
the same identifier.
May be displayed publicly and used as parts of various URLs that are
specific to this profile.
"""
return self.key.parent().id()
@property
def legal_name(self):
"""Full, legal name associated with the profile."""
return '%s %s' % (self.first_name, self.last_name)
@property
def ship_to_address(self):
"""Address to which all program packages should be shipped."""
if self.shipping_address:
address = address_model.Address(**self.shipping_address.to_dict())
if not address.name:
address.name = self.legal_name
else:
address = address_model.Address(**self.residential_address.to_dict())
address.name = self.legal_name
return address
def getSponsorId(profile_key):
"""Returns sponsor ID based on the specified profile key.
Args:
profile_key: Profile key.
Returns:
A string that represents sponsor ID.
"""
if isinstance(profile_key, ndb.Key):
return profile_key.id().split('/')[0]
else:
return profile_key.name().split('/')[0]
def getProgramId(profile_key):
"""Returns program ID based on the specified profile key.
Args:
profile_key: Profile key.
Returns:
A string that represents program ID.
"""
if isinstance(profile_key, ndb.Key):
return profile_key.id().split('/')[1]
else:
return profile_key.name().split('/')[1]
def getUserId(profile_key):
"""Returns user ID based on the specified profile key.
Args:
profile_key: Profile key.
Returns:
A string that represents user ID.
"""
if isinstance(profile_key, ndb.Key):
return profile_key.id().split('/')[2]
else:
return profile_key.name().split('/')[2]