"""Read-only serializers for profile payloads.""" from __future__ import annotations from typing import Any from rest_framework import serializers from apps.profiles.models import Company, Education, Experience, Profile, School, Skill from apps.profiles.privacy import mask_emails, mask_phones class SkillSerializer(serializers.ModelSerializer): class Meta: model = Skill fields = ("id", "name", "slug", "profile_count") class CompanySerializer(serializers.ModelSerializer): class Meta: model = Company fields = ("id", "name", "industry", "size", "founded", "website", "location_name") class SchoolSerializer(serializers.ModelSerializer): class Meta: model = School fields = ("id", "name", "type", "location_name") class ExperienceSerializer(serializers.ModelSerializer): company = CompanySerializer(read_only=True) class Meta: model = Experience fields = ( "id", "title", "title_role", "title_sub_role", "levels", "company", "company_name", "start_date", "end_date", "start_year", "end_year", "is_primary", "location_name", "summary", ) # fmt: skip class EducationSerializer(serializers.ModelSerializer): school = SchoolSerializer(read_only=True) class Meta: model = Education fields = ( "id", "school", "school_name", "degrees", "majors", "minors", "gpa", "start_date", "end_date", "end_year", ) # fmt: skip class PIIMixin: """Masks contact details unless the caller is allowed to see them.""" def _can_view_pii(self) -> bool: request = self.context.get("request") # type: ignore[attr-defined] user = getattr(request, "user", None) return bool(user and user.is_authenticated and user.can_view_pii) def get_emails(self, obj: Profile) -> list[dict[str, str]]: return obj.emails if self._can_view_pii() else mask_emails(obj.emails) def get_phone_numbers(self, obj: Profile) -> list[str]: return obj.phone_numbers if self._can_view_pii() else mask_phones(obj.phone_numbers) class ProfileListSerializer(PIIMixin, serializers.ModelSerializer): """Compact representation used for result cards.""" headline = serializers.CharField(read_only=True) company = serializers.CharField(source="current_company.name", default="", read_only=True) skills = serializers.SerializerMethodField() emails = serializers.SerializerMethodField() class Meta: model = Profile fields = ( "id", "full_name", "headline", "job_title", "job_title_role", "seniority_levels", "company", "industry", "location_name", "location_country", "years_experience", "linkedin_url", "skills", "emails", ) # fmt: skip def get_skills(self, obj: Profile) -> list[str]: # ``prefetch_related`` in the view keeps this at one extra query total. return [link.skill.name for link in obj.profile_skills.all()[:10]] class ProfileDetailSerializer(PIIMixin, serializers.ModelSerializer): headline = serializers.CharField(read_only=True) current_company = CompanySerializer(read_only=True) skills = serializers.SerializerMethodField() experiences = ExperienceSerializer(many=True, read_only=True) educations = EducationSerializer(many=True, read_only=True) emails = serializers.SerializerMethodField() phone_numbers = serializers.SerializerMethodField() pii_masked = serializers.SerializerMethodField() class Meta: model = Profile fields = ( "id", "external_id", "full_name", "first_name", "last_name", "gender", "headline", "job_title", "job_title_role", "job_title_sub_role", "seniority_levels", "current_company", "industry", "job_summary", "job_start_date", "location_name", "location_locality", "location_region", "location_country", "location_continent", "summary", "years_experience", "inferred_salary", "connections", "interests", "languages", "linkedin_url", "linkedin_username", "github_url", "twitter_url", "skills", "experiences", "educations", "emails", "phone_numbers", "pii_masked", ) # fmt: skip def get_skills(self, obj: Profile) -> list[dict[str, Any]]: return [ {"name": link.skill.name, "slug": link.skill.slug} for link in obj.profile_skills.all() ] def get_pii_masked(self, obj: Profile) -> bool: # noqa: ARG002 return not self._can_view_pii()