diff --git a/make/schema b/make/schema index 05e84b61..83b711f8 100755 --- a/make/schema +++ b/make/schema @@ -5,5 +5,4 @@ set -exuo pipefail ensure_web_installed -./manage.py graph_models seminar | dot -Tpdf > schema_seminar.pdf ./manage.py graph_models -a -g | dot -Tpdf > schema_all.pdf diff --git a/odevzdavatko/migrations/0007_odstrel_treenode_pre.py b/odevzdavatko/migrations/0007_odstrel_treenode_pre.py new file mode 100644 index 00000000..8228403c --- /dev/null +++ b/odevzdavatko/migrations/0007_odstrel_treenode_pre.py @@ -0,0 +1,13 @@ +# Generated by Django 4.2.16 on 2024-11-02 19:45 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('odevzdavatko', '0006_tvorba_post'), + ] + + operations = [ + ] diff --git a/odevzdavatko/migrations/0008_odstrel_treenode_relink.py b/odevzdavatko/migrations/0008_odstrel_treenode_relink.py new file mode 100644 index 00000000..f1dbbc3f --- /dev/null +++ b/odevzdavatko/migrations/0008_odstrel_treenode_relink.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.16 on 2024-11-02 20:44 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('treenode', '0001_odstrel_treenode_create'), + ('odevzdavatko', '0007_odstrel_treenode_pre'), + ] + + operations = [ + migrations.AlterField( + model_name='reseni', + name='text_cely', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='reseni_cely_set', to='treenode.reseninode', verbose_name='Plná verze textu řešení'), + ), + ] diff --git a/odevzdavatko/migrations/0009_odstrel_treenode_post.py b/odevzdavatko/migrations/0009_odstrel_treenode_post.py new file mode 100644 index 00000000..f63cc125 --- /dev/null +++ b/odevzdavatko/migrations/0009_odstrel_treenode_post.py @@ -0,0 +1,14 @@ +# Generated by Django 4.2.16 on 2024-11-02 20:52 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('odevzdavatko', '0008_odstrel_treenode_relink'), + ('treenode', '0003_odstrel_treenode_post'), + ] + + operations = [ + ] diff --git a/odevzdavatko/models.py b/odevzdavatko/models.py index a52f370f..1cabbfb5 100644 --- a/odevzdavatko/models.py +++ b/odevzdavatko/models.py @@ -49,7 +49,7 @@ class Reseni(bm.SeminarModelBase): forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False, default=FORMA_EMAIL) - text_cely = models.OneToOneField('seminar.ReseniNode', verbose_name='Plná verze textu řešení', + text_cely = models.OneToOneField('treenode.ReseniNode', verbose_name='Plná verze textu řešení', blank=True, null=True, related_name="reseni_cely_set", on_delete=models.PROTECT) diff --git a/personalni/migrations/0016_odstrel_treenode_pre.py b/personalni/migrations/0016_odstrel_treenode_pre.py new file mode 100644 index 00000000..7b22f68c --- /dev/null +++ b/personalni/migrations/0016_odstrel_treenode_pre.py @@ -0,0 +1,13 @@ +# Generated by Django 4.2.16 on 2024-11-02 19:45 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('personalni', '0015_tvorba_post'), + ] + + operations = [ + ] diff --git a/personalni/migrations/0017_odstrel_treenode_post.py b/personalni/migrations/0017_odstrel_treenode_post.py new file mode 100644 index 00000000..a29e7068 --- /dev/null +++ b/personalni/migrations/0017_odstrel_treenode_post.py @@ -0,0 +1,14 @@ +# Generated by Django 4.2.16 on 2024-11-02 20:52 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('personalni', '0016_odstrel_treenode_pre'), + ('treenode', '0003_odstrel_treenode_post'), + ] + + operations = [ + ] diff --git a/seminar/migrations/0140_odstrel_treenode_pre.py b/seminar/migrations/0140_odstrel_treenode_pre.py new file mode 100644 index 00000000..b74a1215 --- /dev/null +++ b/seminar/migrations/0140_odstrel_treenode_pre.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.16 on 2024-11-02 19:45 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0139_tvorba_post'), + ('odevzdavatko', '0007_odstrel_treenode_pre'), + ('personalni', '0016_odstrel_treenode_pre'), + ('tvorba', '0004_odstrel_treenode_pre'), + ('contenttypes', '0002_remove_content_type_name'), + ] + + operations = [ + ] diff --git a/seminar/migrations/0141_odstrel_treenode_unmanage.py b/seminar/migrations/0141_odstrel_treenode_unmanage.py new file mode 100644 index 00000000..b5ec6a4a --- /dev/null +++ b/seminar/migrations/0141_odstrel_treenode_unmanage.py @@ -0,0 +1,69 @@ +# Generated by Django 4.2.16 on 2024-11-02 19:56 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0140_odstrel_treenode_pre'), + ] + + operations = [ + migrations.AlterModelOptions( + name='castnode', + options={'managed': False, 'verbose_name': 'Část (Node)', 'verbose_name_plural': 'Části (Node)'}, + ), + migrations.AlterModelOptions( + name='cislonode', + options={'managed': False, 'verbose_name': 'Číslo (Node)', 'verbose_name_plural': 'Čísla (Node)'}, + ), + migrations.AlterModelOptions( + name='mezicislonode', + options={'managed': False, 'verbose_name': 'Mezičíslo (Node)', 'verbose_name_plural': 'Mezičísla (Node)'}, + ), + migrations.AlterModelOptions( + name='obrazek', + options={'managed': False, 'verbose_name': 'obrázek', 'verbose_name_plural': 'obrázky'}, + ), + migrations.AlterModelOptions( + name='orgtextnode', + options={'managed': False, 'verbose_name': 'Organizátorský článek (Node)', 'verbose_name_plural': 'Organizátorské články (Node)'}, + ), + migrations.AlterModelOptions( + name='pohadkanode', + options={'managed': False, 'verbose_name': 'Pohádka (Node)', 'verbose_name_plural': 'Pohádky (Node)'}, + ), + migrations.AlterModelOptions( + name='reseninode', + options={'managed': False, 'verbose_name': 'Otištěné řešení (Node)', 'verbose_name_plural': 'Otištěná řešení (Node)'}, + ), + migrations.AlterModelOptions( + name='rocniknode', + options={'managed': False, 'verbose_name': 'Ročník (Node)', 'verbose_name_plural': 'Ročníky (Node)'}, + ), + migrations.AlterModelOptions( + name='temavcislenode', + options={'managed': False, 'verbose_name': 'Téma v čísle (Node)', 'verbose_name_plural': 'Témata v čísle (Node)'}, + ), + migrations.AlterModelOptions( + name='text', + options={'managed': False, 'verbose_name': 'text', 'verbose_name_plural': 'texty'}, + ), + migrations.AlterModelOptions( + name='textnode', + options={'managed': False, 'verbose_name': 'Text (Node)', 'verbose_name_plural': 'Text (Node)'}, + ), + migrations.AlterModelOptions( + name='treenode', + options={'managed': False, 'verbose_name': 'TreeNode', 'verbose_name_plural': 'TreeNody'}, + ), + migrations.AlterModelOptions( + name='ulohavzoraknode', + options={'managed': False, 'verbose_name': 'Vzorák úlohy (Node)', 'verbose_name_plural': 'Vzoráky úloh (Node)'}, + ), + migrations.AlterModelOptions( + name='ulohazadaninode', + options={'managed': False, 'verbose_name': 'Zadání úlohy (Node)', 'verbose_name_plural': 'Zadání úloh (Node)'}, + ), + ] diff --git a/seminar/migrations/0142_odstrel_treenode_delete.py b/seminar/migrations/0142_odstrel_treenode_delete.py new file mode 100644 index 00000000..ec1a121c --- /dev/null +++ b/seminar/migrations/0142_odstrel_treenode_delete.py @@ -0,0 +1,153 @@ +# Generated by Django 4.2.16 on 2024-11-02 20:47 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0141_odstrel_treenode_unmanage'), + ('odevzdavatko', '0008_odstrel_treenode_relink'), + ('treenode', '0001_odstrel_treenode_create'), + ] + + operations = [ + migrations.RemoveField( + model_name='cislonode', + name='cislo', + ), + migrations.RemoveField( + model_name='cislonode', + name='treenode_ptr', + ), + migrations.RemoveField( + model_name='mezicislonode', + name='treenode_ptr', + ), + migrations.RemoveField( + model_name='obrazek', + name='text', + ), + migrations.RemoveField( + model_name='orgtextnode', + name='organizator', + ), + migrations.RemoveField( + model_name='orgtextnode', + name='treenode_ptr', + ), + migrations.RemoveField( + model_name='pohadkanode', + name='pohadka', + ), + migrations.RemoveField( + model_name='pohadkanode', + name='treenode_ptr', + ), + migrations.RemoveField( + model_name='reseninode', + name='reseni', + ), + migrations.RemoveField( + model_name='reseninode', + name='treenode_ptr', + ), + migrations.RemoveField( + model_name='rocniknode', + name='rocnik', + ), + migrations.RemoveField( + model_name='rocniknode', + name='treenode_ptr', + ), + migrations.RemoveField( + model_name='temavcislenode', + name='tema', + ), + migrations.RemoveField( + model_name='temavcislenode', + name='treenode_ptr', + ), + migrations.RemoveField( + model_name='textnode', + name='text', + ), + migrations.RemoveField( + model_name='textnode', + name='treenode_ptr', + ), + migrations.RemoveField( + model_name='treenode', + name='first_child', + ), + migrations.RemoveField( + model_name='treenode', + name='polymorphic_ctype', + ), + migrations.RemoveField( + model_name='treenode', + name='root', + ), + migrations.RemoveField( + model_name='treenode', + name='succ', + ), + migrations.RemoveField( + model_name='ulohavzoraknode', + name='treenode_ptr', + ), + migrations.RemoveField( + model_name='ulohavzoraknode', + name='uloha', + ), + migrations.RemoveField( + model_name='ulohazadaninode', + name='treenode_ptr', + ), + migrations.RemoveField( + model_name='ulohazadaninode', + name='uloha', + ), + migrations.DeleteModel( + name='CastNode', + ), + migrations.DeleteModel( + name='CisloNode', + ), + migrations.DeleteModel( + name='MezicisloNode', + ), + migrations.DeleteModel( + name='Obrazek', + ), + migrations.DeleteModel( + name='OrgTextNode', + ), + migrations.DeleteModel( + name='PohadkaNode', + ), + migrations.DeleteModel( + name='ReseniNode', + ), + migrations.DeleteModel( + name='RocnikNode', + ), + migrations.DeleteModel( + name='TemaVCisleNode', + ), + migrations.DeleteModel( + name='Text', + ), + migrations.DeleteModel( + name='TextNode', + ), + migrations.DeleteModel( + name='TreeNode', + ), + migrations.DeleteModel( + name='UlohaVzorakNode', + ), + migrations.DeleteModel( + name='UlohaZadaniNode', + ), + ] diff --git a/seminar/migrations/0143_odstrel_treenode_post.py b/seminar/migrations/0143_odstrel_treenode_post.py new file mode 100644 index 00000000..b54871c1 --- /dev/null +++ b/seminar/migrations/0143_odstrel_treenode_post.py @@ -0,0 +1,14 @@ +# Generated by Django 4.2.16 on 2024-11-02 20:52 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0142_odstrel_treenode_delete'), + ('treenode', '0003_odstrel_treenode_post'), + ] + + operations = [ + ] diff --git a/seminar/migrations/0144_post_odstrel_vseho.py b/seminar/migrations/0144_post_odstrel_vseho.py new file mode 100644 index 00000000..2a5d10f2 --- /dev/null +++ b/seminar/migrations/0144_post_odstrel_vseho.py @@ -0,0 +1,43 @@ +# Generated by Django 4.2.16 on 2024-11-03 01:55 + +from django.db import migrations + +# Myšlenka: Tahle migrace o sobě prohlašuje, že závisí na všem, co se do téhle chvíle stalo. To má dva důsledky: +# 1. V okamžiku, kdy tahle migrace proběhne, tak už máme model ve stavu který očekáváme. IOW slouží jako bariéra, za kterou nemůžou přetéct úpravy ostatních aplikací (hlavně těch našich) +# 2. Zároveň ale tvrdíme, že k tomu, aby tahle migrace proběhla, potřebujeme (potenciálně relativně staré) verze cizích aplikací, což způsobí uspořádání opačným směrem: DB změny cizích aplikací naopak proběhnou až po této migraci +# Vzhledem k tomu, že by i naše předchozí aplikace měly záviset na těchto změnách, tak tím efektivně vynucujeme zachování stavu pro ty mezilehlé migrace, které možná (chybou) nedokumentovaně spoléhají na to, jak vypadají cizí aplikace. +# Plán do budoucna: Jakmile tahle migrace proběhne na všech myslitelných databázích, můžeme její předchůdce prostě smazat a nahradit nějakou výrazně snazší sadou migrací, která jen vygeneruje správně tabulky a závislosti podle aktuálního modelu. +# - To se ve skutečnosti vesměs už stalo, v odstřelených aplikacích jsou modely stejně všechny „nové s daty spadlými z nebe“. Je moc pozdě v noci, ale myslím si, že prostě bude stačit smazat závislosti na migracích v `seminar`i a celou aplikaci `seminar` zrušit. (Největší problém je to při nasazování DB z nuly např. u generování testdat…) +# Je otázka, jestli tahle migrace nemá bydlet ve `various` či jinde, aby se dala smazat celá složka `seminar`. + +class Migration(migrations.Migration): + + dependencies = [ + ('admin', '0003_logentry_add_action_flag_choices'), + ('auth', '0012_alter_user_first_name_max_length'), + ('authtoken', '0004_alter_tokenproxy_options'), + ('contenttypes', '0002_remove_content_type_name'), + ('flatpages', '0001_initial'), + ('galerie', '0013_post_split_soustredeni'), + ('header_fotky', '0001_initial'), + ('korektury', '0024_vic_orgu_k_pdf'), + ('novinky', '0004_alter_novinky_id'), + ('odevzdavatko', '0009_odstrel_treenode_post'), + ('personalni', '0017_odstrel_treenode_post'), + ('prednasky', '0018_post_split_soustredeni'), + ('reversion', '0002_add_index_on_version_for_content_type_and_db'), + ('seminar', '0143_odstrel_treenode_post'), + ('sessions', '0001_initial'), + ('sifrovacka', '0006_personalni_post_migrate'), + ('sites', '0002_alter_domain_unique'), + ('sitetree', '0002_alter_treeitem_parent_alter_treeitem_tree'), + ('soustredeni', '0010_tvorba_post'), + ('taggit', '0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx'), + ('treenode', '0003_odstrel_treenode_post'), + ('tvorba', '0005_odstrel_treenode_post'), + ('various', '0006_tvorba_post'), + ('vyroci', '0001_initial'), + ] + + operations = [ + ] diff --git a/seminar/models/__init__.py b/seminar/models/__init__.py index 95e449ab..e0377ff0 100644 --- a/seminar/models/__init__.py +++ b/seminar/models/__init__.py @@ -1,8 +1,5 @@ from .tvorba import * -from .odevzdavatko import * from .base import * -from .pomocne import * -from .treenode import * from various.models import Nastaveni from personalni.models import Organizator, Resitel, Skola, Prijemce, Osoba @@ -10,6 +7,7 @@ from soustredeni.models import Soustredeni, Soustredeni_Ucastnici, Soustredeni_O from novinky.models import Novinky from odevzdavatko.models import Reseni, PrilohaReseni, Reseni_Resitele, Hodnoceni from tvorba.models import ZmrazenaVysledkovka, Deadline, Cislo, Rocnik, Pohadka, Tema, Problem, Problemy_Opravovatele, Uloha, Clanek +from treenode.models import UlohaVzorakNode, UlohaZadaniNode, CisloNode, TemaVCisleNode, OrgTextNode, Obrazek, RocnikNode, PohadkaNode, TextNode, MezicisloNode, ReseniNode, CastNode, Text, TreeNode # Kvůli migr. 0041 from soustredeni.models import generate_filename_konfera diff --git a/seminar/models/odevzdavatko.py b/seminar/models/odevzdavatko.py deleted file mode 100644 index efc88e74..00000000 --- a/seminar/models/odevzdavatko.py +++ /dev/null @@ -1,20 +0,0 @@ -from django.db import models - -from seminar.models import treenode as tm -from odevzdavatko.models import Reseni - -class ReseniNode(tm.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) - diff --git a/seminar/models/pomocne.py b/seminar/models/pomocne.py deleted file mode 100644 index 9fc6b7cd..00000000 --- a/seminar/models/pomocne.py +++ /dev/null @@ -1,69 +0,0 @@ -import logging -import os -from django.db import models - -from .base import SeminarModelBase - -logger = logging.getLogger(__name__) - - -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 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 - - diff --git a/treenode/migrations/0001_odstrel_treenode_create.py b/treenode/migrations/0001_odstrel_treenode_create.py new file mode 100644 index 00000000..8e7b1309 --- /dev/null +++ b/treenode/migrations/0001_odstrel_treenode_create.py @@ -0,0 +1,231 @@ +# Generated by Django 4.2.16 on 2024-11-02 20:06 + +from django.db import migrations, models +import django.db.models.deletion + +def nastav_nove_contenttypes(apps, schema_editor): + ContentType = apps.get_model('contenttypes', 'ContentType') + # Seznam níž ověřen tím, že se skutečně při téhle migraci tabulka `django_content_type` (lokální v SQLite) změní správně :-) + for m in ('ulohavzoraknode', 'ulohazadaninode', 'cislonode', 'temavcislenode', 'orgtextnode', 'obrazek', 'rocniknode', 'pohadkanode', 'textnode', 'mezicislonode', 'reseninode', 'castnode', 'text', 'treenode'): + ContentType.objects.filter(app_label='seminar', model=m).update(app_label='treenode') + +def nastav_stare_contenttypes(apps, schema_editor): + ContentType = apps.get_model('contenttypes', 'ContentType') + for m in ('ulohavzoraknode', 'ulohazadaninode', 'cislonode', 'temavcislenode', 'orgtextnode', 'obrazek', 'rocniknode', 'pohadkanode', 'textnode', 'mezicislonode', 'reseninode', 'castnode', 'text', 'treenode'): + ContentType.objects.filter(app_label='treenode', model=m).update(app_label='seminar') + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('seminar', '0141_odstrel_treenode_unmanage'), + ] + + operations = [ + migrations.CreateModel( + name='Obrazek', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('na_web', models.ImageField(blank=True, null=True, upload_to='obrazky/%Y/%m/%d/', verbose_name='obrázek na web')), + ('do_cisla_barevny', models.FileField(blank=True, help_text='Barevná verze obrázku do čísla', null=True, upload_to='obrazky/%Y/%m/%d/', verbose_name='barevný obrázek do čísla')), + ('do_cisla_cernobily', models.FileField(blank=True, help_text='Černobílá verze obrázku do čísla', null=True, upload_to='obrazky/%Y/%m/%d/', verbose_name='černobílý obrázek do čísla')), + ('text', models.ForeignKey(help_text='text, ve kterém se obrázek vyskytuje', on_delete=django.db.models.deletion.CASCADE, to='treenode.text', verbose_name='text')), + ], + options={ + 'verbose_name': 'obrázek', + 'verbose_name_plural': 'obrázky', + 'db_table': 'seminar_obrazky', + 'managed': False, + }, + ), + migrations.CreateModel( + name='Text', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('na_web', models.TextField(blank=True, help_text='Text ke zveřejnění na webu', verbose_name='text na web')), + ('do_cisla', models.TextField(blank=True, help_text='Text ke zveřejnění v čísle', verbose_name='text do čísla')), + ], + options={ + 'verbose_name': 'text', + 'verbose_name_plural': 'texty', + 'db_table': 'seminar_texty', + 'managed': False, + }, + ), + migrations.CreateModel( + name='TreeNode', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('nazev', models.TextField(help_text='Tento název se zobrazuje v nabídkách pro výběr vhodného TreeNode', null=True, verbose_name='název tohoto node')), + ('zajimave', models.BooleanField(default=False, help_text='Zobrazí se daná věc na rozcestníku témátek', verbose_name='Zajímavé')), + ('srolovatelne', models.BooleanField(blank=True, help_text='Bude na stránce témátka možnost tuto položku skrýt', null=True, verbose_name='Srolovatelné')), + ('first_child', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='father_of_first', to='treenode.treenode', verbose_name='první potomek')), + ('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')), + ('root', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='potomci_set', to='treenode.treenode', verbose_name='kořen stromu')), + ('succ', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='prev', to='treenode.treenode', verbose_name='další element na stejné úrovni')), + ], + options={ + 'verbose_name': 'TreeNode', + 'verbose_name_plural': 'TreeNody', + 'db_table': 'seminar_nodes_treenode', + 'managed': False, + }, + ), + migrations.CreateModel( + name='CastNode', + fields=[ + ('treenode_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='treenode.treenode')), + ('nadpis', models.CharField(help_text='Nadpis podvěšené části obsahu', max_length=100, verbose_name='Nadpis')), + ], + options={ + 'verbose_name': 'Část (Node)', + 'verbose_name_plural': 'Části (Node)', + 'db_table': 'seminar_nodes_cast', + 'managed': False, + }, + bases=('treenode.treenode',), + ), + migrations.CreateModel( + name='CisloNode', + fields=[ + ('treenode_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='treenode.treenode')), + ('cislo', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to='tvorba.cislo', verbose_name='číslo')), + ], + options={ + 'verbose_name': 'Číslo (Node)', + 'verbose_name_plural': 'Čísla (Node)', + 'db_table': 'seminar_nodes_cislo', + 'managed': False, + }, + bases=('treenode.treenode',), + ), + migrations.CreateModel( + name='MezicisloNode', + fields=[ + ('treenode_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='treenode.treenode')), + ], + options={ + 'verbose_name': 'Mezičíslo (Node)', + 'verbose_name_plural': 'Mezičísla (Node)', + 'db_table': 'seminar_nodes_mezicislo', + 'managed': False, + }, + bases=('treenode.treenode',), + ), + migrations.CreateModel( + name='OrgTextNode', + fields=[ + ('treenode_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='treenode.treenode')), + ('org_verejny', models.BooleanField(default=True, help_text='Pokud ano, bude org pod článkem podepsaný', verbose_name='Org je veřejný?')), + ('organizator', models.ForeignKey( on_delete=django.db.models.deletion.DO_NOTHING, to='personalni.organizator', verbose_name='Organizátor')), + ], + options={ + 'verbose_name': 'Organizátorský článek (Node)', + 'verbose_name_plural': 'Organizátorské články (Node)', + 'db_table': 'seminar_nodes_orgtextnode', + 'managed': False, + }, + bases=('treenode.treenode',), + ), + migrations.CreateModel( + name='PohadkaNode', + fields=[ + ('treenode_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='treenode.treenode')), + ('pohadka', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to='tvorba.pohadka', verbose_name='pohádka')), + ], + options={ + 'verbose_name': 'Pohádka (Node)', + 'verbose_name_plural': 'Pohádky (Node)', + 'db_table': 'seminar_nodes_pohadka', + 'managed': False, + }, + bases=('treenode.treenode',), + ), + migrations.CreateModel( + name='ReseniNode', + fields=[ + ('treenode_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='treenode.treenode')), + ('reseni', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='odevzdavatko.reseni', verbose_name='reseni')), + ], + options={ + 'verbose_name': 'Otištěné řešení (Node)', + 'verbose_name_plural': 'Otištěná řešení (Node)', + 'db_table': 'seminar_nodes_otistene_reseni', + 'managed': False, + }, + bases=('treenode.treenode',), + ), + migrations.CreateModel( + name='RocnikNode', + fields=[ + ('treenode_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='treenode.treenode')), + ('rocnik', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to='tvorba.rocnik', verbose_name='ročník')), + ], + options={ + 'verbose_name': 'Ročník (Node)', + 'verbose_name_plural': 'Ročníky (Node)', + 'db_table': 'seminar_nodes_rocnik', + 'managed': False, + }, + bases=('treenode.treenode',), + ), + migrations.CreateModel( + name='TemaVCisleNode', + fields=[ + ('treenode_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='treenode.treenode')), + ('tema', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tvorba.tema', verbose_name='téma v čísle')), + ], + options={ + 'verbose_name': 'Téma v čísle (Node)', + 'verbose_name_plural': 'Témata v čísle (Node)', + 'db_table': 'seminar_nodes_temavcisle', + 'managed': False, + }, + bases=('treenode.treenode',), + ), + migrations.CreateModel( + name='TextNode', + fields=[ + ('treenode_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='treenode.treenode')), + ('text', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='treenode.text', verbose_name='text')), + ], + options={ + 'verbose_name': 'Text (Node)', + 'verbose_name_plural': 'Text (Node)', + 'db_table': 'seminar_nodes_obsah', + 'managed': False, + }, + bases=('treenode.treenode',), + ), + migrations.CreateModel( + name='UlohaVzorakNode', + fields=[ + ('treenode_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='treenode.treenode')), + ('uloha', models.OneToOneField(null=True, on_delete=django.db.models.deletion.PROTECT, to='tvorba.uloha', verbose_name='úloha')), + ], + options={ + 'verbose_name': 'Vzorák úlohy (Node)', + 'verbose_name_plural': 'Vzoráky úloh (Node)', + 'db_table': 'seminar_nodes_uloha_vzorak', + 'managed': False, + }, + bases=('treenode.treenode',), + ), + migrations.CreateModel( + name='UlohaZadaniNode', + fields=[ + ('treenode_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='treenode.treenode')), + ('uloha', models.OneToOneField(null=True, on_delete=django.db.models.deletion.PROTECT, to='tvorba.uloha', verbose_name='úloha')), + ], + options={ + 'verbose_name': 'Zadání úlohy (Node)', + 'verbose_name_plural': 'Zadání úloh (Node)', + 'db_table': 'seminar_nodes_uloha_zadani', + 'managed': False, + }, + bases=('treenode.treenode',), + ), + migrations.RunPython(nastav_nove_contenttypes, nastav_stare_contenttypes), + ] diff --git a/treenode/migrations/0002_odstrel_treenode_manage.py b/treenode/migrations/0002_odstrel_treenode_manage.py new file mode 100644 index 00000000..240f14de --- /dev/null +++ b/treenode/migrations/0002_odstrel_treenode_manage.py @@ -0,0 +1,70 @@ +# Generated by Django 4.2.16 on 2024-11-02 20:50 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('treenode', '0001_odstrel_treenode_create'), + ('seminar', '0142_odstrel_treenode_delete'), + ] + + operations = [ + migrations.AlterModelOptions( + name='castnode', + options={'verbose_name': 'Část (Node)', 'verbose_name_plural': 'Části (Node)'}, + ), + migrations.AlterModelOptions( + name='cislonode', + options={'verbose_name': 'Číslo (Node)', 'verbose_name_plural': 'Čísla (Node)'}, + ), + migrations.AlterModelOptions( + name='mezicislonode', + options={'verbose_name': 'Mezičíslo (Node)', 'verbose_name_plural': 'Mezičísla (Node)'}, + ), + migrations.AlterModelOptions( + name='obrazek', + options={'verbose_name': 'obrázek', 'verbose_name_plural': 'obrázky'}, + ), + migrations.AlterModelOptions( + name='orgtextnode', + options={'verbose_name': 'Organizátorský článek (Node)', 'verbose_name_plural': 'Organizátorské články (Node)'}, + ), + migrations.AlterModelOptions( + name='pohadkanode', + options={'verbose_name': 'Pohádka (Node)', 'verbose_name_plural': 'Pohádky (Node)'}, + ), + migrations.AlterModelOptions( + name='reseninode', + options={'verbose_name': 'Otištěné řešení (Node)', 'verbose_name_plural': 'Otištěná řešení (Node)'}, + ), + migrations.AlterModelOptions( + name='rocniknode', + options={'verbose_name': 'Ročník (Node)', 'verbose_name_plural': 'Ročníky (Node)'}, + ), + migrations.AlterModelOptions( + name='temavcislenode', + options={'verbose_name': 'Téma v čísle (Node)', 'verbose_name_plural': 'Témata v čísle (Node)'}, + ), + migrations.AlterModelOptions( + name='text', + options={'verbose_name': 'text', 'verbose_name_plural': 'texty'}, + ), + migrations.AlterModelOptions( + name='textnode', + options={'verbose_name': 'Text (Node)', 'verbose_name_plural': 'Text (Node)'}, + ), + migrations.AlterModelOptions( + name='treenode', + options={'verbose_name': 'TreeNode', 'verbose_name_plural': 'TreeNody'}, + ), + migrations.AlterModelOptions( + name='ulohavzoraknode', + options={'verbose_name': 'Vzorák úlohy (Node)', 'verbose_name_plural': 'Vzoráky úloh (Node)'}, + ), + migrations.AlterModelOptions( + name='ulohazadaninode', + options={'verbose_name': 'Zadání úlohy (Node)', 'verbose_name_plural': 'Zadání úloh (Node)'}, + ), + ] diff --git a/treenode/migrations/0003_odstrel_treenode_post.py b/treenode/migrations/0003_odstrel_treenode_post.py new file mode 100644 index 00000000..a3479d8f --- /dev/null +++ b/treenode/migrations/0003_odstrel_treenode_post.py @@ -0,0 +1,13 @@ +# Generated by Django 4.2.16 on 2024-11-02 20:52 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('treenode', '0002_odstrel_treenode_manage'), + ] + + operations = [ + ] diff --git a/seminar/models/treenode.py b/treenode/models.py similarity index 73% rename from seminar/models/treenode.py rename to treenode/models.py index eee40281..8196d647 100644 --- a/seminar/models/treenode.py +++ b/treenode/models.py @@ -8,9 +8,10 @@ from unidecode import unidecode # Používám pro získání ID odkazu (ještě from polymorphic.models import PolymorphicModel -from personalni.models import Organizator +from seminar.models import SeminarModelBase -from .pomocne import Text +from personalni.models import Organizator +from odevzdavatko.models import Reseni logger = logging.getLogger(__name__) @@ -233,6 +234,58 @@ class UlohaVzorakNode(TreeNode): 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: @@ -249,7 +302,6 @@ class TextNode(TreeNode): def getOdkazStr(self): return str(self.text) - class CastNode(TreeNode): class Meta: db_table = 'seminar_nodes_cast' @@ -263,3 +315,36 @@ class CastNode(TreeNode): 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 + + diff --git a/tvorba/migrations/0004_odstrel_treenode_pre.py b/tvorba/migrations/0004_odstrel_treenode_pre.py new file mode 100644 index 00000000..75a7cc4a --- /dev/null +++ b/tvorba/migrations/0004_odstrel_treenode_pre.py @@ -0,0 +1,13 @@ +# Generated by Django 4.2.16 on 2024-11-02 19:45 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('tvorba', '0003_tvorba_post'), + ] + + operations = [ + ] diff --git a/tvorba/migrations/0005_odstrel_treenode_post.py b/tvorba/migrations/0005_odstrel_treenode_post.py new file mode 100644 index 00000000..ec3de11e --- /dev/null +++ b/tvorba/migrations/0005_odstrel_treenode_post.py @@ -0,0 +1,14 @@ +# Generated by Django 4.2.16 on 2024-11-02 20:52 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('tvorba', '0004_odstrel_treenode_pre'), + ('treenode', '0003_odstrel_treenode_post'), + ] + + operations = [ + ] diff --git a/tvorba/models.py b/tvorba/models.py index d7290ae3..d20eb1f8 100644 --- a/tvorba/models.py +++ b/tvorba/models.py @@ -297,7 +297,7 @@ class Cislo(SeminarModelBase): except ObjectDoesNotExist: # Neexistující *Node nemá smysl aktualizovat, ale je potřeba ho naopak vyrobit logger.warning(f'Číslo {self} nemělo ČísloNode, vyrábím…') - from seminar.models.treenode import CisloNode + from treenode.models import CisloNode CisloNode.objects.create(cislo=self) def zlomovy_deadline_pro_papirove_cislo(self): @@ -572,7 +572,7 @@ class Tema(Problem): def cislo_node(self): tema_node_set = self.temavcislenode_set.all() tema_cisla_vyskyt = [] - from seminar.models.treenode import CisloNode + from treenode.models import CisloNode for tn in tema_node_set: tema_cisla_vyskyt.append( treelib.get_upper_node_of_type(tn, CisloNode).cislo) @@ -648,7 +648,7 @@ class Uloha(Problem): def cislo_node(self): zadani_node = self.ulohazadaninode - from seminar.models.treenode import CisloNode + from treenode.models import CisloNode return treelib.get_upper_node_of_type(zadani_node, CisloNode)