# update these when the User model fields change or if you want to add/remove fields from API responses
#: Maps :class:`~users.models.User` boolean background fields to the label
#: returned in API responses under ``specialCategories``.
#: Add, remove, or rename entries here to control which flags are exposed
#: and how they are labelled without touching serializer logic.
SPECIAL_CATEGORY_FIELDS = {
'international': 'international',
'commuter': 'commuter',
'firstgen': 'firstgen',
'outofstate': 'outofstate',
'transfer': 'transfer',
}
#: Maximum number of mentees a single mentor may be assigned.
#: Mirrors :data:`users.models.MAX_MENTEES` and is re-exported here so that
#: matching serializers and services can import from a single location.
MAX_MENTEES = 2
[docs]
def serialize_mentor(mentor, score=None):
"""
Serialize a mentor ``User`` instance into an API-ready dictionary.
Includes profile fields, available roster capacity, and an optional
compatibility ``score``. The ``score`` field is included in the output
only when explicitly supplied, keeping the schema consistent between
match-listing responses (which need a score) and plain profile lookups
(which do not).
:param mentor: The mentor user instance to serialize.
:type mentor: users.models.User
:param score: Compatibility score for this mentor relative to a specific
mentee. Pass ``None`` (default) to omit the field from the response.
:type score: float or None
:returns: Dictionary of serialized mentor data. When ``score`` is
provided the dict includes a ``'score'`` key; otherwise it is absent.
:rtype: dict
Example — match listing (with score)::
>>> serialize_mentor(mentor_user, score=0.87)
{
'email': 'mentor@ucla.edu',
'name': 'John Smith',
'score': 0.87,
'available_slots': 1,
...
}
Example — profile lookup (without score)::
>>> serialize_mentor(mentor_user)
{
'email': 'mentor@ucla.edu',
'name': 'John Smith',
'available_slots': 2,
...
}
"""
data = {
'email': mentor.email,
'name': full_name(mentor),
'major': mentor.major,
'minor': mentor.minor,
'year': mentor.year,
'hobbies': mentor.hobbies,
'clubs': mentor.clubs,
'goals': mentor.goals,
'specialCategories': special_categories(mentor),
'available_slots': MAX_MENTEES - len(mentor.current_mentees),
'date_joined': mentor.date_joined.isoformat(),
}
if score is not None:
data['score'] = score
return data
[docs]
def serialize_mentee(mentee):
"""
Serialize a mentee ``User`` instance into an API-ready dictionary.
Returns core profile fields and background category labels. Does not
include matching metadata such as ``isMatched`` or ``matchedMentorEmail``
— those are considered internal state and are not exposed through this
serializer.
:param mentee: The mentee user instance to serialize.
:type mentee: users.models.User
:returns: Dictionary of serialized mentee data.
:rtype: dict
Example::
>>> serialize_mentee(mentee_user)
{
'email': 'mentee@g.ucla.edu',
'name': 'Jane Doe',
'major': ['Biology'],
'minor': [],
'year': 1,
'hobbies': 'hiking',
'clubs': 'Pre-Med Society',
'goals': 'Become a doctor',
'specialCategories': ['firstgen', 'transfer']
}
"""
return {
'email': mentee.email,
'name': full_name(mentee),
'major': mentee.major,
'minor': mentee.minor,
'year': mentee.year,
'hobbies': mentee.hobbies,
'clubs': mentee.clubs,
'goals': mentee.goals,
'specialCategories': special_categories(mentee),
}
[docs]
def full_name(user):
"""
Derive a display name from a user's ``firstName`` and ``lastName`` fields.
Strips whitespace from each component before joining. Falls back to
``'Not provided'`` if both fields are blank or absent, ensuring the
return value is always a non-empty string suitable for display.
:param user: The user whose name is being formatted.
:type user: users.models.User
:returns: Full name string, or ``'Not provided'`` if both name fields
are empty.
:rtype: str
Example::
>>> full_name(user) # both fields set
'Jane Doe'
>>> full_name(empty_user) # both fields blank
'Not provided'
"""
first = (user.firstName or '').strip()
last = (user.lastName or '').strip()
return f"{first} {last}".strip() or 'Not provided'
[docs]
def special_categories(user):
"""
Return a list of background category labels that apply to the given user.
Iterates over :data:`SPECIAL_CATEGORY_FIELDS` and includes the label for
each field whose value is truthy on the user instance. Fields absent
from the user object are treated as ``False`` via ``getattr`` default.
To add or remove a category from API responses, update
:data:`SPECIAL_CATEGORY_FIELDS` — no changes to this function are needed.
:param user: The user whose background flags are being evaluated.
:type user: users.models.User
:returns: List of label strings for all truthy background fields.
Returns an empty list if no flags are set.
:rtype: list[str]
Example::
>>> special_categories(user) # firstgen and transfer flags set
['firstgen', 'transfer']
>>> special_categories(user) # no flags set
[]
"""
return [
label
for field, label in SPECIAL_CATEGORY_FIELDS.items()
if getattr(user, field, False)
]