import csv from datetime import datetime import json import os from colorfield.fields import ColorField from django.contrib.auth.models import User from django.db import models from django.forms.models import model_to_dict from django.utils.safestring import mark_safe from filer.fields.file import FilerFileField from lib.core.db.models.base import SharedPermissionBase from lib.core.db.models.mixins import DateAware, AuthorAware, DescriptionAware, NameAware, PublishedAware from tablequizwiki.settings import BASE_DIR class MediaFile(NameAware, DateAware, AuthorAware, DescriptionAware): # https://django-filer.readthedocs.io/en/latest/extending_filer.html # https://pypi.org/project/django-thumbnails/ file = FilerFileField(on_delete=models.RESTRICT, related_name='media_file') def __str__(self): return self.name def __repr__(self): return self.file.name class Link(NameAware, DateAware, AuthorAware, DescriptionAware): url = models.URLField(unique=True, db_index=True) def __str__(self): return self.url def __repr__(self): return self.url class Level(NameAware, DateAware, AuthorAware, DescriptionAware): value = models.IntegerField(unique=True, db_index=True) color = ColorField(default='#F90F90') def __str__(self): return f'{self.value} - {self.name}' def __repr__(self): return f'{self.value}' class Label(NameAware, DateAware, AuthorAware, DescriptionAware): color = ColorField(default='#666666') def __str__(self): return self.name def __repr__(self): return self.name class SharedQuestion(SharedPermissionBase): class Meta: abstract = False class SubmittedQuestion(DateAware): name = models.CharField(max_length=500, unique=True) buzzword = models.CharField(max_length=25, null=True, blank=True) question = models.TextField() awnser = models.TextField() level = models.ForeignKey(Level, on_delete=models.SET_NULL, null=True, blank=True) labels = models.ManyToManyField(Label, blank=True) medias = models.ManyToManyField(MediaFile, blank=True) links = models.ManyToManyField(Link, blank=True) shares = models.ManyToManyField(SharedQuestion, blank=True) release = models.BooleanField(default=False) released = models.BooleanField(default=False) released_at = models.DateTimeField(null=True,blank=True) def __str__(self): return f"{self.name} (#{self.id})" from django.db.models.signals import post_save from django.dispatch import receiver @receiver(post_save, sender=SubmittedQuestion) def release_submitted(sender, instance: SubmittedQuestion, **kwargs): if instance.release: n = Question.objects.create( name=instance.name, buzzword=instance.buzzword, question=instance.question, awnser=instance.awnser, is_published=True, ) try: n.labels.set(instance.labels.all()) n.level_id = instance.level_id n.save() except: n.delete() raise instance.released = True instance.release = False instance.released_at = datetime.now() instance.save() class Question(DateAware, AuthorAware, PublishedAware, DescriptionAware): name = models.CharField(max_length=500, unique=True, db_index=True) buzzword = models.CharField(max_length=25, null=True, blank=True) question = models.TextField(db_index=True) awnser = models.TextField(db_index=True) level = models.ForeignKey(Level, on_delete=models.SET_NULL, null=True) labels = models.ManyToManyField(Label, blank=True) medias = models.ManyToManyField(MediaFile, blank=True) links = models.ManyToManyField(Link, blank=True) shares = models.ManyToManyField(SharedQuestion, blank=True) def __str__(self): return self.name def __repr__(self): return self.name @staticmethod def searchdomain(term, from_quiz=False): return \ models.Q(name__icontains=term) \ | models.Q(question__icontains=term) \ | models.Q(awnser__icontains=term) \ | models.Q(buzzword__icontains=term) \ | models.Q(labels__name__icontains=term) @staticmethod def get_by_tearchterm(term, queryset=None): if not queryset: queryset = Question.objects return queryset.filter(Question.searchdomain(term)).annotate(cnt=models.Count('id')) def to_view(self, for_players: bool = False): ret = {} ret['name'] = '{}{}'.format(self.name, f'({self.buzzword})' if self.buzzword else '') ret['question'] = self.question if self.description: ret['question'] += ' \n\n------------------------------\n\n' + self.description ret['awnser'] = self.awnser ret['level'] = self.level.value ret['medias'] = [] ret['links'] = [] for media in self.medias.all(): ret.setdefault('medias', []).append(os.path.basename(media.file.path)) for link in self.links.all(): ret.setdefault('links', []).append(link.url) ret['question'] = mark_safe(ret['question'].replace('\n', '\n
')) return ret @property def zippath(self): return str(BASE_DIR) + f'/filestore/zip/question.{self.id}.zip' @property def pdfpath(self): return str(BASE_DIR) + f'/filestore/pdf/question.{self.id}.pdf' @property def csvpath(self): return str(BASE_DIR) + f'/filestore/csv/question.{self.id}.csv' def default_json(o): if isinstance(o, (MediaFile,)): return str(o) return json.dumps(model_to_dict(o)) def to_csv(self, fh=None): if fh: cf = fh else: cf = open(self.csvpath, 'w') items = self.to_view() keys = items.keys() dw = csv.DictWriter(cf, fieldnames=list(keys)) dw.writeheader() dw.writerow(items) if not fh: cf.close() return cf if fh else self.csvpath class QuestionVersion(DateAware, AuthorAware): question = models.ForeignKey(Question, on_delete=models.SET_NULL, null=True) data = models.JSONField() def __str__(self): return f'{self.question} - {self.created_at}' if self.question else f'{self.data.name} - {self.created_at}' @receiver(models.signals.post_save, sender=Question) def versionize_question(sender, instance: Question, *args, **kwargs): data = model_to_dict(instance) # to be able to json.dumps in model data['medias'] = [{'id': m.id, '__str__': str(m)} for m in data['medias']] data['links'] = [{'id': m.id, '__str__': str(m)} for m in data['links']] data['labels'] = [{'id': m.id, '__str__': str(m)} for m in data['labels']] data['shares'] = list(map(str, data['shares'])) QuestionVersion.objects.create( question=instance, data=data, # https://django-crum.readthedocs.io/en/latest/ # from crum import get_current_user # author=get_current_user(), )