<commit_before>from django.db import transaction
from django.db.models import Q
from rest_framework import status, viewsets
from rest_framework.decorators import list_route
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

from crowdsourcing.serializers.project import ProjectSerializer
from crowdsourcing.models import Rating, TaskWorker, Project, RawRatingFeedback
from crowdsourcing.serializers.rating import RatingSerializer
from crowdsourcing.permissions.rating import IsRatingOwner
from crowdsourcing.utils import get_pk, setup_peer_review
from mturk.tasks import update_worker_boomerang


class WorkerRequesterRatingViewset(viewsets.ModelViewSet):
    queryset = Rating.objects.all()
    serializer_class = RatingSerializer
    permission_classes = [IsAuthenticated, IsRatingOwner]

    def create(self, request, *args, **kwargs):
        wrr_serializer = RatingSerializer(data=request.data)
        if wrr_serializer.is_valid():
            wrr = wrr_serializer.create(origin=request.user)
            wrr_serializer = RatingSerializer(instance=wrr)
            if wrr.origin_type == Rating.RATING_REQUESTER:
                update_worker_boomerang.delay(wrr.origin_id, wrr.task.project_id)
            return Response(wrr_serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(wrr_serializer.errors,
                            status=status.HTTP_400_BAD_REQUEST)

    def update(self, request, *args, **kwargs):
        wrr_serializer = RatingSerializer(data=request.data, partial=True)
        wrr = self.get_object()
        if wrr_serializer.is_valid():
            wrr = wrr_serializer.update(wrr, wrr_serializer.validated_data)
            wrr_serializer = RatingSerializer(instance=wrr)
            if wrr.origin_type == Rating.RATING_REQUESTER:
                update_worker_boomerang.delay(wrr.origin_id, wrr.task.project_id)
            return Response(wrr_serializer.data, status=status.HTTP_200_OK)
        else:
            return Response(wrr_serializer.errors,
                            status=status.HTTP_400_BAD_REQUEST)

    @list_route(methods=['post'], url_path='boomerang-feedback')
    def boomerang_feedback(self, request, *args, **kwargs):
        origin_id = request.user.id
        id_or_hash = request.data.get('project_id', -1)
        project_id, is_hash = get_pk(id_or_hash)
        if is_hash:
            project_id = Project.objects.filter(group_id=project_id).order_by('-id').first().id
        origin_type = Rating.RATING_REQUESTER
        ratings = request.data.get('ratings', [])
        task_ids = [r['task_id'] for r in ratings]
        worker_ids = [r['worker_id'] for r in ratings]
        task_workers = TaskWorker.objects.filter(~Q(status__in=[TaskWorker.STATUS_SKIPPED, TaskWorker.STATUS_EXPIRED]),
                                                 task__project__owner_id=origin_id,
                                                 task__project_id=project_id,
                                                 task_id__in=task_ids, worker_id__in=worker_ids)
        if task_workers.count() != len(ratings):
            return Response(data={"message": "Task worker ids are not valid, or do not belong to this project"},
                            status=status.HTTP_400_BAD_REQUEST)
        raw_ratings = []
        for r in ratings:
            raw_ratings.append(RawRatingFeedback(requester_id=origin_id, worker_id=r['worker_id'], weight=r['weight'],
                                                 task_id=r['task_id']))
        with transaction.atomic():
            RawRatingFeedback.objects.filter(task_id__in=task_ids, worker_id__in=worker_ids,
                                             requester_id=origin_id).delete()
            RawRatingFeedback.objects.bulk_create(raw_ratings)

            raw_ratings_obj = RawRatingFeedback.objects.filter(requester_id=origin_id, task__project_id=project_id)

            all_ratings = [{"weight": rr.weight, "worker_id": rr.worker_id, "task_id": rr.task_id} for rr in
                           raw_ratings_obj]
            all_worker_ids = [rr.worker_id for rr in raw_ratings_obj]

            max_val = max([r['weight'] for r in all_ratings])
            rating_objects = []

            for rating in all_ratings:
                rating['weight'] = 1 + (round(float(rating['weight']) / max_val, 2) * 2)
                rating_objects.append(
                    Rating(origin_type=origin_type, origin_id=origin_id, target_id=rating['worker_id'],
                           task_id=rating['task_id'], weight=rating['weight']))

            Rating.objects.filter(origin_type=origin_type, origin_id=origin_id, target_id__in=all_worker_ids,
                                  task__project_id=project_id).delete()
            Rating.objects.bulk_create(rating_objects)

        update_worker_boomerang.delay(origin_id, project_id)

        return Response(data={"message": "Success"}, status=status.HTTP_201_CREATED)

    @list_route(methods=['get'], url_path='list-by-target')
    def list_by_target(self, request, *args, **kwargs):
        origin_type = request.query_params.get('origin_type')
        origin_type = 1 if origin_type == 'worker' else 2

        target = request.query_params.get('target', -1)
        rating = Rating.objects.values('id', 'weight') \
            .filter(origin_id=request.user.id, target_id=target, origin_type=origin_type) \
            .order_by('-updated_at').first()
        if rating is None:
            rating = {
                'id': None,
            }
        rating.update({'target': target})
        rating.update({'origin_type': origin_type})
        return Response(data=rating, status=status.HTTP_200_OK)

    @list_route(methods=['post'], url_path='setup-peer-review')
    def setup_peer_review(self, request, *args, **kwargs):
        # Need to add error checking for valid data and for a review_price
        # Will also need to do some checking whether or not stream is true/false, and react accordingly. Maybe this
        # should be done in the API client though?
        worker_responses = request.data
        project_id = worker_responses['task_data']['id']
        project = Project.objects.get(id=project_id)
        finished_workers = []
        for worker in worker_responses:
            task_worker_id = worker['id']
            task_worker = TaskWorker.objects.get(id=task_worker_id)
            finished_workers.append(task_worker)
        review_project = project.projects.filter(is_review=True).first()
        if review_project is not None and review_project.price is not None:
            setup_peer_review(review_project, project, finished_workers)
            return Response(status=status.HTTP_200_OK)
        else:
            return Response(status=status.HTTP_400_BAD_REQUEST) #Maybe this is supposed to be some other error?


class RatingViewset(viewsets.ModelViewSet):
    queryset = Project.objects.filter(deleted_at__isnull=True)
    serializer_class = ProjectSerializer
    permission_classes = [IsAuthenticated]

    @list_route(methods=['GET'])
    def workers_ratings_by_project(self, request, **kwargs):
        project_id = request.query_params.get('project', -1)
        # noinspection SqlResolve
        data = TaskWorker.objects.raw(
            '''
               SELECT
                  r.id                 id,
                  2                    origin_type,
                  r.weight             weight,
                  u.id                 target,
                  u.username           username,
                  p.owner_id           origin,
                  COUNT(tw.task_id) AS "task_count"<commit_msg>small changes`<commit_after>from django.db import transaction
from django.db.models import Q
from rest_framework import status, viewsets
from rest_framework.decorators import list_route
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

from crowdsourcing.serializers.project import ProjectSerializer
from crowdsourcing.models import Rating, TaskWorker, Project, RawRatingFeedback
from crowdsourcing.serializers.rating import RatingSerializer
from crowdsourcing.permissions.rating import IsRatingOwner
from crowdsourcing.utils import get_pk, setup_peer_review
from mturk.tasks import update_worker_boomerang


class WorkerRequesterRatingViewset(viewsets.ModelViewSet):
    queryset = Rating.objects.all()
    serializer_class = RatingSerializer
    permission_classes = [IsAuthenticated, IsRatingOwner]

    def create(self, request, *args, **kwargs):
        wrr_serializer = RatingSerializer(data=request.data)
        if wrr_serializer.is_valid():
            wrr = wrr_serializer.create(origin=request.user)
            wrr_serializer = RatingSerializer(instance=wrr)
            if wrr.origin_type == Rating.RATING_REQUESTER:
                update_worker_boomerang.delay(wrr.origin_id, wrr.task.project_id)
            return Response(wrr_serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(wrr_serializer.errors,
                            status=status.HTTP_400_BAD_REQUEST)

    def update(self, request, *args, **kwargs):
        wrr_serializer = RatingSerializer(data=request.data, partial=True)
        wrr = self.get_object()
        if wrr_serializer.is_valid():
            wrr = wrr_serializer.update(wrr, wrr_serializer.validated_data)
            wrr_serializer = RatingSerializer(instance=wrr)
            if wrr.origin_type == Rating.RATING_REQUESTER:
                update_worker_boomerang.delay(wrr.origin_id, wrr.task.project_id)
            return Response(wrr_serializer.data, status=status.HTTP_200_OK)
        else:
            return Response(wrr_serializer.errors,
                            status=status.HTTP_400_BAD_REQUEST)

    @list_route(methods=['post'], url_path='boomerang-feedback')
    def boomerang_feedback(self, request, *args, **kwargs):
        origin_id = request.user.id
        id_or_hash = request.data.get('project_id', -1)
        project_id, is_hash = get_pk(id_or_hash)
        if is_hash:
            project_id = Project.objects.filter(group_id=project_id).order_by('-id').first().id
        origin_type = Rating.RATING_REQUESTER
        ratings = request.data.get('ratings', [])
        task_ids = [r['task_id'] for r in ratings]
        worker_ids = [r['worker_id'] for r in ratings]
        task_workers = TaskWorker.objects.filter(~Q(status__in=[TaskWorker.STATUS_SKIPPED, TaskWorker.STATUS_EXPIRED]),
                                                 task__project__owner_id=origin_id,
                                                 task__project_id=project_id,
                                                 task_id__in=task_ids, worker_id__in=worker_ids)
        if task_workers.count() != len(ratings):
            return Response(data={"message": "Task worker ids are not valid, or do not belong to this project"},
                            status=status.HTTP_400_BAD_REQUEST)
        raw_ratings = []
        for r in ratings:
            raw_ratings.append(RawRatingFeedback(requester_id=origin_id, worker_id=r['worker_id'], weight=r['weight'],
                                                 task_id=r['task_id']))
        with transaction.atomic():
            RawRatingFeedback.objects.filter(task_id__in=task_ids, worker_id__in=worker_ids,
                                             requester_id=origin_id).delete()
            RawRatingFeedback.objects.bulk_create(raw_ratings)

            raw_ratings_obj = RawRatingFeedback.objects.filter(requester_id=origin_id, task__project_id=project_id)

            all_ratings = [{"weight": rr.weight, "worker_id": rr.worker_id, "task_id": rr.task_id} for rr in
                           raw_ratings_obj]
            all_worker_ids = [rr.worker_id for rr in raw_ratings_obj]

            max_val = max([r['weight'] for r in all_ratings])
            rating_objects = []

            for rating in all_ratings:
                rating['weight'] = 1 + (round(float(rating['weight']) / max_val, 2) * 2)
                rating_objects.append(
                    Rating(origin_type=origin_type, origin_id=origin_id, target_id=rating['worker_id'],
                           task_id=rating['task_id'], weight=rating['weight']))

            Rating.objects.filter(origin_type=origin_type, origin_id=origin_id, target_id__in=all_worker_ids,
                                  task__project_id=project_id).delete()
            Rating.objects.bulk_create(rating_objects)

        update_worker_boomerang.delay(origin_id, project_id)

        return Response(data={"message": "Success"}, status=status.HTTP_201_CREATED)

    @list_route(methods=['get'], url_path='list-by-target')
    def list_by_target(self, request, *args, **kwargs):
        origin_type = request.query_params.get('origin_type')
        origin_type = 1 if origin_type == 'worker' else 2

        target = request.query_params.get('target', -1)
        rating = Rating.objects.values('id', 'weight') \
            .filter(origin_id=request.user.id, target_id=target, origin_type=origin_type) \
            .order_by('-updated_at').first()
        if rating is None:
            rating = {
                'id': None,
            }
        rating.update({'target': target})
        rating.update({'origin_type': origin_type})
        return Response(data=rating, status=status.HTTP_200_OK)

    @list_route(methods=['post'], url_path='setup-peer-review')
    def setup_peer_review(self, request, *args, **kwargs):
        # Need to add error checking for valid data and for a review_price
        # Will also need to do some checking whether or not stream is true/false, and react accordingly. Maybe this
        # should be done in the API client though?
        worker_responses = request.data
        if worker_responses.length < 2:
            return Response(status=status.HTTP_400_BAD_REQUEST)

        project_id = worker_responses[0]['task_data']['id']
        project = Project.objects.get(id=project_id)
        finished_workers = []
        for worker in worker_responses:
            task_worker_id = worker['id']
            task_worker = TaskWorker.objects.get(id=task_worker_id)
            finished_workers.append(task_worker)
        review_project = project.projects.filter(is_review=True).first()
        if review_project is not None and review_project.price is not None:
            setup_peer_review(review_project, project, finished_workers)
            return Response(status=status.HTTP_200_OK)
        else:
            return Response(status=status.HTTP_400_BAD_REQUEST) #Correct error?


class RatingViewset(viewsets.ModelViewSet):
    queryset = Project.objects.filter(deleted_at__isnull=True)
    serializer_class = ProjectSerializer
    permission_classes = [IsAuthenticated]

    @list_route(methods=['GET'])
    def workers_ratings_by_project(self, request, **kwargs):
        project_id = request.query_params.get('project', -1)
        # noinspection SqlResolve
        data = TaskWorker.objects.raw(
            '''
               SELECT
                  r.id                 id,
                  2                    origin_type,
                  r.weight             weight,
                  u.id                 target,
                  u.username           username,
                  p.owner_id           origin,
                  COUNT(tw.task_id) AS "task_count"
{'fraction_non_alphanumeric': 0.09156066378252728, 'fraction_numerical': 0.004971911926131594, 'mean_word_length': 2.510426110607434, 'pattern_counts': {'":': 10, '<': 4, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2114101', 'n_tokens_mistral': 4440, 'n_tokens_neox': 4290, 'n_words': 822}