<commit_before>from django.contrib.contenttypes.models import ContentType
from django.db.models import Exists, OuterRef, Q
from rest_framework import serializers

from agir.events.models import Event
from agir.events.serializers import EventListSerializer
from agir.groups.models import Membership
from agir.groups.models import SupportGroup
from agir.lib.serializers import FlexibleFieldsMixin, CurrentPersonField
from agir.msgs.actions import get_message_unread_comment_count
from agir.msgs.models import (
    SupportGroupMessage,
    SupportGroupMessageComment,
    UserReport,
)
from agir.people.models import Person
from agir.people.serializers import PersonSerializer


class BaseMessageSerializer(FlexibleFieldsMixin, serializers.ModelSerializer):
    id = serializers.UUIDField(read_only=True)
    created = serializers.DateTimeField(read_only=True)
    author = serializers.SerializerMethodField()
    text = serializers.CharField()
    image = serializers.ImageField(required=False)

    def get_author(self, obj):
        if obj.author is not None:
            return PersonSerializer(
                obj.author,
                context=self.context,
                fields=["id", "displayName", "image"],
            ).data
        return {"id": None, "displayName": "Utilisateur·ice supprimé·e", "image": None}


class MessageCommentSerializer(BaseMessageSerializer):
    class Meta:
        model = SupportGroupMessageComment
        fields = ("id", "author", "text", "image", "created")


class LinkedEventField(serializers.RelatedField):
    queryset = Event.objects.all()

    def to_representation(self, obj):
        if obj is None:
            return None
        return EventListSerializer(
            obj,
            fields=[
                "id",
                "name",
                "illustration",
                "startTime",
                "endTime",
                "location",
                "rsvp",
                "routes",
                "compteRendu",
                "subtype",
            ],
            context=self.context,
        ).data

    def to_internal_value(self, pk):
        if pk is None:
            return None
        return self.queryset.model.objects.get(pk=pk)


class SupportGroupMessageSerializer(BaseMessageSerializer):
    RECENT_COMMENT_LIMIT = 4
    LIST_FIELDS = (
        "id",
        "created",
        "author",
        "subject",
        "text",
        "group",
        "linkedEvent",
        "recentComments",
        "commentCount",
        "requiredMembershipType",
    )
    DETAIL_FIELDS = (
        "id",
        "created",
        "author",
        "subject",
        "text",
        "group",
        "linkedEvent",
        "lastUpdate",
        "comments",
        "requiredMembershipType",
    )

    lastUpdate = serializers.DateTimeField(read_only=True, source="last_update")
    group = serializers.SerializerMethodField(read_only=True)
    linkedEvent = LinkedEventField(
        source="linked_event",
        required=False,
        allow_null=True,
    )
    recentComments = serializers.SerializerMethodField(read_only=True)
    commentCount = serializers.SerializerMethodField(read_only=True)
    comments = serializers.SerializerMethodField(read_only=True)

    requiredMembershipType = serializers.ChoiceField(
        source="required_membership_type",
        required=False,
        allow_null=True,
        choices=Membership.MEMBERSHIP_TYPE_CHOICES,
        default=Membership.MEMBERSHIP_TYPE_FOLLOWER,
    )

    def get_group(self, obj):
        return {
            "id": obj.supportgroup.id,
            "name": obj.supportgroup.name,
        }

    def get_recentComments(self, obj):
        recent_comments = (
            obj.comments.active()
            .select_related("author")
            .order_by("-created")[: self.RECENT_COMMENT_LIMIT]
        )
        if recent_comments is not None:
            recent_comments = MessageCommentSerializer(
                reversed(recent_comments), context=self.context, many=True
            ).data
        return recent_comments

    def get_commentCount(self, obj):
        count = obj.comments.active().count()
        if count > self.RECENT_COMMENT_LIMIT:
            return count

    def get_comments(self, obj):
        return MessageCommentSerializer(
            obj.comments.active().select_related("author").order_by("created"),
            context=self.context,
            many=True,
        ).data

    class Meta:
        model = SupportGroupMessage
        fields = (
            "id",
            "created",
            "author",
            "subject",
            "text",
            "image",
            "group",
            "linkedEvent",
            "recentComments",
            "comments",
            "commentCount",
            "lastUpdate",
            "requiredMembershipType",
        )


class SupportGroupMessageParticipantSerializer(serializers.ModelSerializer):
    active = serializers.SerializerMethodField(read_only=True)
    total = serializers.SerializerMethodField(read_only=True)

    def to_representation(self, message):
        self.participants = (
            Person.objects.filter(
                Q(
                    id__in=message.supportgroup.memberships.filter(
                        membership_type__gte=message.required_membership_type
                    ).values_list("person_id")
                )
                | Q(id=message.author.id)
            )
            .annotate(
                has_commented=Exists(message.comments.filter(author_id=OuterRef("id")))
            )
            .distinct()
        )
        return super().to_representation(message)

    def get_active(self, message):
        return [
            {
                "id": person.id,
                "displayName": person.display_name,
                "gender": person.gender,
                "isAuthor": message.author_id == person.id,
                "image": person.image.thumbnail.url
                if (person.image and person.image.thumbnail)
                else None,
            }
            for person in self.participants
            if person.has_commented or message.author_id == person.id
        ]

    def get_total(self, _):
        return len(self.participants)<commit_msg>fix: get participants as list to avoid multiple queries, remove useless distinct<commit_after>from django.contrib.contenttypes.models import ContentType
from django.db.models import Exists, OuterRef, Q
from rest_framework import serializers

from agir.events.models import Event
from agir.events.serializers import EventListSerializer
from agir.groups.models import Membership
from agir.groups.models import SupportGroup
from agir.lib.serializers import FlexibleFieldsMixin, CurrentPersonField
from agir.msgs.actions import get_message_unread_comment_count
from agir.msgs.models import (
    SupportGroupMessage,
    SupportGroupMessageComment,
    UserReport,
)
from agir.people.models import Person
from agir.people.serializers import PersonSerializer


class BaseMessageSerializer(FlexibleFieldsMixin, serializers.ModelSerializer):
    id = serializers.UUIDField(read_only=True)
    created = serializers.DateTimeField(read_only=True)
    author = serializers.SerializerMethodField()
    text = serializers.CharField()
    image = serializers.ImageField(required=False)

    def get_author(self, obj):
        if obj.author is not None:
            return PersonSerializer(
                obj.author,
                context=self.context,
                fields=["id", "displayName", "image"],
            ).data
        return {"id": None, "displayName": "Utilisateur·ice supprimé·e", "image": None}


class MessageCommentSerializer(BaseMessageSerializer):
    class Meta:
        model = SupportGroupMessageComment
        fields = ("id", "author", "text", "image", "created")


class LinkedEventField(serializers.RelatedField):
    queryset = Event.objects.all()

    def to_representation(self, obj):
        if obj is None:
            return None
        return EventListSerializer(
            obj,
            fields=[
                "id",
                "name",
                "illustration",
                "startTime",
                "endTime",
                "location",
                "rsvp",
                "routes",
                "compteRendu",
                "subtype",
            ],
            context=self.context,
        ).data

    def to_internal_value(self, pk):
        if pk is None:
            return None
        return self.queryset.model.objects.get(pk=pk)


class SupportGroupMessageSerializer(BaseMessageSerializer):
    RECENT_COMMENT_LIMIT = 4
    LIST_FIELDS = (
        "id",
        "created",
        "author",
        "subject",
        "text",
        "group",
        "linkedEvent",
        "recentComments",
        "commentCount",
        "requiredMembershipType",
    )
    DETAIL_FIELDS = (
        "id",
        "created",
        "author",
        "subject",
        "text",
        "group",
        "linkedEvent",
        "lastUpdate",
        "comments",
        "requiredMembershipType",
    )

    lastUpdate = serializers.DateTimeField(read_only=True, source="last_update")
    group = serializers.SerializerMethodField(read_only=True)
    linkedEvent = LinkedEventField(
        source="linked_event",
        required=False,
        allow_null=True,
    )
    recentComments = serializers.SerializerMethodField(read_only=True)
    commentCount = serializers.SerializerMethodField(read_only=True)
    comments = serializers.SerializerMethodField(read_only=True)

    requiredMembershipType = serializers.ChoiceField(
        source="required_membership_type",
        required=False,
        allow_null=True,
        choices=Membership.MEMBERSHIP_TYPE_CHOICES,
        default=Membership.MEMBERSHIP_TYPE_FOLLOWER,
    )

    def get_group(self, obj):
        return {
            "id": obj.supportgroup.id,
            "name": obj.supportgroup.name,
        }

    def get_recentComments(self, obj):
        recent_comments = (
            obj.comments.active()
            .select_related("author")
            .order_by("-created")[: self.RECENT_COMMENT_LIMIT]
        )
        if recent_comments is not None:
            recent_comments = MessageCommentSerializer(
                reversed(recent_comments), context=self.context, many=True
            ).data
        return recent_comments

    def get_commentCount(self, obj):
        count = obj.comments.active().count()
        if count > self.RECENT_COMMENT_LIMIT:
            return count

    def get_comments(self, obj):
        return MessageCommentSerializer(
            obj.comments.active().select_related("author").order_by("created"),
            context=self.context,
            many=True,
        ).data

    class Meta:
        model = SupportGroupMessage
        fields = (
            "id",
            "created",
            "author",
            "subject",
            "text",
            "image",
            "group",
            "linkedEvent",
            "recentComments",
            "comments",
            "commentCount",
            "lastUpdate",
            "requiredMembershipType",
        )


class SupportGroupMessageParticipantSerializer(serializers.ModelSerializer):
    active = serializers.SerializerMethodField(read_only=True)
    total = serializers.SerializerMethodField(read_only=True)

    def to_representation(self, message):
        self.participants = list(
            Person.objects.filter(
                Q(
                    id__in=message.supportgroup.memberships.filter(
                        membership_type__gte=message.required_membership_type
                    ).values_list("person_id")
                )
                | Q(id=message.author.id)
            ).annotate(
                has_commented=Exists(message.comments.filter(author_id=OuterRef("id")))
            )
        )
        return super().to_representation(message)

    def get_active(self, message):
        return [
            {
                "id": person.id,
                "displayName": person.display_name,
                "gender": person.gender,
                "isAuthor": message.author_id == person.id,
                "image": person.image.thumbnail.url
                if (person.image and person.image.thumbnail)
                else None,
            }
            for person in self.participants
            if person.has_commented or message.author_id == person.id
        ]

    def get_total(self, _):
        return len(self.participants)
{'fraction_non_alphanumeric': 0.09352689195621133, 'fraction_numerical': 0.00015865460891638903, 'mean_word_length': 2.0891938250428814, 'pattern_counts': {'":': 20, '<': 3, '<?xml version=': 0, '>': 5, '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': '1968925', 'n_tokens_mistral': 3410, 'n_tokens_neox': 3282, 'n_words': 634}