import logging from django.db import models from django.urls import reverse from django.contrib.contenttypes.models import ContentType from unidecode import unidecode # Používám pro získání ID odkazu (ještě je to někde po někom zakomentované) from polymorphic.models import PolymorphicModel from odevzdavatko.models import Reseni from personalni.models import Organizator from seminar.models.pomocne import Text from tvorba.models import Rocnik, Cislo, Tema, Uloha, Pohadka logger = logging.getLogger(__name__) __all__ = [ "ReseniNode", "CastNode", "TextNode", "CisloNode", "PohadkaNode", "OrgTextNode", "MezicisloNode", "TreeNode", "RocnikNode", "UlohaVzorakNode", "UlohaZadaniNode", "TemaVCisleNode", ] class TreeNode(PolymorphicModel): class Meta: db_table = "mam_nodes_treenode" verbose_name = "TreeNode" verbose_name_plural = "TreeNody" # TODO: Nechceme radši jako root vyžadovat přímo RocnikNode? root = models.ForeignKey('TreeNode', related_name="potomci_set", null = True, blank = False, on_delete = models.SET_NULL, # Vrcholy s null kořenem jsou sirotci bez ročníku verbose_name="kořen stromu") first_child = models.OneToOneField('TreeNode', related_name='father_of_first', null = True, blank = True, on_delete=models.SET_NULL, verbose_name="první potomek") succ = models.OneToOneField('TreeNode', related_name="prev", null = True, blank = True, on_delete=models.SET_NULL, verbose_name="další element na stejné úrovni") nazev = models.TextField("název tohoto node", help_text = "Tento název se zobrazuje v nabídkách pro výběr vhodného TreeNode", blank=False, null=True) # Nezveřejnitelný název na stránky - pouze do adminu zajimave = models.BooleanField(default = False, verbose_name = "Zajímavé", help_text = "Zobrazí se daná věc na rozcestníku témátek") srolovatelne = models.BooleanField(null = True, blank = True, verbose_name = "Srolovatelné", help_text = "Bude na stránce témátka možnost tuto položku skrýt") def getOdkazStr(self): # String na rozcestník return self.first_child.getOdkazStr() def getOdkaz(self): # ID HTML tagu, na který se bude scrollovat #{{self.getOdkaz}} # Jsem si vědom, že tu potenciálně vznikají kolize. # Přijdou mi natolik nepravděpodobné, že je neřeším # Chtěl jsem ale hezké odkazy string = unidecode(self.getOdkazStr()) returnVal = "" i = 0 while len(returnVal) < 16: # Max 15 znaků if i == len(string): break if string[i] == " ": returnVal += "-" if string[i].isalnum(): returnVal += string[i].lower() i += 1 return returnVal def __str__(self): if self.nazev: return self.nazev else: #TODO: logování return "Nepojmenovaný Treenode" def save(self, *args, **kwargs): self.aktualizuj_nazev() super().save(*args, **kwargs) def aktualizuj_nazev(self): raise NotImplementedError("Pokus o aktualizaci názvu obecného TreeNode místo konkrétní instance") def get_admin_url(self): content_type = ContentType.objects.get_for_model(self.__class__) return reverse("admin:%s_%s_change" % (content_type.app_label, content_type.model), args=(self.id,)) class RocnikNode(TreeNode): class Meta: db_table = 'mam_nodes_rocnik' verbose_name = 'Ročník (Node)' verbose_name_plural = 'Ročníky (Node)' rocnik = models.OneToOneField(Rocnik, on_delete = models.PROTECT, # Pokud chci mazat ročník, musím si Node pořešit ručně verbose_name = "ročník") def aktualizuj_nazev(self): self.nazev = "RocnikNode: "+str(self.rocnik) class CisloNode(TreeNode): class Meta: db_table = 'mam_nodes_cislo' verbose_name = 'Číslo (Node)' verbose_name_plural = 'Čísla (Node)' cislo = models.OneToOneField(Cislo, on_delete = models.PROTECT, # Pokud chci mazat číslo, musím si Node pořešit ručně verbose_name = "číslo") def aktualizuj_nazev(self): self.nazev = "CisloNode: "+str(self.cislo) def getOdkazStr(self): return "Číslo " + str(self.cislo) class MezicisloNode(TreeNode): class Meta: db_table = 'mam_nodes_mezicislo' verbose_name = 'Mezičíslo (Node)' verbose_name_plural = 'Mezičísla (Node)' # TODO: Využít TreeLib def aktualizuj_nazev(self): from treenode.treelib import safe_pred if safe_pred(self) is not None: if (self.prev.get_real_instance_class() != CisloNode and self.prev.get_real_instance_class() != MezicisloNode): raise ValueError("Předchůdce není číslo!") posledni = self.prev.cislo self.nazev = "MezicisloNode: Mezičíslo po čísle"+str(posledni) elif self.root: if self.root.get_real_instance_class() != RocnikNode: raise ValueError("Kořen stromu není ročník!") rocnik = self.root.rocnik self.nazev = "MezicisloNode: První mezičíslo ročníku "+str(rocnik) else: print("!!!!! Nějaké neidentifikované mezičíslo !!!!!") self.nazev = "MezicisloNode: Neidentifikovatelné mezičíslo!" def getOdkazStr(self): return "Obsah dostupný pouze na webu" class TemaVCisleNode(TreeNode): """ Obsahuje příspěvky k tématu v daném čísle """ class Meta: db_table = 'mam_nodes_temavcisle' verbose_name = 'Téma v čísle (Node)' verbose_name_plural = 'Témata v čísle (Node)' tema = models.ForeignKey(Tema, on_delete=models.PROTECT, # Pokud chci mazat téma, musím si Node pořešit ručně verbose_name = "téma v čísle") def aktualizuj_nazev(self): self.nazev = "TemaVCisleNode: "+str(self.tema) def getOdkazStr(self): return str(self.tema) class OrgTextNode(TreeNode): class Meta: db_table = 'mam_nodes_orgtextnode' verbose_name = 'Organizátorský článek (Node)' verbose_name_plural = 'Organizátorské články (Node)' organizator = models.ForeignKey(Organizator, null=False, blank=False, on_delete=models.DO_NOTHING, verbose_name="Organizátor", ) org_verejny = models.BooleanField(default = True, verbose_name = "Org je veřejný?", help_text = "Pokud ano, bude org pod článkem podepsaný", null=False, ) def aktualizuj_nazev(self): return f"OrgTextNode začínající následujícim: {self.first_child.nazev}" # FIXME!!! #def getOdkazStr(self): # return str(self.clanek) class UlohaZadaniNode(TreeNode): class Meta: db_table = 'mam_nodes_uloha_zadani' verbose_name = 'Zadání úlohy (Node)' verbose_name_plural = 'Zadání úloh (Node)' uloha = models.OneToOneField(Uloha, on_delete=models.PROTECT, # Pokud chci mazat téma, musím si Node pořešit ručně verbose_name = "úloha", null=True, blank=False) def aktualizuj_nazev(self): self.nazev = "UlohaZadaniNode: "+str(self.uloha) def getOdkazStr(self): return str(self.uloha) class PohadkaNode(TreeNode): class Meta: db_table = 'mam_nodes_pohadka' verbose_name = 'Pohádka (Node)' verbose_name_plural = 'Pohádky (Node)' pohadka = models.OneToOneField(Pohadka, on_delete=models.PROTECT, # Pokud chci mazat pohádku, musím si Node pořešit ručně verbose_name = "pohádka", ) def aktualizuj_nazev(self): self.nazev = "PohadkaNode: "+str(self.pohadka) class UlohaVzorakNode(TreeNode): class Meta: db_table = 'mam_nodes_uloha_vzorak' verbose_name = 'Vzorák úlohy (Node)' verbose_name_plural = 'Vzoráky úloh (Node)' uloha = models.OneToOneField(Uloha, on_delete=models.PROTECT, # Pokud chci mazat téma, musím si Node pořešit ručně verbose_name = "úloha", null=True, blank=False) def aktualizuj_nazev(self): self.nazev = "UlohaVzorakNode: "+str(self.uloha) def getOdkazStr(self): return str(self.uloha) class TextNode(TreeNode): class Meta: db_table = 'mam_nodes_obsah' verbose_name = 'Text (Node)' verbose_name_plural = 'Text (Node)' text = models.ForeignKey(Text, on_delete=models.CASCADE, verbose_name = 'text') def aktualizuj_nazev(self): self.nazev = "TextNode: "+str(self.text) def getOdkazStr(self): return str(self.text) class CastNode(TreeNode): class Meta: db_table = 'mam_nodes_cast' verbose_name = 'Část (Node)' verbose_name_plural = 'Části (Node)' nadpis = models.CharField('Nadpis', max_length=100, help_text = 'Nadpis podvěšené části obsahu') def aktualizuj_nazev(self): self.nazev = "CastNode: "+str(self.nadpis) def getOdkazStr(self): return str(self.nadpis) class ReseniNode(TreeNode): class Meta: db_table = 'mam_nodes_otistene_reseni' verbose_name = 'Otištěné řešení (Node)' verbose_name_plural = 'Otištěná řešení (Node)' reseni = models.ForeignKey(Reseni, on_delete=models.PROTECT, verbose_name = 'reseni') def aktualizuj_nazev(self): self.nazev = "ReseniNode: "+str(self.reseni) def getOdkazStr(self): return str(self.reseni)