ejplatform/ej-server

View on GitHub
src/ej_clusters/factories.py

Summary

Maintainability
A
2 hrs
Test Coverage
import random

from django.contrib.auth import get_user_model
from django.db.models import Avg
from django.conf import settings
from ej_clusters.models import Cluster, Stereotype, StereotypeVote
from ej_conversations import create_conversation
from ej_conversations.enums import Choice
from ej_conversations.models import Vote

User = get_user_model()


def set_clusters_from_comments(conversation, comment_map, exclusive=True, author=None):
    """
    Create clusters and stereotypes from conversation.

    Usage:

        >>> set_clusters_from_comments(conversation, {
            'cluster1': [
                'stereotype comment for cluster 1',
                'alternative stereotype comment for cluster 1',
            ]
            'cluster2': [
                'stereotype comment for cluster 2',
            ]
        })
    """
    author = author or conversation.author
    clusterization = conversation.get_clusterization()
    created_comments = []
    created_stereotypes = []

    for cluster_name, comments in comment_map.items():
        if isinstance(cluster_name, (tuple, list)):
            cluster_name, description = cluster_name
        else:
            description = f'Stereotype for the "{cluster_name}" cluster'

        # Create cluster and stereotype
        cluster = Cluster.objects.create(clusterization=clusterization, name=cluster_name)
        stereotype, _ = Stereotype.objects.get_or_create(
            name=cluster_name, description=description, owner=author
        )
        cluster.stereotypes.add(stereotype)
        created_stereotypes.append(stereotype)

        # Save comments for stereotype
        if isinstance(comments, str):
            comments = [comments]
        for text in comments:
            comment = conversation.create_comment(author, text, status="approved", check_limits=False)
            created_comments.append(comment)
            stereotype.vote(comment, "agree")

    if exclusive:
        for stereotype in created_stereotypes:
            voted_ids = stereotype.votes.values_list("comment_id", flat=True)
            votes = {comment: "disagree" for comment in created_comments if comment.id not in voted_ids}
            stereotype.cast_votes(votes)

    return created_comments


def cluster_votes(conversation, users):
    clusterization = conversation.get_clusterization()
    comments = list(conversation.comments.all())
    comments_map = {comment.id: comment for comment in comments}
    clusters = {cluster: [] for cluster in clusterization.clusters.all()}
    cluster_list = list(clusters)
    n_clusters = len(cluster_list)

    for i, user in enumerate(users):
        cluster = cluster_list[i % n_clusters]
        clusters[cluster].append(user)

    votes = []
    for cluster, users in clusters.items():
        vote_profiles = (
            StereotypeVote.objects.filter(author__in=cluster.stereotypes.all())
            .values("comment")
            .annotate(average=Avg("choice"))
        )
        for data in vote_profiles:
            comment_id = data["comment"]
            prob = 0.5 + data["average"] * 0.4

            for user in users:
                vote = random_vote(prob)

                if vote is not None:
                    vote = comments_map[comment_id].vote(user, vote, commit=False)
                    votes.append(vote)

    Vote.objects.bulk_create(votes)


def random_vote(prob):
    r = random.random()
    if r < 0.25:
        return Choice.SKIP
    elif r < 0.50:
        return None
    elif random.random() < prob:
        return Choice.AGREE
    else:
        return Choice.DISAGREE


#
# Examples
#
def make_conversation_with_clusters_en():
    conversation = create_conversation(
        "How should our society organize the production of goods and services?",
        "Economy",
        is_promoted=True,
        author=User.objects.filter(is_staff=True).first(),
    )
    set_clusters_from_comments(
        conversation,
        {
            "Liberal": [
                "Free market should regulate how enterprises invest money and hire " "employees.",
                "State should provide a stable judicial system and refrain from " "regulating the economy.",
            ],
            "Socialist": [
                "Government and the society as a whole must regulate business "
                "decisions to favor the common good rather than private interests.",
                "State leadership is necessary to drive a strong economy.",
            ],
            "Fascist": [
                "Government should eliminate opposition in order to ensure " "governability.",
                "Military should occupy high ranks in government.",
            ],
        },
    )
    return conversation


def make_conversation_with_clusters_pt_br():
    conversation = create_conversation(
        "Que medidas devem ser feitas para melhorar a educação de jovens e adolescentes?",
        "Educação",
        is_promoted=True,
        author=User.objects.filter(is_staff=True).first(),
    )
    set_clusters_from_comments(
        conversation,
        {
            "Estatista": [
                "É necessário aumentar a verba destinada à educação pública de qualidade",
                "Devemos incentivar a participação da classe média na escola pública reservando vagas nas "
                "universidades.",
                "O Brasil deve utilizar o dinheiro do pré-sal somente para a educação.",
            ],
            "Privatista": [
                "Estado deve financiar a criação de parcerias público-privadas para a educação",
                "Escolas particulares promovem maior autonomia pedagógica e dão poder de escolha aos pais.",
                "O Brasil deve financiar alunos carentes com vagas em escolas particulares.",
            ],
            "Liberal": [
                "Escolas devem promover criatividade e autonomia dos jovens",
                "Jovens devem possuir atividades extra-classe regulares em museus, parques, bibliotecas, etc.",
                "É necessário dar aulas de filosofia, sociologia, etc para incentivar o pensamento crítico",
            ],
            "Disciplinador": [
                "Escolas devem treinar a disciplina e respeito à autoridade e ao cumprimento de regras.",
                "Alunos e professores devem ser punidos exemplarmente por desordem e violação das regras.",
                "As escolas devem proibir manifestações políticas do professor em sala de aula",
            ],
            "Tecnocrata": [
                "A escola deve fornecer treinamento para o mercado de trabalho e capacitação profissional desde cedo.",
                "As disciplinas e conteúdos ensinados na escola devem refletir demandas do mercado de trabalho.",
                "É necessário um currículo e testes unificados.",
            ],
        },
    )
    return conversation


def make_conversation_with_clusters():
    if settings.COUNTRY in ("brasil", "brazil"):
        return make_conversation_with_clusters_pt_br()
    else:
        return make_conversation_with_clusters_pt_br()