from django.db import models from django.db.models.aggregates import Sum from django.db.models.functions import Coalesce from django.conf import settings from django.urls import reverse from django.template.loader import render_to_string from django.dispatch import receiver from django.utils.translation import get_language from constance import config from tinymce import HTMLField def get_internationalized(obj_set, prop_name): obj = obj_set.filter(language=get_language()).first() if obj: return getattr(obj, prop_name) return "" class KbCategory(models.Model): slug = models.SlugField() internal_name = models.CharField("Internal name", max_length=100) def get_absolute_url(self): return reverse('help:kb_category', kwargs=dict(category=self.slug)) def get_top_entries(self, n=None): n = n or config.KB_MAX_TOP_ENTRIES return KbEntry.objects.get_top(n, category=self) def get_entry_count(self): return self.entry_set.filter(internal=False).count() def __str__(self): return self.name @property def name(self): return get_internationalized(self.name_set, 'name') @property def description(self): return get_internationalized(self.description_set, 'description') class Meta: verbose_name = "Knowledgebase Category" verbose_name_plural = "Knowledgebase Categories" class KbCategoryName(models.Model): category = models.ForeignKey(KbCategory, on_delete=models.CASCADE, related_name='name_set') language = models.CharField(max_length=2) name = models.CharField(max_length=100) class KbCategoryDescription(models.Model): category = models.ForeignKey(KbCategory, on_delete=models.CASCADE, related_name='description_set') language = models.CharField(max_length=2) description = models.TextField() class KbEntry(models.Model): create_date = models.DateTimeField(auto_now_add=True, editable=False) category = models.ForeignKey(KbCategory, on_delete=models.CASCADE, related_name='entry_set') title = models.CharField(max_length=50) internal = models.BooleanField(default=False) def vote(self, user, v): vote = self.vote_set.filter(user=user).first() if vote: if vote.vote == v: return vote.vote = v vote.save() else: v = KbEntryVote(entry=self, user=user, vote=v) v.save() def get_score(self): return self.vote_set.aggregate(Sum('vote'))['vote__sum'] or 0 def get_vote_count(self): return self.vote_set.count() def get_absolute_url(self): return reverse('help:kb_entry', kwargs=dict(category=self.category.slug, entry=self.id)) @property def question(self): return self.question_set.filter(language=get_language()).first().question @property def answer(self): return self.answer_set.filter(language=get_language()).first().answer def __str__(self): return self.question class Meta: verbose_name = "Knowledgebase Entry" verbose_name_plural = "Knowledgebase Entries" class KbEntryManager(models.Manager): def get_top(self, n, **kwargs): return (self.filter(internal=False, **kwargs) .annotate(score=Coalesce(Sum('vote_set__vote'), 0)) .order_by('-score', 'id') [:n]) objects = KbEntryManager() class KbEntryQuestion(models.Model): entry = models.ForeignKey(KbEntry, on_delete=models.CASCADE, related_name='question_set') language = models.CharField(max_length=2) question = models.CharField(max_length=200) class KbEntryAnswer(models.Model): entry = models.ForeignKey(KbEntry, on_delete=models.CASCADE, related_name='answer_set') language = models.CharField(max_length=2) answer = HTMLField() class KbEntryVote(models.Model): entry = models.ForeignKey(KbEntry, on_delete=models.CASCADE, related_name='vote_set') user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True) create_date = models.DateTimeField(auto_now_add=True, editable=False) vote = models.IntegerField() def __str__(self): return 'Vote %+d on %s by %s' % (self.vote, self.entry, self.user) class Meta: verbose_name = "Knowledgebase Entry Vote"