mamweb/treenode/models.py
2024-11-03 03:20:52 +01:00

350 lines
11 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 seminar.models import SeminarModelBase
from personalni.models import Organizator
from odevzdavatko.models import Reseni
logger = logging.getLogger(__name__)
import tvorba.models as am
class TreeNode(PolymorphicModel):
class Meta:
db_table = "seminar_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 = 'seminar_nodes_rocnik'
verbose_name = 'Ročník (Node)'
verbose_name_plural = 'Ročníky (Node)'
rocnik = models.OneToOneField(am.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 = 'seminar_nodes_cislo'
verbose_name = 'Číslo (Node)'
verbose_name_plural = 'Čísla (Node)'
cislo = models.OneToOneField(am.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 = 'seminar_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 = 'seminar_nodes_temavcisle'
verbose_name = 'Téma v čísle (Node)'
verbose_name_plural = 'Témata v čísle (Node)'
tema = models.ForeignKey(am.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 = 'seminar_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 = 'seminar_nodes_uloha_zadani'
verbose_name = 'Zadání úlohy (Node)'
verbose_name_plural = 'Zadání úloh (Node)'
uloha = models.OneToOneField(am.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 = 'seminar_nodes_pohadka'
verbose_name = 'Pohádka (Node)'
verbose_name_plural = 'Pohádky (Node)'
pohadka = models.OneToOneField(am.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 = 'seminar_nodes_uloha_vzorak'
verbose_name = 'Vzorák úlohy (Node)'
verbose_name_plural = 'Vzoráky úloh (Node)'
uloha = models.OneToOneField(am.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 ReseniNode(TreeNode):
class Meta:
db_table = 'seminar_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)
# LEdoian: Můžu prostě odstřelit Text a Obrázek do aplikace `treenode`, kam podle mě
# stejně patří? (Myšlenka, proč by tam měly patřit: tak, jak teď jsou je stejně
# využívají jen TreeNody a žádné rozhraní k nim stejně není, takže aktuálně
# použít nejdou (jako zbytek TN) a jejich sémantika pro „společnou tvorbu čísla
# na web a PDF“ je ze stejné školy. A taky kvůli nemíchání pokud vbrzku bude
# potřeba nějaký podobný model, navrhuji ho udělat znovu a klidně úplně stejně,
# staré věci pak buď zůstanou skryté, nebo je datově namigrujeme taková
# migrace bude snadná.) Jidáš: jo, a napiš tam tyhle myšlenky do komentáře.
# (zhruba přepis diskuse ve web-dev, 2024-10-30.)
class Text(SeminarModelBase):
class Meta:
db_table = 'seminar_texty'
verbose_name = 'text'
verbose_name_plural = 'texty'
na_web = models.TextField(
'text na web', blank=True,
help_text='Text ke zveřejnění na webu')
do_cisla = models.TextField(
'text do čísla', blank=True,
help_text='Text ke zveřejnění v čísle')
# má OneToOneField s:
# Reseni (je u něj jako reseni_cele)
# obrázky mají návaznost opačným směrem (vazba z druhé strany)
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
# *Node.save() aktualizuje název *Nodu.
for tn in self.textnode_set.all():
tn.save()
def __str__(self):
return str(self.na_web)[:20]
class TextNode(TreeNode):
class Meta:
db_table = 'seminar_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 = 'seminar_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 Obrazek(SeminarModelBase):
class Meta:
db_table = 'seminar_obrazky'
verbose_name = 'obrázek'
verbose_name_plural = 'obrázky'
# Interní ID
id = models.AutoField(primary_key=True)
na_web = models.ImageField(
'obrázek na web', upload_to='obrazky/%Y/%m/%d/',
null=True, blank=True)
text = models.ForeignKey(
Text, verbose_name='text',
help_text='text, ve kterém se obrázek vyskytuje',
null=False, blank=False, on_delete=models.CASCADE)
do_cisla_barevny = models.FileField(
'barevný obrázek do čísla',
help_text='Barevná verze obrázku do čísla',
upload_to='obrazky/%Y/%m/%d/', blank=True, null=True)
do_cisla_cernobily = models.FileField(
'černobílý obrázek do čísla',
help_text='Černobílá verze obrázku do čísla',
upload_to='obrazky/%Y/%m/%d/', blank=True, null=True)
# TODO placement hint - chci ho tady / pred textem / za textem