ejplatform/ej-server

View on GitHub
src/ej_conversations/models/vote.py

Summary

Maintainability
A
0 mins
Test Coverage
from django.conf import settings
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _

from boogie import models
from django.db import models as django_models
from boogie.fields import EnumField
from .vote_queryset import VoteQuerySet
from ..enums import Choice

VOTE_ERROR_MESSAGE = _("vote should be one of 'agree', 'disagree' or 'skip', got {value}")
VOTING_ERROR = lambda value: ValueError(VOTE_ERROR_MESSAGE.format(value=value))
VOTE_NAMES = {Choice.AGREE: "agree", Choice.DISAGREE: "disagree", Choice.SKIP: "skip"}
VOTE_VALUES = {v: k for k, v in VOTE_NAMES.items()}


class VoteChannels:
    TELEGRAM = "telegram"
    WHATSAPP = "twilio"
    OPINION_COMPONENT = "opinion_component"
    RASA_WEBCHAT = "socketio"
    RASAX = "rasa"
    UNKNOWN = "unknown"
    EJ = "ej"
    ROCKETCHAT = "rocketchat"

    @staticmethod
    def choices():
        return [
            (VoteChannels.TELEGRAM, "Telegram"),
            (VoteChannels.WHATSAPP, "Whatsapp"),
            (VoteChannels.RASAX, "RASAX"),
            (VoteChannels.OPINION_COMPONENT, _("Opinion Component")),
            (VoteChannels.RASA_WEBCHAT, _("Rasa webchat")),
            (VoteChannels.EJ, _("EJ")),
            (VoteChannels.ROCKETCHAT, _("RocketChat")),
            (VoteChannels.UNKNOWN, _("Unknown")),
        ]


class Vote(models.Model):
    """
    A single vote cast for a comment.
    """

    author = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="votes", on_delete=models.PROTECT)
    comment = models.ForeignKey("Comment", related_name="votes", on_delete=models.CASCADE)
    choice = EnumField(Choice, _("Choice"), help_text=_("Agree, disagree or skip"))
    created = models.DateTimeField(_("Created at"), auto_now_add=True)
    channel = models.CharField(
        _("Channel"),
        max_length=50,
        blank=False,
        help_text=_("From which EJ channel the vote comes from"),
        choices=VoteChannels.choices(),
        default=VoteChannels.UNKNOWN,
    )
    analytics_utm = models.JSONField(blank=True, null=True)
    objects = VoteQuerySet.as_manager()

    class Meta:
        unique_together = ("author", "comment")
        ordering = ["id"]

    def __str__(self):
        comment = truncate(self.comment.content, 40)
        return f"{self.author} - {self.choice.name} ({comment})"

    def clean(self, *args, **kwargs):
        if self.comment.is_pending:
            msg = _("non-moderated comments cannot receive votes")
            raise ValidationError(msg)


#
# UTILITY FUNCTIONS
#
def normalize_choice(value):
    """
    Normalize numeric and string values to the correct vote value that
    should be stored in the database.
    """
    if value in VOTE_NAMES:
        return value
    try:
        return VOTE_VALUES[value]
    except KeyError:
        raise VOTING_ERROR(value)


def truncate(st, size):
    if len(st) > size - 2:
        return st[: size - 3] + "..."
    return st