Example #1
0
def get_assignments(user=None, state=None):
    queryset = SocialAssignments.objects

    if user is not None:
        queryset = queryset.filter(user=user)

    if state is not None:
        queryset = queryset.filter(state=state)

    queryset = queryset.order_by("-id").values()

    assignments = [a for a in queryset]
    assignmentsById = {}

    tweetIds = [a["social_id"] for a in assignments]
    for a in assignments:
        tweetIds.append(a["social_id"])
        for tweetId in a["thread_tweets"]:
            tweetIds.append(tweetId)

    tweets = {}
    for tweet in get_tweets_by_ids(tweetIds):
        tweets[tweet["tweet_id"]] = TweetsSerializer(tweet).data

    for assignment in assignments:
        assignmentsById[assignment["id"]] = SocialAssignmentSerializer(assignment).data

    return {"assignments": assignmentsById, "tweets": tweets}
Example #2
0
    def assign_reviewer(self, request, format=None):
        qp = request.query_params
        tweetId = qp["tweetId"] if "tweetId" in qp else None
        reviewerId = qp["reviewerId"] if "reviewerId" in qp else None

        try:
            status = get_status_from_db(tweetId)
            if status is None:
                raise ScremsongException(
                    "Could not find tweet {} in local db".format(tweetId))

            parents, parent = resolve_tweet_parents(status)
            parent, tweets, relationships = resolve_tweet_thread_for_parent(
                parent)
            replyTweetIds = [
                tweetId for tweetId in list(tweets.keys())
                if tweetId != parent["data"]["id_str"]
            ]

            assignment, created = SocialAssignments.objects.update_or_create(
                platform=SocialPlatformChoice.TWITTER,
                social_id=parent["data"]["id_str"],
                defaults={
                    "user_id": reviewerId,
                    "assigned_by": request.user,
                    "thread_relationships": relationships,
                    "thread_tweets": replyTweetIds
                })

            websockets.send_channel_message(
                "reviewers.assign", {
                    "assignment":
                    SocialAssignmentSerializer(assignment).data,
                    "tweets":
                    set_tweet_object_state_en_masse(tweets,
                                                    TweetState.ASSIGNED),
                })

            return Response({"OK": True})

        except ScremsongException as e:
            return Response(
                {
                    "error":
                    "Could not assign tweet {}. Failed to resolve and update tweet thread. Message: {}"
                    .format(tweetId, str(e))
                },
                status=status.HTTP_400_BAD_REQUEST)
Example #3
0
    def mark_read(self, request, format=None):
        qp = request.query_params
        assignmentId = int(
            qp["assignmentId"]) if "assignmentId" in qp else None

        assignment = SocialAssignments.objects.get(id=assignmentId)
        assignment.last_read_on = getCreationDateOfNewestTweetInAssignment(
            assignment)
        assignment.save()

        websockets.send_channel_message(
            "reviewers.assignment_metdata_changed", {
                "assignment": SocialAssignmentSerializer(assignment).data,
            })

        return Response({"OK": True})
Example #4
0
    def restore(self, request, format=None):
        qp = request.query_params
        assignmentId = int(
            qp["assignmentId"]) if "assignmentId" in qp else None

        assignment = SocialAssignments.objects.get(id=assignmentId)
        assignment.close_reason = None
        assignment.state = SocialAssignmentState.PENDING
        assignment.save()

        websockets.send_channel_message(
            "reviewers.assignment_metdata_changed", {
                "assignment": SocialAssignmentSerializer(assignment).data,
            })

        return Response({"OK": True})
Example #5
0
    def bulk_reassign_reviewer(self, request, format=None):
        qp = request.query_params
        currentReviewerId = int(
            qp["currentReviewerId"]) if "currentReviewerId" in qp else None
        newReviewerId = int(
            qp["newReviewerId"]) if "newReviewerId" in qp else None

        if currentReviewerId is not None and newReviewerId is not None:
            try:
                assignmentsUpdated = []
                tweetsUpdated = {}

                assignments = SocialAssignments.objects.filter(
                    user_id=currentReviewerId).filter(
                        state=SocialAssignmentState.PENDING)
                with transaction.atomic():
                    for assignment in assignments:
                        assignment.user_id = newReviewerId
                        assignment.assigned_by = request.user
                        assignment.save()

                        assignmentsUpdated.append(
                            SocialAssignmentSerializer(assignment).data)
                        parent = get_status_from_db(assignment.social_id)
                        parent, tweets, relationships = resolve_tweet_thread_for_parent(
                            parent)
                        tweetsUpdated = {
                            **tweetsUpdated,
                            **set_tweet_object_state_en_masse(
                                tweets, TweetState.ASSIGNED)
                        }

                websockets.send_channel_message("reviewers.bulk_assign", {
                    "assignments": assignmentsUpdated,
                    "tweets": tweetsUpdated,
                })

                return Response({"OK": True})

            except ScremsongException as e:
                return Response(
                    {
                        "error":
                        "Could not bulk reassign assignments for {} to {}. Failed to resolve and update tweet thread. Message: {}"
                        .format(currentReviewerId, newReviewerId, str(e))
                    },
                    status=status.HTTP_400_BAD_REQUEST)
Example #6
0
    def close(self, request, format=None):
        qp = request.query_params
        assignmentId = int(
            qp["assignmentId"]) if "assignmentId" in qp else None
        reason = str(qp["reason"]) if "reason" in qp else None

        if SocialAssignmentCloseReason.has_value(reason) is True:
            assignment = SocialAssignments.objects.get(id=assignmentId)
            assignment.close_reason = reason
            assignment.state = SocialAssignmentState.CLOSED
            assignment.last_read_on = getCreationDateOfNewestTweetInAssignment(
                assignment)
            assignment.save()

            websockets.send_channel_message(
                "reviewers.assignment_metdata_changed", {
                    "assignment": SocialAssignmentSerializer(assignment).data,
                })

        return Response({"OK": True})
Example #7
0
    def reassign_reviewer(self, request, format=None):
        qp = request.query_params
        assignmentId = int(
            qp["assignmentId"]) if "assignmentId" in qp else None
        newReviewerId = int(
            qp["newReviewerId"]) if "newReviewerId" in qp else None

        if assignmentId is not None and newReviewerId is not None:
            try:
                assignment = get_or_none(SocialAssignments, id=assignmentId)
                assignment.user_id = newReviewerId
                assignment.assigned_by = request.user
                assignment.save()

                parent = get_status_from_db(assignment.social_id)
                parent, tweets, relationships = resolve_tweet_thread_for_parent(
                    parent)

                websockets.send_channel_message(
                    "reviewers.assign", {
                        "assignment":
                        SocialAssignmentSerializer(assignment).data,
                        "tweets":
                        set_tweet_object_state_en_masse(
                            tweets, TweetState.ASSIGNED),
                    })

                return Response({"OK": True})

            except ScremsongException as e:
                return Response(
                    {
                        "error":
                        "Could not reassign assignment {}. Failed to resolve and update tweet thread. Message: {}"
                        .format(assignmentId, str(e))
                    },
                    status=status.HTTP_400_BAD_REQUEST)
Example #8
0
def process_new_tweet_reply(status, tweetSource, sendWebSocketEvent):
    # Deal with tweets arriving / being processed out of order.
    # If it's already part of an assignment then it's been processed and clients have been notified.
    if is_tweet_part_of_an_assignment(status["id_str"]) is True:
        logger.warning(
            "Got tweet {} that was already part of an assignment (process_new_tweet_reply)"
            .format(status["id_str"]))
        # Only send a web socket event if we're not handling this elsewhere (e.g. backfilling)
        if sendWebSocketEvent is True:
            notify_of_saved_tweet(get_tweet_from_db(status["id_str"]))
        return True

    # OK! This means that this is a newly arrived tweet, so we need to work out if it's part of an already existing assignment.

    # Start by saving the tweet as dirty (i.e. we need it in the database for thread resolution, but haven't finished processing it yet)
    tweet, created = save_tweet(status,
                                source=tweetSource,
                                status=TweetStatus.DIRTY)

    try:
        parents, parent = resolve_tweet_parents(status)

        # If the parent is tweet is part of an assignment then we need to go and
        # run a refresh to get us all new replies in the thread.
        # This gets us replies that we don't get via the stream, as well as our own replies.
        if is_tweet_part_of_an_assignment(parent["id_str"]) is True:
            parent, tweets, relationships = resolve_tweet_thread_for_parent(
                parent)
            replyTweetIds = [
                tweetId for tweetId in list(tweets.keys())
                if tweetId != parent["data"]["id_str"]
            ]

            assignment, created = SocialAssignments.objects.update_or_create(
                platform=SocialPlatformChoice.TWITTER,
                social_id=parent["data"]["id_str"],
                defaults={
                    "thread_relationships": relationships,
                    "thread_tweets": replyTweetIds,
                    "last_updated_on": timezone.now()
                })

            # Adding a new tweet reopens the assignment if the user is accepting assignments or unassigns it if they're offline and the assignment is closed
            # if is_user_accepting_assignments(assignment.user_id) is False and is_from_demsausage(tweet) is False:
            #     logger.info("Processing tweet {}: User is offline, so delete the assignment".format(status["id_str"]))
            #     assignmentId = assignment.id
            #     assignment.delete()

            #     websockets.send_channel_message("reviewers.unassign", {
            #         "assignmentId": assignmentId,
            #     })

            # else:
            # Adding a new tweet reopens the assignment if the user is accepting assignments
            if assignment.state == SocialAssignmentState.CLOSED:
                if is_from_demsausage(tweet) is False:
                    logger.info(
                        "Processing tweet {}: Set it to pending".format(
                            status["id_str"]))
                    assignment.state = SocialAssignmentState.PENDING
                    assignment.save()

                    websockets.send_user_channel_message(
                        "notifications.send", {
                            "message":
                            "One of your assignments has had a new reply arrive - it's been added back into your queue again",
                            "options": {
                                "variant": NotificationVariants.INFO
                            }
                        }, assignment.user.username)

            else:
                websockets.send_user_channel_message(
                    "notifications.send", {
                        "message":
                        "One of your assignments has had a new reply arrive",
                        "options": {
                            "variant": NotificationVariants.INFO
                        }
                    }, assignment.user.username)

            websockets.send_channel_message(
                "reviewers.assignment_updated", {
                    "assignment": SocialAssignmentSerializer(assignment).data,
                    "tweets": tweets,
                })

            tweet.state = TweetState.ASSIGNED

        # Once we're done processing the tweet, or if its parent is not part of an assignment,
        # then we just carry on and save the tweet has processed and send a notification.
        tweet.status = TweetStatus.OK
        tweet.save()
        if sendWebSocketEvent is True:
            notify_of_saved_tweet(tweet)
        return True

    except Exception as e:
        # Mark tweets as dirty if we failed to resolve thread relationships (or if something else terrible happened)
        tweet = Tweets.objects.update_or_create(
            tweet_id=tweet.tweet_id, defaults={"status": TweetStatus.DIRTY})
        raise e

    logger.error("Failed to process new tweet {}".format(status["id_str"]))
    return False