class JoinableActionPermissions(permissions.IsAuthenticated):
    def has_object_permission(self, request, view, obj):
        # Only the applicant/invitee, team captain, or staff users can access
        current_player = request.user.player
        return current_player == obj.player or current_player.id == obj.team.captain_id or request.user.is_staff class MembershipPermissions(permissions.BasePermission):
    def has_permission(self, request, view):
        if request.method in ('PUT', 'PATCH', ):
            # Only team captains can edit team members
            return request.user.player.teams_captain_of.exists()
        return True

    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return True
        current_player = request.user.player
        if request.method == 'DELETE':
            # Only the member's team captain, the member's player, or staff users can delete memberships
            return current_player == obj.player or current_player.id == obj.team.captain_id or request.user.is_staff
        # Only the member's team captain or staff users can make changes
        return current_player.id == obj.team.captain_id or request.user.is_staff class ApplicationSerializer(ReadOnlyApplicationSerializer):
    """Used for creating applications."""
    team = serializers.PrimaryKeyRelatedField(queryset=Team.objects.all())
    player = serializers.PrimaryKeyRelatedField(queryset=Player.objects.all())
    position = serializers.PrimaryKeyRelatedField(queryset=Position.objects.all())

    def validate(self, data):
        # Players can create applications for teams which:
        #   1. They don't already belong to
        #   2. They don't already have an application to
        team = data['team']
        player = data['player']
        user = self.context['request'].user
        current_player = user.player

        if current_player.id != player.id:
            raise serializers.ValidationError('You cannot create applications for other players.')
        if team.players.filter(id=current_player.id).exists():
            raise serializers.ValidationError('You are already on this team.')
        if current_player.application_set.filter(team_id=team.id).exists():
            raise serializers.ValidationError('You already applied to this team.')
        return data

    class Meta(ReadOnlyApplicationSerializer.Meta):
        read_only_fields = (
            'created',
            'status',
        ) class EditApplicationSerializer(ReadOnlyApplicationSerializer):
    """Used for changing status."""
    status = serializers.ChoiceField(Status.CHOICES)

    @staticmethod
    def _process_status_changed_as_player(current_status, new_status):
        if not (current_status == Status.PENDING and new_status == Status.WITHDRAWN):
            raise serializers.ValidationError('You cannot change application status to that value.')

    @staticmethod
    def _process_status_changed_as_captain(current_status, new_status):
        if not (current_status == Status.PENDING and new_status in (Status.ACCEPTED, Status.REJECTED)):
            raise serializers.ValidationError('You cannot change application status to that value.')

    def validate_status(self, new_status):
        # Only allowable state transfers:
        #   As player: PENDING --> WITHDRAWN
        #   As captain: PENDING --> ACCEPTED, PENDING --> REJECTED
        if self.instance:
            current_player = self.context['request'].user.player
            current_status = self.instance.status
            status_changed = current_status != new_status

            if status_changed:
                if current_player == self.instance.player:
                    self._process_status_changed_as_player(current_status, new_status)
                elif current_player == self.instance.team.captain:
                    self._process_status_changed_as_captain(current_status, new_status)

        return new_status

    class Meta(ReadOnlyApplicationSerializer.Meta):
        read_only_fields = (
            'created',
            'status',
            'team',
            'player',
            'position',
        ) class EditInvitationSerializer(ReadOnlyInvitationSerializer):
    """Used for changing status."""
    status = serializers.ChoiceField(Status.CHOICES)

    @staticmethod
    def _process_status_changed_as_player(current_status, new_status):
        if not (current_status == Status.PENDING and new_status in (Status.ACCEPTED, Status.REJECTED)):
            raise serializers.ValidationError('You cannot change invitation status to that value.')

    @staticmethod
    def _process_status_changed_as_captain(current_status, new_status):
        if not (current_status == Status.PENDING and new_status == Status.WITHDRAWN):
            raise serializers.ValidationError('You cannot change invitation status to that value.')

    def validate_status(self, new_status):
        # Only allowable state transfers:
        #   As player: PENDING --> ACCEPTED, PENDING --> REJECTED
        #   As captain: PENDING --> WITHDRAWN
        if self.instance:
            current_player = self.context['request'].user.player
            current_status = self.instance.status
            status_changed = current_status != new_status

            if status_changed:
                if current_player == self.instance.player:
                    self._process_status_changed_as_player(current_status, new_status)
                elif current_player == self.instance.team.captain:
                    self._process_status_changed_as_captain(current_status, new_status)

        return new_status

    class Meta(ReadOnlyApplicationSerializer.Meta):
        read_only_fields = (
            'created',
            'status',
            'team',
            'player',
            'position',
        ) class EditMembershipAsCaptainSerializer(MembershipSerializer):
    position = serializers.PrimaryKeyRelatedField(queryset=Position.objects.all())

    class Meta(MembershipSerializer.Meta):
        read_only_fields = (
            'created',
            'updated',
            'team',
            'player',
            'url',
        ) class InterestSerializer(FixtureSerializer):
    url = serializers.HyperlinkedIdentityField(view_name='interest-detail')

    class Meta(FixtureSerializer.Meta):
        model = Interest class InvitationSerializer(serializers.ModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name='invitation-detail')
    team = serializers.PrimaryKeyRelatedField(queryset=Team.objects.all())
    player = serializers.PrimaryKeyRelatedField(queryset=Player.objects.all())
    position = serializers.PrimaryKeyRelatedField(queryset=Position.objects.all())
    created_by = serializers.PrimaryKeyRelatedField(queryset=Player.objects.all())

    def validate(self, data):
        # Only captains can create invitations.
        # Captains can't invite players who:
        #   1. Already have been invited
        #   2. Has an application
        #   3. Are already on the team
        #   4. Are themselves
        team = data['team']
        player = data['player']
        user = self.context['request'].user
        current_player = user.player

        if current_player.id != team.captain.id:
            raise serializers.ValidationError('Only the team captain can create invitations.')

        if player.id == current_player.id:
            raise serializers.ValidationError('You cannot invite yourself.')

        if team.invitation_set.filter(player__id=player.id).exists():
            raise serializers.ValidationError('This player has already been invited.')

        if team.application_set.filter(player_id=player.id).exists():
            raise serializers.ValidationError('This player has already applied.')

        if team.players.filter(id=player.id).exists():
            raise serializers.ValidationError('This player is already on the team.')

        return data

    @staticmethod
    def setup_eager_loading(queryset):
        queryset = queryset.select_related(
            'team',
            'player',
            'position',
            'created_by',
        )
        return queryset

    class Meta:
        model = Invitation
        fields = (
            'id',
            'created',
            'updated',
            'team',
            'player',
            'position',
            'status',
            'created_by',
            'url',
        )
        read_only_fields = (
            'id',
            'created',
            'updated',
            'team',
            'player',
            'position',
            'created_by',
            'url',
        ) class LanguageSerializer(FixtureSerializer):
    url = serializers.HyperlinkedIdentityField(view_name='language-detail')

    class Meta(FixtureSerializer.Meta):
        model = Language class PositionSerializer(serializers.ModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name='position-detail')

    class Meta:
        model = Position
        fields = (
            'id',
            'name',
            'secondary',
            'url',
        )
        read_only_fields = (
            'id',
            'name',
            'secondary',
            'url',
        ) class ReadOnlyApplicationSerializer(serializers.ModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name='application-detail')
    team = serializers.PrimaryKeyRelatedField(read_only=True)
    player = serializers.PrimaryKeyRelatedField(read_only=True)
    position = serializers.PrimaryKeyRelatedField(read_only=True)

    @staticmethod
    def setup_eager_loading(queryset):
        queryset = queryset.select_related(
            'team',
            'player',
            'position',
        )
        return queryset

    class Meta:
        model = Application
        fields = (
            'id',
            'created',
            'updated',
            'team',
            'player',
            'position',
            'status',
            'url',
        )
        read_only_fields = (
            'created',
            'status',
            'team',
            'player',
            'position',
            'status',
        ) class ReadOnlyInvitationSerializer(serializers.ModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name='invitation-detail')
    team = serializers.PrimaryKeyRelatedField(read_only=True)
    player = serializers.PrimaryKeyRelatedField(read_only=True)
    position = serializers.PrimaryKeyRelatedField(read_only=True)

    @staticmethod
    def setup_eager_loading(queryset):
        queryset = queryset.select_related(
            'team',
            'player',
            'position',
        )
        return queryset

    class Meta:
        model = Invitation
        fields = (
            'id',
            'created',
            'updated',
            'team',
            'player',
            'position',
            'status',
            'url',
        )
        read_only_fields = (
            'created',
            'status',
            'team',
            'player',
            'position',
            'status',
        ) class RegionSerializer(FixtureSerializer):
    url = serializers.HyperlinkedIdentityField(view_name='region-detail')

    class Meta(FixtureSerializer.Meta):
        model = Region class MembershipSerializer(serializers.ModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name='teammember-detail')
    team = serializers.PrimaryKeyRelatedField(read_only=True)
    player = serializers.PrimaryKeyRelatedField(read_only=True)
    position = serializers.PrimaryKeyRelatedField(read_only=True)

    @staticmethod
    def setup_eager_loading(queryset):
        queryset = queryset.select_related(
            'team',
            'player',
            'player__user',
            'position',
        )
        return queryset

    class Meta:
        model = TeamMember
        fields = (
            'id',
            'created',
            'updated',
            'team',
            'player',
            'position',
            'url',
        )
        read_only_fields = (
            'created',
        ) class UserEmailPreferencesSerializer(serializers.ModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name='useremailpreferences-detail')
    email_preferences = EmailPreferenceSerializer(many=True)

    @staticmethod
    def setup_eager_loading(queryset):
        queryset = queryset.select_related(
            'user',
        ).prefetch_related(
            'email_preferences',
        )
        return queryset

    def update(self, instance, validated_data):
        email_preferences = validated_data.pop('email_preferences')

        for preference in email_preferences:
            preference_id = preference.get('id')
            preference_receive = preference.get('receive')
            if None in (preference_id, preference_receive):
                raise serializers.ValidationError('Receive email preference without ID or new value.')

            try:
                preference_instance = instance.email_preferences.get(pk=preference_id)
            except EmailPreference.DoesNotExist:
                raise serializers.ValidationError('Received invalid email preference ID.')
            else:
                if preference_receive != preference_instance.receive:
                    preference_instance.receive = preference_receive
                    preference_instance.save()

        return UserEmailPreferences.objects.get(pk=instance.pk)

    class Meta:
        model = UserEmailPreferences
        fields = (
            'id',
            'created',
            'updated',
            'url',
            'user',
            'email_preferences',
        )
        read_only_fields = (
            'id',
            'created',
            'updated',
            'url',
            'user',
        )
import mimetypes
from uuid import uuid4
from django.contrib.auth import get_user_model
from rest_framework import mixins, permissions, status, views, viewsets
from rest_framework.decorators import list_route
from rest_framework.response import Response
from common.models import (
    Application,
    Interest,
    Invitation,
    Language,
    Position,
    Region,
    TeamMember,
)
from lib.boto import init_boto
from teams.models import Team
from tf_auth.models import UserEmailPreferences
from .permissions import (
    JoinableActionPermissions,
    MembershipPermissions,
)
from .serializers import (
    ApplicationSerializer,
    EditApplicationSerializer,
    EditInvitationSerializer,
    EditMembershipAsCaptainSerializer,
    InterestSerializer,
    InvitationSerializer,
    LanguageSerializer,
    PositionSerializer,
    ReadOnlyApplicationSerializer,
    ReadOnlyInvitationSerializer,
    RegionSerializer,
    MembershipSerializer,
    UserEmailPreferencesSerializer,
)



User = get_user_model()


class RegionViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Region.objects.all()
    serializer_class = RegionSerializer


class PositionViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Position.objects.all()
    serializer_class = PositionSerializer


class InterestViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Interest.objects.all()
    serializer_class = InterestSerializer


class LanguageViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Language.objects.all()
    serializer_class = LanguageSerializer


class JoinableEventViewSet(viewsets.ModelViewSet):
    def get_serializer_context(self):
        return {'request': self.request}

    def get_queryset(self):
        queryset = super().get_queryset()
        queryset = self.get_serializer_class().setup_eager_loading(queryset)

        # Only team captains can use the `team` parameter
        team_id = self.request.query_params.get('team')
        try:
            team = Team.objects.get(id=team_id)
        except Team.DoesNotExist:
            team_id = None
        else:
            if self.request.user.player.id != team.captain_id:
                team_id = None

        if team_id:
            queryset = queryset.filter(team_id=team_id)
        else:
            if self.request.user.is_staff:
                # Enable the `player` parameter for staff users
                player_id = self.request.query_params.get('player')
                if player_id:
                    queryset = queryset.filter(player_id=player_id)
            else:
                queryset = queryset.filter(player=self.request.user.player)

        return queryset


class ApplicationViewSet(JoinableEventViewSet):
    queryset = Application.objects.all()

    serializer_class = ApplicationSerializer