<commit_before>from typing import Optional

from allauth.account.models import EmailAddress
from allauth.socialaccount.models import SocialAccount
from django.db.models import Q
from django.urls import reverse
from drf_yasg.utils import swagger_serializer_method
from knox.models import AuthToken
from rest_framework import serializers

from accounts.api.serializers_account_image import AccountImageSerializer
from accounts.models import Account
from users.models import (
    Invite,
    User,
    get_feature_flags,
    get_orgs_summary,
    get_projects_summary,
)


class UserSerializer(serializers.ModelSerializer):
    """
    A serializer for public user details.

    Only fields considered public should be available here.
    Includes fields from the user's personal `Account`.
    """

    display_name = serializers.SerializerMethodField()
    location = serializers.SerializerMethodField()
    image = serializers.SerializerMethodField()
    website = serializers.SerializerMethodField()
    public_email = serializers.SerializerMethodField()

    def get_personal_account_attr(self, user: User, attr: str) -> Optional[str]:
        """Get an attribute of a user's personal account."""
        try:
            return getattr(user.personal_account, attr, None)
        except User.personal_account.RelatedObjectDoesNotExist:
            return None

    def get_display_name(self, user) -> Optional[str]:
        """Get the display name of the user's personal account."""
        return self.get_personal_account_attr(user, "display_name")

    def get_location(self, user) -> Optional[str]:
        """Get the location for the user's personal account."""
        return self.get_personal_account_attr(user, "location")

    @swagger_serializer_method(serializer_or_field=AccountImageSerializer)
    def get_image(self, user) -> Optional[dict]:
        """Get the URLs of alternative image sizes for the account."""
        request = self.context.get("request")
        try:
            account = user.personal_account
        except User.personal_account.RelatedObjectDoesNotExist:
            return None
        return AccountImageSerializer.create(request, account) if request else None

    def get_website(self, user) -> Optional[str]:
        """Get the website of the user's personal account."""
        return self.get_personal_account_attr(user, "website")

    def get_public_email(self, user) -> Optional[str]:
        """Get the email address of the user's personal account."""
        return self.get_personal_account_attr(user, "email")

    class Meta:
        model = User
        fields = [
            "id",
            "username",
            "first_name",
            "last_name",
            "display_name",
            "location",
            "image",
            "website",
            "public_email",
        ]


class MeEmailAddressSerializer(serializers.ModelSerializer):
    """
    An email address linked to a user.
    """

    class Meta:
        model = EmailAddress
        fields = ["email", "primary", "verified"]


class MeLinkedAccountSerializer(serializers.ModelSerializer):
    """
    An external, third party account linked to a user.
    """

    class Meta:
        model = SocialAccount
        fields = ["provider", "uid", "date_joined", "last_login"]


class PersonalAccountSerializer(serializers.ModelSerializer):
    """
    The user's own personal account.
    """

    class Meta:
        model = Account
        fields = ["id", "tier"]


class MeFeatureFlagsSerializer(serializers.Serializer):
    """
    Serializer for a user's feature flag settings.

    This is here simply for documentation purposes (`drf-yasg`
    requires a `serializer_class` on views). The actual properties
    are dynamic, based on database rows.
    """

    feature_flag = serializers.ChoiceField(["on", "off"])<commit_msg>feat(API): Add email to linked account fields<commit_after>from typing import Optional

from allauth.account.models import EmailAddress
from allauth.socialaccount.models import SocialAccount
from django.db.models import Q
from django.urls import reverse
from drf_yasg.utils import swagger_serializer_method
from knox.models import AuthToken
from rest_framework import serializers

from accounts.api.serializers_account_image import AccountImageSerializer
from accounts.models import Account
from users.models import (
    Invite,
    User,
    get_feature_flags,
    get_orgs_summary,
    get_projects_summary,
)


class UserSerializer(serializers.ModelSerializer):
    """
    A serializer for public user details.

    Only fields considered public should be available here.
    Includes fields from the user's personal `Account`.
    """

    display_name = serializers.SerializerMethodField()
    location = serializers.SerializerMethodField()
    image = serializers.SerializerMethodField()
    website = serializers.SerializerMethodField()
    public_email = serializers.SerializerMethodField()

    def get_personal_account_attr(self, user: User, attr: str) -> Optional[str]:
        """Get an attribute of a user's personal account."""
        try:
            return getattr(user.personal_account, attr, None)
        except User.personal_account.RelatedObjectDoesNotExist:
            return None

    def get_display_name(self, user) -> Optional[str]:
        """Get the display name of the user's personal account."""
        return self.get_personal_account_attr(user, "display_name")

    def get_location(self, user) -> Optional[str]:
        """Get the location for the user's personal account."""
        return self.get_personal_account_attr(user, "location")

    @swagger_serializer_method(serializer_or_field=AccountImageSerializer)
    def get_image(self, user) -> Optional[dict]:
        """Get the URLs of alternative image sizes for the account."""
        request = self.context.get("request")
        try:
            account = user.personal_account
        except User.personal_account.RelatedObjectDoesNotExist:
            return None
        return AccountImageSerializer.create(request, account) if request else None

    def get_website(self, user) -> Optional[str]:
        """Get the website of the user's personal account."""
        return self.get_personal_account_attr(user, "website")

    def get_public_email(self, user) -> Optional[str]:
        """Get the email address of the user's personal account."""
        return self.get_personal_account_attr(user, "email")

    class Meta:
        model = User
        fields = [
            "id",
            "username",
            "first_name",
            "last_name",
            "display_name",
            "location",
            "image",
            "website",
            "public_email",
        ]


class MeEmailAddressSerializer(serializers.ModelSerializer):
    """
    An email address linked to a user.
    """

    class Meta:
        model = EmailAddress
        fields = ["email", "primary", "verified"]


class MeLinkedAccountSerializer(serializers.ModelSerializer):
    """
    An external, third party account linked to a user.
    """

    email = serializers.SerializerMethodField()

    def get_email(self, account):
        """
        Get the email address, if any, associated with the linked account.
        """
        if account.extra_data and "email" in account.extra_data:
            return account.extra_data["email"]
        else:
            return None

    class Meta:
        model = SocialAccount
        fields = ["provider", "email", "uid", "date_joined", "last_login"]


class PersonalAccountSerializer(serializers.ModelSerializer):
    """
    The user's own personal account.
    """

    class Meta:
        model = Account
        fields = ["id", "tier"]


class MeFeatureFlagsSerializer(serializers.Serializer):
    """
    Serializer for a user's feature flag settings.

    This is here simply for documentation purposes (`drf-yasg`
    requires a `serializer_class` on views). The actual properties
    are dynamic, based on database rows.
    """

    feature_flag = serializers.ChoiceField(["on", "off"])
{'fraction_non_alphanumeric': 0.09048323471400395, 'fraction_numerical': 0.0, 'mean_word_length': 3.058529264632316, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6624154', 'n_tokens_mistral': 2137, 'n_tokens_neox': 2064, 'n_words': 627}