From 792a8fa4dcf8d4e50aa7ad67ef8c24a11342a619 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Wed, 23 Oct 2024 00:31:21 +0200 Subject: [PATCH 01/51] pre --- .../migrations/0012_odstrel_odevzdavatka_pre.py | 13 +++++++++++++ .../migrations/0131_odstrel_odevzdavatka_pre.py | 14 ++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 personalni/migrations/0012_odstrel_odevzdavatka_pre.py create mode 100644 seminar/migrations/0131_odstrel_odevzdavatka_pre.py diff --git a/personalni/migrations/0012_odstrel_odevzdavatka_pre.py b/personalni/migrations/0012_odstrel_odevzdavatka_pre.py new file mode 100644 index 00000000..c712d421 --- /dev/null +++ b/personalni/migrations/0012_odstrel_odevzdavatka_pre.py @@ -0,0 +1,13 @@ +# Generated by Django 4.2.13 on 2024-10-22 22:17 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('personalni', '0011_osloveni_vsechny_choices'), + ] + + operations = [ + ] diff --git a/seminar/migrations/0131_odstrel_odevzdavatka_pre.py b/seminar/migrations/0131_odstrel_odevzdavatka_pre.py new file mode 100644 index 00000000..6d68cdc8 --- /dev/null +++ b/seminar/migrations/0131_odstrel_odevzdavatka_pre.py @@ -0,0 +1,14 @@ +# Generated by Django 4.2.13 on 2024-10-22 22:17 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0130_clanek_strana'), + ('personalni', '0012_odstrel_odevzdavatka_pre'), + ] + + operations = [ + ] From d1172b9d388800d784136fa4291720e9fd81ea94 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Wed, 23 Oct 2024 00:31:58 +0200 Subject: [PATCH 02/51] unm --- ...ns_alter_prilohareseni_options_and_more.py | 29 +++++++++++++++++++ seminar/models/odevzdavatko.py | 24 +++++++++------ 2 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 seminar/migrations/0132_alter_hodnoceni_options_alter_prilohareseni_options_and_more.py diff --git a/seminar/migrations/0132_alter_hodnoceni_options_alter_prilohareseni_options_and_more.py b/seminar/migrations/0132_alter_hodnoceni_options_alter_prilohareseni_options_and_more.py new file mode 100644 index 00000000..75575b0a --- /dev/null +++ b/seminar/migrations/0132_alter_hodnoceni_options_alter_prilohareseni_options_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 4.2.13 on 2024-10-22 22:31 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0131_odstrel_odevzdavatka_pre'), + ] + + operations = [ + migrations.AlterModelOptions( + name='hodnoceni', + options={'managed': False, 'verbose_name': 'Hodnocení', 'verbose_name_plural': 'Hodnocení'}, + ), + migrations.AlterModelOptions( + name='prilohareseni', + options={'managed': False, 'ordering': ['reseni', 'vytvoreno'], 'verbose_name': 'Příloha řešení', 'verbose_name_plural': 'Přílohy řešení'}, + ), + migrations.AlterModelOptions( + name='reseni', + options={'managed': False, 'ordering': ['-cas_doruceni'], 'verbose_name': 'Řešení', 'verbose_name_plural': 'Řešení'}, + ), + migrations.AlterModelOptions( + name='reseni_resitele', + options={'managed': False, 'ordering': ['reseni', 'resitele'], 'verbose_name': 'Řešení řešitelů', 'verbose_name_plural': 'Řešení řešitelů'}, + ), + ] diff --git a/seminar/models/odevzdavatko.py b/seminar/models/odevzdavatko.py index 0c106df7..bebc74f8 100644 --- a/seminar/models/odevzdavatko.py +++ b/seminar/models/odevzdavatko.py @@ -26,15 +26,18 @@ class Reseni(bm.SeminarModelBase): verbose_name_plural = 'Řešení' #ordering = ['-problem', 'resitele'] # FIXME: Takhle to chceme, ale nefunguje to. ordering = ['-cas_doruceni'] + managed = False # Interní ID id = models.AutoField(primary_key = True) # Ke každé dvojici řešní a problém existuje nanejvýš jedno hodnocení, doplnění vazby. problem = models.ManyToManyField(am.Problem, verbose_name='problém', help_text='Problém', + related_name='problem_old', through='Hodnoceni') resitele = models.ManyToManyField(Resitel, verbose_name='autoři řešení', + related_name='resitele_old', help_text='Seznam autorů řešení', through='Reseni_Resitele') @@ -51,8 +54,8 @@ 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('ReseniNode', verbose_name='Plná verze textu řešení', - blank=True, null=True, related_name="reseni_cely_set", + text_cely = models.OneToOneField('seminar.ReseniNode', verbose_name='Plná verze textu řešení', + blank=True, null=True, related_name="reseni_cely_set_old", on_delete=models.PROTECT) poznamka = models.TextField('neveřejná poznámka', blank=True, @@ -95,6 +98,7 @@ class Hodnoceni(bm.SeminarModelBase): db_table = 'seminar_hodnoceni' verbose_name = 'Hodnocení' verbose_name_plural = 'Hodnocení' + managed = False # Interní ID id = models.AutoField(primary_key = True) @@ -104,16 +108,16 @@ class Hodnoceni(bm.SeminarModelBase): blank=True, null=True) cislo_body = models.ForeignKey(am.Cislo, verbose_name='číslo pro body', - related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT) + related_name='hodnoceni_old', blank=True, null=True, on_delete=models.PROTECT) # V ročníku < 26 nastaveno na deadline vygenerovaný pro původní cislo_body deadline_body = models.ForeignKey(am.Deadline, verbose_name='deadline pro body', - related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT) + related_name='hodnoceni_old', blank=True, null=True, on_delete=models.PROTECT) - reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) + reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE, related_name='hodnoceni_old') problem = models.ForeignKey(am.Problem, verbose_name='problém', - related_name='hodnoceni', on_delete=models.PROTECT) + related_name='hodnoceni_old', on_delete=models.PROTECT) feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)') @@ -190,11 +194,12 @@ class PrilohaReseni(bm.SeminarModelBase): verbose_name = 'Příloha řešení' verbose_name_plural = 'Přílohy řešení' ordering = ['reseni', 'vytvoreno'] + managed = False # Interní ID id = models.AutoField(primary_key = True) - reseni = models.ForeignKey(Reseni, verbose_name='řešení', related_name='prilohy', + reseni = models.ForeignKey(Reseni, verbose_name='řešení', related_name='prilohy_old', on_delete=models.CASCADE) vytvoreno = models.DateTimeField('vytvořeno', default=timezone.now, blank=True, editable=False) @@ -225,13 +230,14 @@ class Reseni_Resitele(models.Model): verbose_name = 'Řešení řešitelů' verbose_name_plural = 'Řešení řešitelů' ordering = ['reseni', 'resitele'] + managed = False # Interní ID id = models.AutoField(primary_key = True) - resitele = models.ForeignKey(Resitel, verbose_name='řešitel', on_delete=models.PROTECT) + resitele = models.ForeignKey(Resitel, verbose_name='řešitel', on_delete=models.PROTECT, related_name='rere_old') - reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) + reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE, related_name='rere_old') # podil - jakou merou se ktery resitel podilel na danem reseni # - pouziti v budoucnu, pokud by resitele nemeli dostat vsichni stejne bodu za spolecne reseni From 49e93025c00aec7893ebe5a98ebd57f3f2191dbf Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Wed, 23 Oct 2024 01:01:09 +0200 Subject: [PATCH 03/51] =?UTF-8?q?unm=202:=20zm=C4=9Bna=20jm=C3=A9na?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...hareseni_options_and_more.py => 0132_unmanage_odevzdavatko.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename seminar/migrations/{0132_alter_hodnoceni_options_alter_prilohareseni_options_and_more.py => 0132_unmanage_odevzdavatko.py} (100%) diff --git a/seminar/migrations/0132_alter_hodnoceni_options_alter_prilohareseni_options_and_more.py b/seminar/migrations/0132_unmanage_odevzdavatko.py similarity index 100% rename from seminar/migrations/0132_alter_hodnoceni_options_alter_prilohareseni_options_and_more.py rename to seminar/migrations/0132_unmanage_odevzdavatko.py From 33381b43070be982a51e049eefadc60387c8508b Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Wed, 23 Oct 2024 21:46:19 +0200 Subject: [PATCH 04/51] create --- odevzdavatko/migrations/0001_create.py | 99 ++++++++++ odevzdavatko/models.py | 243 ++++++++++++++++++++++++ seminar/models/__init__.py | 3 + seminar/models/odevzdavatko.py | 245 +------------------------ split-apps-meta/create.notes | 1 + 5 files changed, 347 insertions(+), 244 deletions(-) create mode 100644 odevzdavatko/migrations/0001_create.py create mode 100644 odevzdavatko/models.py diff --git a/odevzdavatko/migrations/0001_create.py b/odevzdavatko/migrations/0001_create.py new file mode 100644 index 00000000..22ee11ed --- /dev/null +++ b/odevzdavatko/migrations/0001_create.py @@ -0,0 +1,99 @@ +# Generated by Django 4.2.13 on 2024-10-22 22:51 + +from django.db import migrations, models +import django.utils.timezone +import odevzdavatko.models + +def nastav_nove_contenttypes(apps, schema_editor): + ContentType = apps.get_model('contenttypes', 'ContentType') + for m in ('reseni', 'hodnoceni', 'reseni_resitele', 'prilohareseni'): + ContentType.objects.filter(app_label='seminar', model=m).update(app_label='odevzdavatko') + +def nastav_stare_contenttypes(apps, schema_editor): + ContentType = apps.get_model('contenttypes', 'ContentType') + for m in ('reseni', 'hodnoceni', 'reseni_resitele', 'prilohareseni'): + ContentType.objects.filter(app_label='odevzdavatko', model=m).update(app_label='seminar') + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('seminar', '0132_unmanage_odevzdavatko'), + ] + + operations = [ + migrations.CreateModel( + name='Hodnoceni', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('body', models.DecimalField(blank=True, decimal_places=1, max_digits=8, null=True, verbose_name='body')), + ('feedback', models.TextField(blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)', verbose_name='zpětná vazba')), + ('cislo_body', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='seminar.cislo', verbose_name='číslo pro body')), + ('deadline_body', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='seminar.deadline', verbose_name='deadline pro body')), + ('problem', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='seminar.problem', verbose_name='problém')), + ('reseni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='odevzdavatko.reseni', verbose_name='řešení')), + ], + options={ + 'verbose_name': 'Hodnocení', + 'verbose_name_plural': 'Hodnocení', + 'db_table': 'seminar_hodnoceni', + 'managed': False, + }, + ), + migrations.CreateModel( + name='PrilohaReseni', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('vytvoreno', models.DateTimeField(blank=True, default=django.utils.timezone.now, editable=False, verbose_name='vytvořeno')), + ('soubor', models.FileField(upload_to=odevzdavatko.models.generate_filename, verbose_name='soubor')), + ('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu', verbose_name='neveřejná poznámka')), + ('res_poznamka', models.TextField(blank=True, help_text='Poznámka k příloze řešení, např. co daný soubor obsahuje', verbose_name='poznámka řešitele')), + ('reseni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='prilohy', to='odevzdavatko.reseni', verbose_name='řešení')), + ], + options={ + 'verbose_name': 'Příloha řešení', + 'verbose_name_plural': 'Přílohy řešení', + 'db_table': 'seminar_priloha_reseni', + 'ordering': ['reseni', 'vytvoreno'], + 'managed': False, + }, + ), + migrations.CreateModel( + name='Reseni', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('cas_doruceni', models.DateTimeField(blank=True, default=django.utils.timezone.now, verbose_name='čas_doručení')), + ('forma', models.CharField(choices=[('papir', 'Papírové řešení'), ('email', 'Emailem'), ('upload', 'Upload přes web')], default='email', max_length=16, verbose_name='forma řešení')), + ('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k řešení (plain text)', verbose_name='neveřejná poznámka')), + ('zverejneno', models.BooleanField(default=False, help_text='Udává, zda je řešení zveřejněno', verbose_name='řešení zveřejněno')), + ('problem', models.ManyToManyField(help_text='Problém', through='odevzdavatko.Hodnoceni', to='seminar.problem', verbose_name='problém')), + ('resitele', models.ManyToManyField(help_text='Seznam autorů řešení', through='odevzdavatko.Reseni_Resitele', to='personalni.resitel', verbose_name='autoři řešení')), + ('text_cely', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='reseni_cely_set', to='seminar.reseninode', verbose_name='Plná verze textu řešení')), + ], + options={ + 'verbose_name': 'Řešení', + 'verbose_name_plural': 'Řešení', + 'db_table': 'seminar_reseni', + 'ordering': ['-cas_doruceni'], + 'managed': False, + }, + ), + migrations.CreateModel( + name='Reseni_Resitele', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('reseni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='odevzdavatko.reseni', verbose_name='řešení')), + ('resitele', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='personalni.resitel', verbose_name='řešitel')), + ], + options={ + 'verbose_name': 'Řešení řešitelů', + 'verbose_name_plural': 'Řešení řešitelů', + 'db_table': 'seminar_reseni_resitele', + 'ordering': ['reseni', 'resitele'], + 'managed': False, + }, + ), + migrations.RunPython(nastav_nove_contenttypes, nastav_stare_contenttypes), + + ] diff --git a/odevzdavatko/models.py b/odevzdavatko/models.py new file mode 100644 index 00000000..65ab9a37 --- /dev/null +++ b/odevzdavatko/models.py @@ -0,0 +1,243 @@ +import os + +import reversion + +from django.contrib.sites.shortcuts import get_current_site +from django.db import models +from django.db.models import Sum +from django.urls import reverse_lazy +from django.utils import timezone +from django.conf import settings + +from seminar.models import tvorba as am +from seminar.models import base as bm + +from odevzdavatko.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet +from personalni.models import Resitel + +@reversion.register(ignore_duplicates=True) +class Reseni(bm.SeminarModelBase): + + class Meta: + db_table = 'seminar_reseni' + verbose_name = 'Řešení' + verbose_name_plural = 'Řešení' + #ordering = ['-problem', 'resitele'] # FIXME: Takhle to chceme, ale nefunguje to. + ordering = ['-cas_doruceni'] + managed = False + + # Interní ID + id = models.AutoField(primary_key = True) + + # Ke každé dvojici řešní a problém existuje nanejvýš jedno hodnocení, doplnění vazby. + problem = models.ManyToManyField(am.Problem, verbose_name='problém', help_text='Problém', + through='Hodnoceni') + + resitele = models.ManyToManyField(Resitel, verbose_name='autoři řešení', + help_text='Seznam autorů řešení', through='Reseni_Resitele') + + + cas_doruceni = models.DateTimeField('čas_doručení', default=timezone.now, blank=True) + + FORMA_PAPIR = 'papir' + FORMA_EMAIL = 'email' + FORMA_UPLOAD = 'upload' + FORMA_CHOICES = [ + (FORMA_PAPIR, 'Papírové řešení'), + (FORMA_EMAIL, 'Emailem'), + (FORMA_UPLOAD, 'Upload přes web'), + ] + 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í', + blank=True, null=True, related_name="reseni_cely_set", + on_delete=models.PROTECT) + + poznamka = models.TextField('neveřejná poznámka', blank=True, + help_text='Neveřejná poznámka k řešení (plain text)') + + zverejneno = models.BooleanField('řešení zveřejněno', default=False, + help_text='Udává, zda je řešení zveřejněno') + + def verejne_url(self): + return str(reverse_lazy('odevzdavatko_detail_reseni', args=[self.id])) + + def absolute_url(self): + return "https://" + str(get_current_site(None)) + self.verejne_url() + + # má OneToOneField s: + # Konfera + + # má ForeignKey s: + # Hodnoceni + + def sum_body(self): + return self.hodnoceni_set.all().aggregate(Sum('body'))["body__sum"] + + def __str__(self): + return "{}({}): {}({})".format(self.resitele.first(),len(self.resitele.all()), self.problem.first() ,len(self.problem.all())) + # NOTE: Potenciální DB HOG (bez select_related) + + def deadline_reseni(self): + return am.Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first() + +## Pravdepodobne uz nebude potreba: +# def save(self, *args, **kwargs): +# if ((self.cislo_body is None) and (self.problem.cislo_reseni) and +# (self.problem.typ == Problem.TYP_ULOHA)): +# self.cislo_body = self.problem.cislo_reseni +# super(Reseni, self).save(*args, **kwargs) + +class Hodnoceni(bm.SeminarModelBase): + class Meta: + db_table = 'seminar_hodnoceni' + verbose_name = 'Hodnocení' + verbose_name_plural = 'Hodnocení' + managed = False + + # Interní ID + id = models.AutoField(primary_key = True) + + + body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body', + blank=True, null=True) + + cislo_body = models.ForeignKey(am.Cislo, verbose_name='číslo pro body', + related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT) + + # V ročníku < 26 nastaveno na deadline vygenerovaný pro původní cislo_body + deadline_body = models.ForeignKey(am.Deadline, verbose_name='deadline pro body', + related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT) + + reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) + + problem = models.ForeignKey(am.Problem, verbose_name='problém', + related_name='hodnoceni', on_delete=models.PROTECT) + + feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)') + + @property + def body_celkem(self): + # FIXME řeším jen prvního řešitele. + return Hodnoceni.objects.filter(problem=self.problem, reseni__resitele=self.reseni.resitele.first(), body__isnull=False).aggregate(Sum("body"))["body__sum"] + + @body_celkem.setter + def body_celkem(self, value): + if value is None: + self.body = None + else: + if self.body is None: + self.body = 0 + if self.body_celkem is None: + self.body += value + else: + self.body += value - self.body_celkem + + @property + def body_neprepocitane(self): + if self.body is None: + return None + return inverze_vzorecku_na_prepocet(self.body, self.reseni.resitele.count()) + + @body_neprepocitane.setter + def body_neprepocitane(self, value): + if value is None: + self.body = None + else: + self.body = vzorecek_na_prepocet(value, self.reseni.resitele.count()) + + @property + def body_neprepocitane_celkem(self): + if self.body_celkem is None: + return None + return inverze_vzorecku_na_prepocet(self.body_celkem, self.reseni.resitele.count()) + + @body_neprepocitane_celkem.setter + def body_neprepocitane_celkem(self, value): + if value is None: + self.body = None + else: + self.body_celkem = vzorecek_na_prepocet(value, self.reseni.resitele.count()) + + @property + def body_max(self): + if self.body_neprepocitane_max is None: + return None + return vzorecek_na_prepocet(self.body_neprepocitane_max, self.reseni.resitele.count()) + + @property + def body_neprepocitane_max(self): + if not isinstance(self.problem.get_real_instance(), am.Uloha): + return None + return self.problem.uloha.max_body + + def __str__(self): + return "{}, {}, {}".format(self.problem, self.reseni, self.body) + +def generate_filename(self, filename): + return os.path.join( + settings.SEMINAR_RESENI_DIR, + am.aux_generate_filename(self, filename) + ) + + +@reversion.register(ignore_duplicates=True) +class PrilohaReseni(bm.SeminarModelBase): + + class Meta: + db_table = 'seminar_priloha_reseni' + verbose_name = 'Příloha řešení' + verbose_name_plural = 'Přílohy řešení' + ordering = ['reseni', 'vytvoreno'] + managed = False + + # Interní ID + id = models.AutoField(primary_key = True) + + reseni = models.ForeignKey(Reseni, verbose_name='řešení', related_name='prilohy', + on_delete=models.CASCADE) + + vytvoreno = models.DateTimeField('vytvořeno', default=timezone.now, blank=True, editable=False) + + soubor = models.FileField('soubor', upload_to = generate_filename) + + poznamka = models.TextField('neveřejná poznámka', blank=True, + help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu') + + res_poznamka = models.TextField('poznámka řešitele', blank=True, + help_text='Poznámka k příloze řešení, např. co daný soubor obsahuje') + + def __str__(self): + return str(self.soubor) + + def split(self): + "Vrátí cestu rozsekanou po složkách. To se hodí v templatech" + # Věřím, že tohle funguje, případně použít os.path nebo pathlib. + return self.soubor.url.split('/') + + +# Vazebna tabulka. Mozna se generuje automaticky. +@reversion.register(ignore_duplicates=True) +class Reseni_Resitele(models.Model): + + class Meta: + db_table = 'seminar_reseni_resitele' + verbose_name = 'Řešení řešitelů' + verbose_name_plural = 'Řešení řešitelů' + ordering = ['reseni', 'resitele'] + managed = False + + # Interní ID + id = models.AutoField(primary_key = True) + + resitele = models.ForeignKey(Resitel, verbose_name='řešitel', on_delete=models.PROTECT) + + reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) + + # podil - jakou merou se ktery resitel podilel na danem reseni + # - pouziti v budoucnu, pokud by resitele nemeli dostat vsichni stejne bodu za spolecne reseni + + def __str__(self): + return '{} od {}'.format(self.reseni, self.resitel) + # NOTE: Poteciální DB HOG bez select_related diff --git a/seminar/models/__init__.py b/seminar/models/__init__.py index e404864a..4bc85266 100644 --- a/seminar/models/__init__.py +++ b/seminar/models/__init__.py @@ -8,6 +8,9 @@ from various.models import Nastaveni from personalni.models import Organizator, Resitel, Skola, Prijemce, Osoba from soustredeni.models import Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Konfera, Konfery_Ucastnici from novinky.models import Novinky +from odevzdavatko.models import Reseni, PrilohaReseni, Reseni_Resitele, Hodnoceni # Kvůli migr. 0041 from soustredeni.models import generate_filename_konfera +# migr. 0001 +from odevzdavatko.models import generate_filename diff --git a/seminar/models/odevzdavatko.py b/seminar/models/odevzdavatko.py index bebc74f8..efc88e74 100644 --- a/seminar/models/odevzdavatko.py +++ b/seminar/models/odevzdavatko.py @@ -1,250 +1,7 @@ -import os - -import reversion - -from django.contrib.sites.shortcuts import get_current_site from django.db import models -from django.db.models import Sum -from django.urls import reverse_lazy -from django.utils import timezone -from django.conf import settings -from seminar.models import tvorba as am from seminar.models import treenode as tm -from seminar.models import base as bm - -from odevzdavatko.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet -from personalni.models import Resitel - - -@reversion.register(ignore_duplicates=True) -class Reseni(bm.SeminarModelBase): - - class Meta: - db_table = 'seminar_reseni' - verbose_name = 'Řešení' - verbose_name_plural = 'Řešení' - #ordering = ['-problem', 'resitele'] # FIXME: Takhle to chceme, ale nefunguje to. - ordering = ['-cas_doruceni'] - managed = False - - # Interní ID - id = models.AutoField(primary_key = True) - - # Ke každé dvojici řešní a problém existuje nanejvýš jedno hodnocení, doplnění vazby. - problem = models.ManyToManyField(am.Problem, verbose_name='problém', help_text='Problém', - related_name='problem_old', - through='Hodnoceni') - - resitele = models.ManyToManyField(Resitel, verbose_name='autoři řešení', - related_name='resitele_old', - help_text='Seznam autorů řešení', through='Reseni_Resitele') - - - cas_doruceni = models.DateTimeField('čas_doručení', default=timezone.now, blank=True) - - FORMA_PAPIR = 'papir' - FORMA_EMAIL = 'email' - FORMA_UPLOAD = 'upload' - FORMA_CHOICES = [ - (FORMA_PAPIR, 'Papírové řešení'), - (FORMA_EMAIL, 'Emailem'), - (FORMA_UPLOAD, 'Upload přes web'), - ] - 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í', - blank=True, null=True, related_name="reseni_cely_set_old", - on_delete=models.PROTECT) - - poznamka = models.TextField('neveřejná poznámka', blank=True, - help_text='Neveřejná poznámka k řešení (plain text)') - - zverejneno = models.BooleanField('řešení zveřejněno', default=False, - help_text='Udává, zda je řešení zveřejněno') - - def verejne_url(self): - return str(reverse_lazy('odevzdavatko_detail_reseni', args=[self.id])) - - def absolute_url(self): - return "https://" + str(get_current_site(None)) + self.verejne_url() - - # má OneToOneField s: - # Konfera - - # má ForeignKey s: - # Hodnoceni - - def sum_body(self): - return self.hodnoceni_set.all().aggregate(Sum('body'))["body__sum"] - - def __str__(self): - return "{}({}): {}({})".format(self.resitele.first(),len(self.resitele.all()), self.problem.first() ,len(self.problem.all())) - # NOTE: Potenciální DB HOG (bez select_related) - - def deadline_reseni(self): - return am.Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first() - -## Pravdepodobne uz nebude potreba: -# def save(self, *args, **kwargs): -# if ((self.cislo_body is None) and (self.problem.cislo_reseni) and -# (self.problem.typ == Problem.TYP_ULOHA)): -# self.cislo_body = self.problem.cislo_reseni -# super(Reseni, self).save(*args, **kwargs) - -class Hodnoceni(bm.SeminarModelBase): - class Meta: - db_table = 'seminar_hodnoceni' - verbose_name = 'Hodnocení' - verbose_name_plural = 'Hodnocení' - managed = False - - # Interní ID - id = models.AutoField(primary_key = True) - - - body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body', - blank=True, null=True) - - cislo_body = models.ForeignKey(am.Cislo, verbose_name='číslo pro body', - related_name='hodnoceni_old', blank=True, null=True, on_delete=models.PROTECT) - - # V ročníku < 26 nastaveno na deadline vygenerovaný pro původní cislo_body - deadline_body = models.ForeignKey(am.Deadline, verbose_name='deadline pro body', - related_name='hodnoceni_old', blank=True, null=True, on_delete=models.PROTECT) - - reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE, related_name='hodnoceni_old') - - problem = models.ForeignKey(am.Problem, verbose_name='problém', - related_name='hodnoceni_old', on_delete=models.PROTECT) - - feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)') - - @property - def body_celkem(self): - # FIXME řeším jen prvního řešitele. - return Hodnoceni.objects.filter(problem=self.problem, reseni__resitele=self.reseni.resitele.first(), body__isnull=False).aggregate(Sum("body"))["body__sum"] - - @body_celkem.setter - def body_celkem(self, value): - if value is None: - self.body = None - else: - if self.body is None: - self.body = 0 - if self.body_celkem is None: - self.body += value - else: - self.body += value - self.body_celkem - - @property - def body_neprepocitane(self): - if self.body is None: - return None - return inverze_vzorecku_na_prepocet(self.body, self.reseni.resitele.count()) - - @body_neprepocitane.setter - def body_neprepocitane(self, value): - if value is None: - self.body = None - else: - self.body = vzorecek_na_prepocet(value, self.reseni.resitele.count()) - - @property - def body_neprepocitane_celkem(self): - if self.body_celkem is None: - return None - return inverze_vzorecku_na_prepocet(self.body_celkem, self.reseni.resitele.count()) - - @body_neprepocitane_celkem.setter - def body_neprepocitane_celkem(self, value): - if value is None: - self.body = None - else: - self.body_celkem = vzorecek_na_prepocet(value, self.reseni.resitele.count()) - - @property - def body_max(self): - if self.body_neprepocitane_max is None: - return None - return vzorecek_na_prepocet(self.body_neprepocitane_max, self.reseni.resitele.count()) - - @property - def body_neprepocitane_max(self): - if not isinstance(self.problem.get_real_instance(), am.Uloha): - return None - return self.problem.uloha.max_body - - def __str__(self): - return "{}, {}, {}".format(self.problem, self.reseni, self.body) - -def generate_filename(self, filename): - return os.path.join( - settings.SEMINAR_RESENI_DIR, - am.aux_generate_filename(self, filename) - ) - - -@reversion.register(ignore_duplicates=True) -class PrilohaReseni(bm.SeminarModelBase): - - class Meta: - db_table = 'seminar_priloha_reseni' - verbose_name = 'Příloha řešení' - verbose_name_plural = 'Přílohy řešení' - ordering = ['reseni', 'vytvoreno'] - managed = False - - # Interní ID - id = models.AutoField(primary_key = True) - - reseni = models.ForeignKey(Reseni, verbose_name='řešení', related_name='prilohy_old', - on_delete=models.CASCADE) - - vytvoreno = models.DateTimeField('vytvořeno', default=timezone.now, blank=True, editable=False) - - soubor = models.FileField('soubor', upload_to = generate_filename) - - poznamka = models.TextField('neveřejná poznámka', blank=True, - help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu') - - res_poznamka = models.TextField('poznámka řešitele', blank=True, - help_text='Poznámka k příloze řešení, např. co daný soubor obsahuje') - - def __str__(self): - return str(self.soubor) - - def split(self): - "Vrátí cestu rozsekanou po složkách. To se hodí v templatech" - # Věřím, že tohle funguje, případně použít os.path nebo pathlib. - return self.soubor.url.split('/') - - -# Vazebna tabulka. Mozna se generuje automaticky. -@reversion.register(ignore_duplicates=True) -class Reseni_Resitele(models.Model): - - class Meta: - db_table = 'seminar_reseni_resitele' - verbose_name = 'Řešení řešitelů' - verbose_name_plural = 'Řešení řešitelů' - ordering = ['reseni', 'resitele'] - managed = False - - # Interní ID - id = models.AutoField(primary_key = True) - - resitele = models.ForeignKey(Resitel, verbose_name='řešitel', on_delete=models.PROTECT, related_name='rere_old') - - reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE, related_name='rere_old') - - # podil - jakou merou se ktery resitel podilel na danem reseni - # - pouziti v budoucnu, pokud by resitele nemeli dostat vsichni stejne bodu za spolecne reseni - - def __str__(self): - return '{} od {}'.format(self.reseni, self.resitel) - # NOTE: Poteciální DB HOG bez select_related +from odevzdavatko.models import Reseni class ReseniNode(tm.TreeNode): class Meta: diff --git a/split-apps-meta/create.notes b/split-apps-meta/create.notes index a48a17f8..2e9f6228 100644 --- a/split-apps-meta/create.notes +++ b/split-apps-meta/create.notes @@ -5,5 +5,6 @@ makemigrations ! Doplnit ForeignKeys (Vypadá to, že se dá vesměs zkopírovat předpis z models.py, jen místo prvního fieldu dát `to='app.model'. Dokonce asi funguje použít už novou aplikaci pro vazby v rámci aplikace.) To samé s ManyToManyFieldy (through= musí taky být 'app.model') (Zdá se, že jde dobastlit tuhle migraci polozpětně – doplnit co chybělo až podle toho, co vygeneruje migrace po zamanagování nového modelu.) + Alternativa: zagitovat si unmanaged model, upravit ho na `managed = True`, vyrobit migrace, vyrobit je ještě jednou (z nějakého důvodu) a vykrást ty. Pak `models.py` vrátit do unmanaged stavu a soubory s novými migracemi smazat bez náhrady (obdobné vzniknou znovu v případě potřeby). doplnit závislost na unmanage migrate From 130907174d483ac65171ff0e30930749212f98ff Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Wed, 23 Oct 2024 21:54:49 +0200 Subject: [PATCH 05/51] relink --- .../migrations/0133_relink_odevzdavatko.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 seminar/migrations/0133_relink_odevzdavatko.py diff --git a/seminar/migrations/0133_relink_odevzdavatko.py b/seminar/migrations/0133_relink_odevzdavatko.py new file mode 100644 index 00000000..e0b7d93a --- /dev/null +++ b/seminar/migrations/0133_relink_odevzdavatko.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.13 on 2024-10-23 19:53 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('odevzdavatko', '0001_create'), + ('seminar', '0132_unmanage_odevzdavatko'), + ] + + operations = [ + migrations.AlterField( + model_name='reseninode', + name='reseni', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='odevzdavatko.reseni', verbose_name='reseni'), + ), + ] From d288fefeccc84110b4e0654d8d11a26988f529dd Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Wed, 23 Oct 2024 22:54:14 +0200 Subject: [PATCH 06/51] =?UTF-8?q?Zru=C5=A1en=C3=AD=20do=C4=8Dasn=C3=BDch?= =?UTF-8?q?=20odkaz=C5=AF=20do=20`seminar.models.*`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Poznámky: - `seminar.models.base` je v pořádku (není to dočasný soubor) - dočasné importy v `seminar/models/*.py` jsou taky validní, protože odtamtud zmizí. Jde mi o to, aby náhodné věci buď používaly už-správnou aplikaci, nebo postaru `seminar.models` jako celek, aby během odstřelu nebylo potřeba všechny věci přepisovat. Ano, tohle je potenciálně kontroverzní commit. Ale je dočasný a mně poněkud rozbíjí workflow, když mi v náhodné okamžiky odmítne běžet náhodná část webu na kterou nesahám jen kvůli tomu, že importuje věci odněkud, odkud to nečekám. Alternativní řešení: dát správné importy (s dočasnými `seminar.models.*`) do správných `aplikace/models.py` už teď a importovat věci rovnou z výsledných modulů. To zajišťuje jak konzistenci, tak to, že při odstřelu se cesta změní na jednom očekávaném místě (resp. spíš se prostě smaže) a všechno ostatní bude pokračovat ve fungování. --- odevzdavatko/models.py | 2 +- odevzdavatko/testutils.py | 2 +- soustredeni/models.py | 2 +- soustredeni/testutils.py | 2 +- tvorba/admin.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/odevzdavatko/models.py b/odevzdavatko/models.py index 65ab9a37..ad41d4ec 100644 --- a/odevzdavatko/models.py +++ b/odevzdavatko/models.py @@ -9,7 +9,7 @@ from django.urls import reverse_lazy from django.utils import timezone from django.conf import settings -from seminar.models import tvorba as am +import seminar.models as am # tvorba from seminar.models import base as bm from odevzdavatko.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet diff --git a/odevzdavatko/testutils.py b/odevzdavatko/testutils.py index 1f382438..c1240ab4 100644 --- a/odevzdavatko/testutils.py +++ b/odevzdavatko/testutils.py @@ -1,7 +1,7 @@ import datetime import random -from seminar.models.odevzdavatko import Reseni, Hodnoceni +from odevzdavatko.models import Reseni, Hodnoceni def gen_reseni_ulohy(rnd, cisla, uloha, pocet_resitelu, poradi_cisla, resitele_cisla, resitele): diff --git a/soustredeni/models.py b/soustredeni/models.py index f71e6736..56db6974 100644 --- a/soustredeni/models.py +++ b/soustredeni/models.py @@ -10,7 +10,7 @@ from django.conf import settings from personalni.models import Resitel, Organizator from seminar.models.base import SeminarModelBase -from seminar.models import tvorba as am +import seminar.models as am # tvorba logger = logging.getLogger(__name__) diff --git a/soustredeni/testutils.py b/soustredeni/testutils.py index 52e81d1c..6591cbb3 100644 --- a/soustredeni/testutils.py +++ b/soustredeni/testutils.py @@ -6,7 +6,7 @@ from typing import Sequence import lorem from .models import Soustredeni, Konfera -import seminar.models.tvorba as am +import seminar.models as am # tvorba import personalni.models as pm logger = logging.getLogger(__name__) diff --git a/tvorba/admin.py b/tvorba/admin.py index e6c2c64b..01880d5b 100644 --- a/tvorba/admin.py +++ b/tvorba/admin.py @@ -9,7 +9,7 @@ from django.utils.safestring import mark_safe import soustredeni.models -from seminar.models.tvorba import Rocnik, ZmrazenaVysledkovka, Deadline, Uloha, Problem, Tema, Clanek, Cislo +from seminar.models import Rocnik, ZmrazenaVysledkovka, Deadline, Uloha, Problem, Tema, Clanek, Cislo # tvorba admin.site.register(Rocnik) admin.site.register(ZmrazenaVysledkovka) From 10a824719bf8a504c98398c735603e8a54ef3e5d Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Wed, 23 Oct 2024 23:01:54 +0200 Subject: [PATCH 07/51] delete --- .../migrations/0134_delete_odevzdavatko.py | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 seminar/migrations/0134_delete_odevzdavatko.py diff --git a/seminar/migrations/0134_delete_odevzdavatko.py b/seminar/migrations/0134_delete_odevzdavatko.py new file mode 100644 index 00000000..479b9d6c --- /dev/null +++ b/seminar/migrations/0134_delete_odevzdavatko.py @@ -0,0 +1,50 @@ +# Generated by Django 4.2.13 on 2024-10-23 19:56 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0133_relink_odevzdavatko'), + ('odevzdavatko', '0001_create'), + ] + + operations = [ + migrations.RemoveField( + model_name='prilohareseni', + name='reseni', + ), + migrations.RemoveField( + model_name='reseni', + name='problem', + ), + migrations.RemoveField( + model_name='reseni', + name='resitele', + ), + migrations.RemoveField( + model_name='reseni', + name='text_cely', + ), + migrations.RemoveField( + model_name='reseni_resitele', + name='reseni', + ), + migrations.RemoveField( + model_name='reseni_resitele', + name='resitele', + ), + migrations.DeleteModel( + name='Hodnoceni', + ), + migrations.DeleteModel( + name='PrilohaReseni', + ), + migrations.DeleteModel( + name='Reseni', + ), + migrations.DeleteModel( + name='Reseni_Resitele', + ), + ] From 07e9c8b7e4f40fd58e0348497ed7de48db4b5104 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Wed, 23 Oct 2024 23:09:19 +0200 Subject: [PATCH 08/51] manage --- odevzdavatko/migrations/0002_manage.py | 30 ++++++++++++++++++++++++++ odevzdavatko/models.py | 4 ---- 2 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 odevzdavatko/migrations/0002_manage.py diff --git a/odevzdavatko/migrations/0002_manage.py b/odevzdavatko/migrations/0002_manage.py new file mode 100644 index 00000000..73c35288 --- /dev/null +++ b/odevzdavatko/migrations/0002_manage.py @@ -0,0 +1,30 @@ +# Generated by Django 4.2.13 on 2024-10-23 21:07 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('odevzdavatko', '0001_create'), + ('seminar', '0134_delete_odevzdavatko'), + ] + + operations = [ + migrations.AlterModelOptions( + name='hodnoceni', + options={'verbose_name': 'Hodnocení', 'verbose_name_plural': 'Hodnocení'}, + ), + migrations.AlterModelOptions( + name='prilohareseni', + options={'ordering': ['reseni', 'vytvoreno'], 'verbose_name': 'Příloha řešení', 'verbose_name_plural': 'Přílohy řešení'}, + ), + migrations.AlterModelOptions( + name='reseni', + options={'ordering': ['-cas_doruceni'], 'verbose_name': 'Řešení', 'verbose_name_plural': 'Řešení'}, + ), + migrations.AlterModelOptions( + name='reseni_resitele', + options={'ordering': ['reseni', 'resitele'], 'verbose_name': 'Řešení řešitelů', 'verbose_name_plural': 'Řešení řešitelů'}, + ), + ] diff --git a/odevzdavatko/models.py b/odevzdavatko/models.py index ad41d4ec..a5b6a7e1 100644 --- a/odevzdavatko/models.py +++ b/odevzdavatko/models.py @@ -24,7 +24,6 @@ class Reseni(bm.SeminarModelBase): verbose_name_plural = 'Řešení' #ordering = ['-problem', 'resitele'] # FIXME: Takhle to chceme, ale nefunguje to. ordering = ['-cas_doruceni'] - managed = False # Interní ID id = models.AutoField(primary_key = True) @@ -94,7 +93,6 @@ class Hodnoceni(bm.SeminarModelBase): db_table = 'seminar_hodnoceni' verbose_name = 'Hodnocení' verbose_name_plural = 'Hodnocení' - managed = False # Interní ID id = models.AutoField(primary_key = True) @@ -190,7 +188,6 @@ class PrilohaReseni(bm.SeminarModelBase): verbose_name = 'Příloha řešení' verbose_name_plural = 'Přílohy řešení' ordering = ['reseni', 'vytvoreno'] - managed = False # Interní ID id = models.AutoField(primary_key = True) @@ -226,7 +223,6 @@ class Reseni_Resitele(models.Model): verbose_name = 'Řešení řešitelů' verbose_name_plural = 'Řešení řešitelů' ordering = ['reseni', 'resitele'] - managed = False # Interní ID id = models.AutoField(primary_key = True) From c7fe1cb386dbe2ca780ca05d5b8fa1bdb4bbe1c3 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Wed, 23 Oct 2024 23:11:56 +0200 Subject: [PATCH 09/51] =?UTF-8?q?post=20=E2=80=93=20odevzd=C3=A1v=C3=A1tko?= =?UTF-8?q?=20odst=C5=99eleno!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0003_odstrel_odevzdavatka_post.py | 13 +++++++++++++ .../migrations/0013_odstrel_odevzdavatka_post.py | 14 ++++++++++++++ .../migrations/0135_odstrel_odevzdavatka_post.py | 14 ++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 odevzdavatko/migrations/0003_odstrel_odevzdavatka_post.py create mode 100644 personalni/migrations/0013_odstrel_odevzdavatka_post.py create mode 100644 seminar/migrations/0135_odstrel_odevzdavatka_post.py diff --git a/odevzdavatko/migrations/0003_odstrel_odevzdavatka_post.py b/odevzdavatko/migrations/0003_odstrel_odevzdavatka_post.py new file mode 100644 index 00000000..77936a26 --- /dev/null +++ b/odevzdavatko/migrations/0003_odstrel_odevzdavatka_post.py @@ -0,0 +1,13 @@ +# Generated by Django 4.2.13 on 2024-10-23 21:10 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('odevzdavatko', '0002_manage'), + ] + + operations = [ + ] diff --git a/personalni/migrations/0013_odstrel_odevzdavatka_post.py b/personalni/migrations/0013_odstrel_odevzdavatka_post.py new file mode 100644 index 00000000..127e1c40 --- /dev/null +++ b/personalni/migrations/0013_odstrel_odevzdavatka_post.py @@ -0,0 +1,14 @@ +# Generated by Django 4.2.13 on 2024-10-23 21:10 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('personalni', '0012_odstrel_odevzdavatka_pre'), + ('odevzdavatko', '0003_odstrel_odevzdavatka_post'), + ] + + operations = [ + ] diff --git a/seminar/migrations/0135_odstrel_odevzdavatka_post.py b/seminar/migrations/0135_odstrel_odevzdavatka_post.py new file mode 100644 index 00000000..d515b73e --- /dev/null +++ b/seminar/migrations/0135_odstrel_odevzdavatka_post.py @@ -0,0 +1,14 @@ +# Generated by Django 4.2.13 on 2024-10-23 21:10 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0134_delete_odevzdavatko'), + ('odevzdavatko', '0003_odstrel_odevzdavatka_post'), + ] + + operations = [ + ] From 788e8d22a29f89127e6ea0c34e3fb9489001f993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 25 Oct 2024 12:57:07 +0200 Subject: [PATCH 10/51] =?UTF-8?q?Pokus=20o=20opravu=20styl=C5=AF=20na=20st?= =?UTF-8?q?ar=C3=BDch=20iOSech?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mamweb/static/css/base.css | 2 + mamweb/static/css/mamweb_legacy.css | 728 ++++++++++++++++++++++++++++ mamweb/templates/base.html | 2 +- 3 files changed, 731 insertions(+), 1 deletion(-) create mode 100644 mamweb/static/css/mamweb_legacy.css diff --git a/mamweb/static/css/base.css b/mamweb/static/css/base.css index a4433d55..c562cb5b 100644 --- a/mamweb/static/css/base.css +++ b/mamweb/static/css/base.css @@ -1,5 +1,7 @@ @charset "utf-8"; /* vynuť utf-8 */ +@import "mamweb_legacy.css" supports((-webkit-touch-callout: none) and (not (offset-position: normal))); + /* Obecné styly pro html tagy */ @font-face { diff --git a/mamweb/static/css/mamweb_legacy.css b/mamweb/static/css/mamweb_legacy.css new file mode 100644 index 00000000..4da02fba --- /dev/null +++ b/mamweb/static/css/mamweb_legacy.css @@ -0,0 +1,728 @@ +@charset "utf-8"; /* vynuť utf-8 */ + +.button { + margin: 10px 0 10px 0; + padding: 4px 0; /*vertikální centování textu*/ + text-align: center; + background-color: var(--hlavni-oranzova); + color: var(--barva-pozadi); + font-size: 150%; + font-weight: bold; + font-variant: small-caps; + filter: drop-shadow(0px 5px 5px rgba(0, 0, 0, 0.4)); +} +.button:hover { + position: relative; + top: 2px; + left: 2px; + background-color: #df490e; +} +/******************/ + + + + + +/* Rozložení webu a jeho prvky (hlavička, menu, footer) */ + +/**** KONTEJNER ****/ +div.kontejner { /* Ne container, aby se to netlouklo s bootstrapem. */ + width: 970px; + margin: auto; + min-height: 100vh; + position: relative; + padding: 0; +} + +.org-logged-in div.kontejner { + margin-top: var(--login-bar-height); +} + +div.kontejner div.kontent-wrapper { + padding-bottom: var(--footer-height); +} + +div.kontejner div.kontent-wrapper div.kontent { + padding: 15px 30px; +} + +/* Roztáhne obsah z containeru na celou šířku obrazovky: */ +.full_width { + width: 100vw; + margin-left: calc(-50vw + 485px); +} + +/* Na úzkém displeji nechceme nic dělat. */ +@media(max-width: 860px) { + .full_width { + margin-left: 0; + width: unset; + } +} +/*******************/ + + +/**** HLAVIČKA ****/ +#header { + position: relative; + background: url("../images/header/vikendovka.jpg") no-repeat center top; /* poměr 350:970, TODO: aby to nemuselo být přesně na pixely */ + background-size: 100%; + top: 58px; +} + +#header img.logo { + width: 100%; + filter: drop-shadow(0px 5px 5px rgba(0, 0, 0, 0.4)); +} + +#header img.logo-mobile { + display: none; +} + +#header .no-mobile { + background-size: contain; +} +/******************/ + + +/**** Footer ****/ +#footer { + position: absolute; + bottom: 0; + width: 100%; + background: url("../images/mozaika-footer.svg") no-repeat top center; + height: var(--footer-height); + background-size: 100%; + filter: drop-shadow(5px 0px 5px rgba(0, 0, 0, 0.4)); + padding-top: 3.5%; +} + +#footer p.license { + text-align: center; + font-weight: 400; + bottom: 0; +} + +#footer p.license a { + color: #333; +} + +@media (max-width: 650px) { + #footer { + display: none; + } +} + +p.license-mobile { + display: none; + margin-bottom: 0; +} + +@media (max-width: 650px) { + p.license-mobile { + position: relative; + display: block; + font-size: 90%; + background-color: var(--hlavni-oranzova); + padding: 5%; + text-align: justify; + } +} +/****************/ + + +/**** LOGIN BAR ****/ +div.login-bar { + background: var(--orgovska-fialova); + color: var(--svetla-oranzova); + width: 100%; + + position: fixed; + margin-top: calc(-1 * var(--login-bar-height)); + min-height: var(--login-bar-height); + z-index: 4086; + + padding-left: 5px; + padding-right: 5px; +} + +div.login-bar div { + display: inline; +} + +div.login-bar a.LOGIN-ref-admin { + display: inline; + color: var(--barva-pozadi); +} + +div.login-bar .LOGIN_napis-webarum { + display: inline; + color: var(--barva-pozadi); + float: right; +} + +div.login-bar .LOGIN_napis-webarum a { + color: var(--svetla-oranzova); + text-decoration: underline; +} +/*******************/ + + +/* stránka přes celý displej */ +@media (max-width: 970px) { + #header { + background-size: 100%; + } + + #header img.logo { + width: 100%; + } +} + +/* malý tablet, mobil */ +@media (max-width: 650px) { + #header { + width: 100%; + top: 0; + background-image: none; + } + + #header img.logo { + display: none; + } + + #header img.logo-mobile { + display: block; + top: 0; + left: 0; + width: 100%; + filter: drop-shadow(0px 0 5px rgba(0, 0, 0, 0.4)); + margin-bottom: 3px; + } + + #header .no-mobile{ + display: none; + } +} + + + + + +/**** MENU ****/ + +ul.menu { + width: 100%; + padding: 0; + margin-top: -5px; /* posune celé menu nahoru (pak potřeba zvětšit mezeru mezi menu a submenu) */ + + font-variant: small-caps; +} + +ul.menu a { + text-decoration: none; + font-weight: bold; + font-size: 105%; +} + +ul.menu li { + margin: 0; + display: inline-block; + width: 16.666667%; + text-align: center; + + font-size: 140%; + font-weight: 400; +} + +ul.menu li >a:hover, >a:active { + color: black; +} + +ul.menu li.active>a { + color: var(--svetla-oranzova); +} + +ul.menu ul.submenu { + background-color: var(--hlavni-oranzova); + margin-top: 10px; /* mezera mezi hlavním menu a submenu */ + margin-bottom: 10px; + padding-top: 10px; + padding-bottom: 5px; + filter: drop-shadow(0px 5px 5px rgba(0, 0, 0, 0.4)); + + z-index: 50; + font-weight: 400; +} + +ul.menu ul.submenu li { + width: auto; + padding: 0 20px 0 20px; + display: inline-block; +} + +ul.menu ul.submenu li >a { + color: var(--svetla-oranzova); + text-decoration: none; + text-shadow: none; +} + +ul.menu ul.submenu li >a :hover { + color: black; +} + +ul.menu ul.submenu li.active>a, .parentactive ul li:first-child>a { + color: black; +} + +@media (max-width: 970px) { + ul.menu { + font-size: 90%; + margin-top: -7px; + } + + ul.menu li { + margin-top: 10px; /* posunutí textu hlavního menu níže */ + } + + ul.submenu { + margin-top: 8px; /* mezera mezi hlavním menu a submenu */ + } + + ul.submenu li { + margin-top: 0; /* aby se spolu s textem hlavního menu neposunoval níže i text submenu */ + } +} + +@media(max-width: 800px) { + ul.menu { + font-size: 80%; + margin-top: -2px; + } + + ul.menu li { + margin-top: 10px; /* posunutí textu hlavního menu níže */ + } + + ul.submenu { + margin-top: 8px; /* mezera mezi hlavním menu a submenu */ + } + ul.submenu li { + margin-top: 0; /* aby se spolu s textem hlavního menu neposunoval níže i text submenu */ + } +} + +@media (max-width: 650px) { + ul.menu { + display: none; + } + + ul.menu_mobile { + display: block; + z-index: 10; + position: sticky; + font-variant: small-caps; + font-size: 150%; + font-weight: bold; + list-style-type: none; + padding-top: 3px; + padding-bottom: 3px; + padding-left: 12px; + } + + ul.menu_mobile a:active, ul.menu_mobile a:hover, ul.menu_mobile a:focus { + text-decoration: none; + color: black; + } + + ul.menu_mobile ul { + list-style-type: none; + font-size: 90%; + color: black; /*černé šipky submenu*/ + } + + ul.menu_mobile ul li > a { + color: black; + } + + ul.menu_mobile ul li::before { + content: ' \276D '; /*https://www.w3schools.com/cssref/css_entities.asp*/ + } + + ul.menu_mobile br { + display: none; + } +} /* konec @media */ +/**************/ + +/**** ZBYTEK ****/ +/* (konkrétní stránky) */ + +/* Titulní stránka */ +.titulnistrana { + display: flex; + text-align: justify; + +@media(max-width: 800px){ + .titulnistrana { + display: block; + } +} + +.titulnistrana h1 { text-align: center; } + +.titulnistrana .TITULNI_STRANA_zjistit_vic { + text-align: center; + margin-bottom: 30px; +} + +.titulnistrana .TITULNI_STRANA_zjistit_vic hr { + display: none; +} + +@media(max-width: 800px){ + .titulnistrana .TITULNI_STRANA_zjistit_vic hr { + display: flex; + } +} + +@media(max-width: 800px) { + .titulnistrana .TITULNI_STRANA_graf { + padding-top: 40px; + } +} + +.titulnistrana .TITULNI_STRANA_graf .TITULNI_STRANA_graf-svg { + display: flex; +} + +.titulnistrana .TITULNI_STRANA_graf .TITULNI_STRANA_graf-svg #svg-graf { + width: 100%; + height: auto; + margin: 30px; +} + +@media(max-width: 800px){ + .titulnistrana .TITULNI_STRANA_graf .TITULNI_STRANA_graf-svg #svg-graf { + max-width: 500px; + padding: 10px; + margin: auto; + } +} + +.titulnistrana .TITULNI_STRANA_obsah { + width: 66%; +} + +@media(max-width: 800px){ + .titulnistrana .TITULNI_STRANA_obsah { + width: 100%; + } +} + +.titulnistrana .TITULNI_STRANA_vitej_titulka, .TITULNI_STRANA_temata_titulka { + width: 49%; + padding: 10px; + display: table-cell; +} + +@media (max-width: 650px) { + .titulnistrana .TITULNI_STRANA_vitej_titulka, .TITULNI_STRANA_temata_titulka { + width: 100%; + display: block; + } +} + +.titulnistrana .TITULNI_STRANA_novinky { + width: 33%; + padding: 10px; +} + +@media(max-width: 800px){ + .titulnistrana .TITULNI_STRANA_novinky { + width: 100%; + max-width: 500px; + margin: auto; + } +} + + +/* Stránky Aktuální ročník */ +.stranka_aktualni_zadani { + text-align: center; +} + +.stranka_aktualni_zadani #AKTUALNI_ZADADNI_obrazek { + margin-top: 15px; +} + +.stranka_aktualni_zadani div.AKTUALNI_ZADANI_termin { + text-align: center; + font-size: large; + font-weight: bold; +} + +@media (max-width: 420px) { + .stranka_aktualni_zadani div.AKTUALNI_ZADANI_termin { + font-size: small; + } +} + +.stranka_aktualni_zadani .AKTUALNI_ZADANI_datum { + color: var(--hlavni-oranzova); + margin: 0; + } +} + +/* Stránka Jak řešit */ +.jakresit svg { + width: 33%; + padding: 10px; + filter: none; +} + +@media(max-width: 860px) { + .jakresit svg { + margin: auto; + display: grid; + width: 100%; + max-width: 360px; + } +} + + + + + + + + + +/**** OZNAČENÍ NE-PUBLIC ČÁSTÍ ****/ +.mam-org-only { + background: var(--orgovska-svetla-fialova); + padding: 10px; + margin: 10px -10px; + border: var(--orgovska-fialova) 2px dashed; +} + +.mam-org-only .mam-org-only { + border: 0; +} + +.mam-org-only li { + padding: 3px 0; + margin: -2px 0; +} +/**********************************/ + + + +/**** OTÁČECÍ KARTY ****/ +/* (orgové, archiv) */ +.flip-card { + perspective: 1000px; /* Remove this if you don't want the 3D effect */ + margin-left: auto; + margin-right: auto; +} + + /* This container is needed to position the front and back side */ +.flip-card .flip-card-inner { + position: relative; + width: 100%; + height: 100%; + transition: transform 0.8s; + transform-style: preserve-3d; +} + + /* Do an horizontal flip when you move the mouse over the flip box container */ +.flip-card:hover .flip-card-inner { + transform: rotateY(180deg); +} + + /* Position the front and back side */ +.flip-card .flip-card-front, .flip-card-back { + position: absolute; + width: 100%; + height: 100%; + -webkit-backface-visibility: hidden; /* Safari */ + backface-visibility: hidden; +} + +.flip-card div.flip-card-foto, div.flip-card-foto img { + width: 100%; + height: 100%; + + /* Pokud je na přední straně něco proklikávacího (třeba celá fotka), tak na dotykových zařízeních nemůže proklikávat, aby se dalo otáčet */ +@media(hover: none) { + .flip-card div.flip-card-foto, div.flip-card-foto img a { pointer-events: none; } +} + + /* Style the back side */ + .flip-card-back { + transform: rotateY(180deg); + padding: 10px; + padding-top: 20px; + } +} +/***********************/ + + +/**** TABULKY ****/ + +/** Tabulka s čárami mezi sloupci **/ +/* Např. výsledkovky */ + +.tabulka_oramovane_sloupce { + border: solid 2px; +} + +.tabulka_oramovane_sloupce td:first-child, .tabulka_oramovane_sloupce th:first-child { + border-left: none; + border-right: solid 1px; +} + +.tabulka_oramovane_sloupce td:nth-child(2), .tabulka_oramovane_sloupce th:nth-child(2) { + border-left: none; +} + +.tabulka_oramovane_sloupce td, .tabulka_oramovane_sloupce th { + padding: 0.1em 0.3em; + border-left: solid 1px; +} + +.tabulka_oramovane_sloupce thead th, .tabulka_oramovane_sloupce thead td { + border-bottom: solid 1px; +} +/***********************************/ + + +/** Tabulka se střídajícími se barvami řádků **/ +/* Skoro jakákoliv tabulka kromě výsledkovek */ + +.barevna_tabulka td th { + padding: 1px 10px 1px 10px; +} + +.barevna_tabulka tbody tr:nth-child(even), thead tr { + background: var(--svetlounka-oranzova); +} + +.barevna_tabulka tbody tr:nth-child(odd) { + background: var(--barva-pozadi); +} +/**********************************************/ + + +/** Tabulka, kde první řádek a sloupec je pořád vidět **/ +/* Např. tabulka odevzdaných řešení, nebo výsledkovky */ +.tabulka_s_uchycenym_radkem_a_sloupcem { + /* Omezí výšku a šířku, aby bylo příjemné na scrollování a zapne scrollování */ + display: block; + width: fit-content; /* display: block; roztahuje na celou šířku */ + max-height: 80vh; + overflow: auto; + max-width: 90%; /* (FIXME asi není potřeba u tabulek, co nejsou na celou obrazovku) */ + margin-left: 5%; /* Vystředování (FIXME není potřeba u tabulek, co nejsou na celou obrazovku) */ + + border-collapse: separate; /* Pokud má tabulka orámování, je potřeba ho separovat, aby dodrželo position: sticky; */ + border-spacing: 0; +} + + /* Uchytí první řádek */ +.tabulka_s_uchycenym_radkem_a_sloupcem thead tr { + position: sticky; + top: 0; + z-index: 2; +} + + /* Uchytí první sloupec */ +.tabulka_s_uchycenym_radkem_a_sloupcem td:first-child, .tabulka_s_uchycenym_radkem_a_sloupcem th:first-child { + position: sticky; + left: 0; + background: inherit; /* (Snad) zneprůhlední první sloupec */ + z-index: 1; +} +/*******************************************************/ + +/** Tabulka mající všechna ohraničení **/ +.plne_ohranicena_tabulka { + border-collapse: collapse; +} + +.plne_ohranicena_tabulka tr th, .plne_ohranicena_tabulka tr td { + border: 1px solid black; + padding: 1px 10px 1px 10px; +} +/***************************************/ + +/** Výsledkovky **/ +.vysledkovka, .tabulka_oramovane_sloupce td:first-child, .vysledkovka, .tabulka_oramovane_sloupce th:first-child { position: unset; } +.vysledkovka, .tabulka_oramovane_sloupce td:nth-child(2), .vysledkovka, .tabulka_oramovane_sloupce th:nth-child(2) { + border-right: solid 1px; + + position: sticky; + left: 0; + background: inherit; /* (Snad) zneprůhlední druhý sloupec */ + z-index: 1; +} +.vysledkovka, .tabulka_oramovane_sloupce td:nth-child(3), .vysledkovka, .tabulka_oramovane_sloupce th:nth-child(3) { + border-left: none; +} +/*****************/ + + +/** Tabulka mých (řešitelových) řešení **/ +.moje_reseni tr th, .moje_reseni tr td { + text-align: center; +} + +.moje_reseni tr td.problem { text-align: left; } +/****************************************/ + + +/** Detail řešení **/ +.bodovani>input { + width: 4em; +} + +.bodovani>input::placeholder { + color: lightgray; + opacity: 1; +} + +.bodovani>input::-webkit-input-placeholder { /* Edge */ + color: lightgray; +} + +/*******************/ +/*****************/ + + +.novinka .novinka_obrazek { + margin: 10px 0 10px 0; + width: 100%; +} + +.novinka .novinka_datum { + font-weight: bold; +} + +.novinka .novinka_autor { + text-align: right; + font-style: italic; +} + +/**** FORMULÁŘE ****/ +div.gdpr { + font-size: 6pt; +} + +div.gdpr p { + font-size: 6pt; + margin-bottom: .66em; +} + +/*******************/ + + diff --git a/mamweb/templates/base.html b/mamweb/templates/base.html index 1a4c22ab..60447467 100644 --- a/mamweb/templates/base.html +++ b/mamweb/templates/base.html @@ -10,7 +10,7 @@ {% block custom_css %}{% endblock %} - + From e0dc75763cd7a902dc428becbf3179ac79663fcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 25 Oct 2024 13:31:50 +0200 Subject: [PATCH 11/51] =?UTF-8?q?Jin=C3=BD=20pokus=20o=20opravu=20styl?= =?UTF-8?q?=C5=AF=20na=20star=C3=BDch=20iOSech?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mamweb/static/css/base.css | 2 -- mamweb/static/css/mamweb_legacy.css | 3 +++ mamweb/templates/base.html | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/mamweb/static/css/base.css b/mamweb/static/css/base.css index c562cb5b..a4433d55 100644 --- a/mamweb/static/css/base.css +++ b/mamweb/static/css/base.css @@ -1,7 +1,5 @@ @charset "utf-8"; /* vynuť utf-8 */ -@import "mamweb_legacy.css" supports((-webkit-touch-callout: none) and (not (offset-position: normal))); - /* Obecné styly pro html tagy */ @font-face { diff --git a/mamweb/static/css/mamweb_legacy.css b/mamweb/static/css/mamweb_legacy.css index 4da02fba..fd360d45 100644 --- a/mamweb/static/css/mamweb_legacy.css +++ b/mamweb/static/css/mamweb_legacy.css @@ -1,5 +1,7 @@ @charset "utf-8"; /* vynuť utf-8 */ +@supports (-webkit-touch-callout: none) and (not (offset-position: normal)) { + .button { margin: 10px 0 10px 0; padding: 4px 0; /*vertikální centování textu*/ @@ -726,3 +728,4 @@ div.gdpr p { /*******************/ +} diff --git a/mamweb/templates/base.html b/mamweb/templates/base.html index 60447467..4ad7fd0a 100644 --- a/mamweb/templates/base.html +++ b/mamweb/templates/base.html @@ -10,7 +10,8 @@ {% block custom_css %}{% endblock %} - + + From 46a8f136fa5b7c73a73aaeda80b73d4dbf710a1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 25 Oct 2024 20:21:47 +0200 Subject: [PATCH 12/51] Fix formularOKView (TemplateDoesNotExist) --- various/views/pomocne.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/various/views/pomocne.py b/various/views/pomocne.py index 42547467..e25ed9df 100644 --- a/various/views/pomocne.py +++ b/various/views/pomocne.py @@ -13,7 +13,7 @@ from django.urls import reverse # Interní, nemá se nikdy objevit v urls (jinak to účastníci vytrolí) def formularOKView(request, text='', dalsi_odkazy: Sequence[tuple[str, str]] = ()): - template_name = 'seminar/formular_ok.html' + template_name = 'various/formular_ok.html' odkazy = list(dalsi_odkazy) + [ # (Text, odkaz) ('Vrátit se na titulní stránku', reverse('titulni_strana')), From ce83247fe0487e19784f5af03fbd7c9b052dbd8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Mon, 28 Oct 2024 12:29:08 +0100 Subject: [PATCH 13/51] =?UTF-8?q?Dopln=C4=9Bn=C3=AD=20nepovinn=C3=A9ho=20n?= =?UTF-8?q?esting=20selektoru=20&,=20abychom=20roz=C5=A1=C3=AD=C5=99ili=20?= =?UTF-8?q?support=20na=20star=C5=A1=C3=AD=20prohl=C3=AD=C5=BEe=C4=8De?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mamweb/static/css/layout.css | 88 +++++++++++++++++------------------ mamweb/static/css/modules.css | 43 +++++++++-------- mamweb/templates/base.html | 12 ++--- 3 files changed, 71 insertions(+), 72 deletions(-) diff --git a/mamweb/static/css/layout.css b/mamweb/static/css/layout.css index bab8ea39..bd139780 100644 --- a/mamweb/static/css/layout.css +++ b/mamweb/static/css/layout.css @@ -19,10 +19,10 @@ div.kontejner {/* Ne container, aby se to netlouklo s bootstrapem. */ margin-top: var(--login-bar-height); } - div.kontent-wrapper { + & div.kontent-wrapper { padding-bottom: var(--footer-height); - div.kontent { + & div.kontent { padding: 15px 30px; } } @@ -67,16 +67,16 @@ div.kontejner {/* Ne container, aby se to netlouklo s bootstrapem. */ background-size: 100%; top: 58px; - img.logo { + & img.logo { width: 100%; filter: drop-shadow(0px 5px 5px rgba(0, 0, 0, 0.4)); } - img.logo-mobile { + & img.logo-mobile { display: none; } - .no-mobile { + & .no-mobile { background-size: contain; } } @@ -94,12 +94,12 @@ div.kontejner {/* Ne container, aby se to netlouklo s bootstrapem. */ filter: drop-shadow(5px 0px 5px rgba(0, 0, 0, 0.4)); padding-top: 3.5%; - p.license { + & p.license { text-align: center; font-weight: 400; bottom: 0; - a { + & a { color: #333; } } @@ -139,21 +139,21 @@ div.login-bar { padding-left: 5px; padding-right: 5px; - div { + & div { display: inline; } - a.LOGIN-ref-admin { + & a.LOGIN-ref-admin { display: inline; color: var(--barva-pozadi); } - .LOGIN_napis-webarum { + & .LOGIN_napis-webarum { display: inline; color: var(--barva-pozadi); float: right; - a { + & a { color: var(--svetla-oranzova); text-decoration: underline; } @@ -176,7 +176,7 @@ div.login-bar { #header { background-size: 100%; - img.logo { + & img.logo { width: 100%; } } @@ -202,11 +202,11 @@ div.login-bar { top: 0; background-image: none; - img.logo { + & img.logo { display: none; } - img.logo-mobile { + & img.logo-mobile { display: block; top: 0; left: 0; @@ -215,7 +215,7 @@ div.login-bar { margin-bottom: 3px; } - .no-mobile{ + & .no-mobile{ display: none; } } @@ -241,13 +241,13 @@ ul.menu { font-variant: small-caps; - a { + & a { text-decoration: none; font-weight: bold; font-size: 105%; } - li { + & li { margin: 0; display: inline-block; width: 16.666667%; @@ -256,7 +256,7 @@ ul.menu { font-size: 140%; font-weight: 400; - >a:hover, >a:active { + &>a:hover, &>a:active { color: black; } @@ -265,7 +265,7 @@ ul.menu { } } - ul.submenu { + & ul.submenu { background-color: var(--hlavni-oranzova); margin-top: 10px; /* mezera mezi hlavním menu a submenu */ @@ -277,24 +277,24 @@ ul.menu { z-index: 50; font-weight: 400; - li { + & li { width: auto; padding: 0 20px 0 20px; display: inline-block; - >a { + &>a { color: var(--svetla-oranzova); text-decoration: none; text-shadow: none; - :hover { + &:hover { color: black; } } } } - ul.submenu li.active>a, .parentactive ul li:first-child>a { + & ul.submenu li.active>a, & .parentactive ul li:first-child>a { color: black; } } @@ -304,7 +304,7 @@ ul.menu { font-size: 90%; margin-top: -7px; - li { + & li { margin-top: 10px; /* posunutí textu hlavního menu níže */ } } @@ -312,7 +312,7 @@ ul.menu { ul.submenu { margin-top: 8px; /* mezera mezi hlavním menu a submenu */ - li { + & li { margin-top: 0; /* aby se spolu s textem hlavního menu neposunoval níže i text submenu */ } } @@ -323,7 +323,7 @@ ul.menu { font-size: 80%; margin-top: -2px; - li { + & li { margin-top: 10px; /* posunutí textu hlavního menu níže */ } } @@ -331,7 +331,7 @@ ul.menu { ul.submenu { margin-top: 8px; /* mezera mezi hlavním menu a submenu */ - li { + & li { margin-top: 0; /* aby se spolu s textem hlavního menu neposunoval níže i text submenu */ } } @@ -354,20 +354,20 @@ ul.menu { padding-bottom: 3px; padding-left: 12px; - a { + & a { &:active, &:hover, &:focus { text-decoration: none; color: black; } } - ul { + & ul { list-style-type: none; font-size: 90%; color: black; /*černé šipky submenu*/ - li { - > a { + & li { + &>a { color: black; } @@ -377,7 +377,7 @@ ul.menu { } } - br { + & br { display: none; } } @@ -459,13 +459,13 @@ body.suprodweb { &:before, &:after { background: red; } } display: block; } - h1 { text-align: center; } + & h1 { text-align: center; } - .TITULNI_STRANA_zjistit_vic{ + & .TITULNI_STRANA_zjistit_vic{ text-align: center; margin-bottom: 30px; - hr { + & hr { display: none; @media(max-width: 800px){ @@ -474,15 +474,15 @@ body.suprodweb { &:before, &:after { background: red; } } } } - .TITULNI_STRANA_graf { + & .TITULNI_STRANA_graf { @media(max-width: 800px) { padding-top: 40px; } - .TITULNI_STRANA_graf-svg { + & .TITULNI_STRANA_graf-svg { display: flex; - #svg-graf { + & #svg-graf { width: 100%; height: auto; margin: 30px; @@ -496,7 +496,7 @@ body.suprodweb { &:before, &:after { background: red; } } } } - .TITULNI_STRANA_obsah { + & .TITULNI_STRANA_obsah { width: 66%; @media(max-width: 800px){ @@ -504,7 +504,7 @@ body.suprodweb { &:before, &:after { background: red; } } } } - .TITULNI_STRANA_vitej_titulka, .TITULNI_STRANA_temata_titulka { + & .TITULNI_STRANA_vitej_titulka, & .TITULNI_STRANA_temata_titulka { width: 49%; padding: 10px; display: table-cell; @@ -515,7 +515,7 @@ body.suprodweb { &:before, &:after { background: red; } } } } - .TITULNI_STRANA_novinky { + & .TITULNI_STRANA_novinky { width: 33%; padding: 10px; @@ -540,11 +540,11 @@ div.odpocet { .stranka_aktualni_zadani { text-align: center; - #AKTUALNI_ZADADNI_obrazek { + & #AKTUALNI_ZADADNI_obrazek { margin-top: 15px; } - div.AKTUALNI_ZADANI_termin { + & div.AKTUALNI_ZADANI_termin { text-align: center; font-size: large; font-weight: bold; @@ -553,7 +553,7 @@ div.odpocet { font-size: small; } - .AKTUALNI_ZADANI_datum { + & .AKTUALNI_ZADANI_datum { color: var(--hlavni-oranzova); margin: 0; } diff --git a/mamweb/static/css/modules.css b/mamweb/static/css/modules.css index 2b823349..e7096a6e 100644 --- a/mamweb/static/css/modules.css +++ b/mamweb/static/css/modules.css @@ -20,7 +20,7 @@ border: 0; } - &li { + & li { padding: 3px 0; margin: -2px 0; } @@ -57,7 +57,7 @@ margin-right: auto; /* This container is needed to position the front and back side */ - .flip-card-inner { + & .flip-card-inner { position: relative; width: 100%; height: 100%; @@ -71,7 +71,7 @@ } /* Position the front and back side */ - .flip-card-front, .flip-card-back { + & .flip-card-front, & .flip-card-back { position: absolute; width: 100%; height: 100%; @@ -79,18 +79,18 @@ backface-visibility: hidden; } - div.flip-card-foto, div.flip-card-foto img { + & div.flip-card-foto, & div.flip-card-foto img { width: 100%; height: 100%; /* Pokud je na přední straně něco proklikávacího (třeba celá fotka), tak na dotykových zařízeních nemůže proklikávat, aby se dalo otáčet */ @media(hover: none) { - a { pointer-events: none; } + & a { pointer-events: none; } } } /* Style the back side */ - .flip-card-back { + & .flip-card-back { transform: rotateY(180deg); padding: 10px; padding-top: 20px; @@ -203,7 +203,7 @@ div.org_email { .tabulka_oramovane_sloupce { border: solid 2px; - td, th { + & td, & th { &:first-child, &:first-child { border-left: none; border-right: solid 1px; @@ -217,7 +217,7 @@ div.org_email { border-left: solid 1px; } - thead { th, td { + & thead { & th, & td { border-bottom: solid 1px; } } } @@ -228,15 +228,15 @@ div.org_email { /* Skoro jakákoliv tabulka kromě výsledkovek */ .barevna_tabulka { - td th { + & td th { padding: 1px 10px 1px 10px; } - tbody tr:nth-child(even), thead tr { + & tbody tr:nth-child(even), & thead tr { background: var(--svetlounka-oranzova); } - tbody tr:nth-child(odd) { + & tbody tr:nth-child(odd) { background: var(--barva-pozadi); } } @@ -258,14 +258,14 @@ div.org_email { border-spacing: 0; /* Uchytí první řádek */ - thead tr { + & thead tr { position: sticky; top: 0; z-index: 2; } /* Uchytí první sloupec */ - td, th { &:first-child { + & td, & th { &:first-child { position: sticky; left: 0; background: inherit; /* (Snad) zneprůhlední první sloupec */ @@ -290,7 +290,7 @@ div.org_email { .plne_ohranicena_tabulka { border-collapse: collapse; - tr { th, td { + & tr { & th, & td { border: 1px solid black; padding: 1px 10px 1px 10px; } } @@ -305,7 +305,7 @@ div.org_email { /** Výsledkovky **/ .vysledkovka, .tabulka_oramovane_sloupce { - td, th { + & td, & th { &:first-child { position: unset; } &:nth-child(2) { @@ -327,11 +327,11 @@ div.org_email { /** Tabulka mých (řešitelových) řešení **/ .moje_reseni tr { - th, td { + & th, & td { text-align: center; } - td.problem { text-align: left; } + & td.problem { text-align: left; } } /* Různá šířka problému */ @@ -378,16 +378,16 @@ div.org_email { .novinka { - .novinka_obrazek { + & .novinka_obrazek { margin: 10px 0 10px 0; width: 100%; } - .novinka_datum { + & .novinka_datum { font-weight: bold; } - .novinka_autor { + & .novinka_autor { text-align: right; font-style: italic; } @@ -413,7 +413,6 @@ table#reseni.form td, table#reseni.form tr { } @media(max-width: 800px) { - table#reseni.form td, table#reseni.form tr { display: inline-grid; max-width: 300px; @@ -473,7 +472,7 @@ ul.form li{ div.gdpr { font-size: 6pt; - p { + & p { font-size: 6pt; margin-bottom: .66em; } diff --git a/mamweb/templates/base.html b/mamweb/templates/base.html index 4ad7fd0a..8021b507 100644 --- a/mamweb/templates/base.html +++ b/mamweb/templates/base.html @@ -8,12 +8,12 @@ {% block custom_css %}{% endblock %} - - - - - - + + + + + + From 9ee82f72a7c4619bbe3010e86e34d3b38c771fda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 29 Oct 2024 19:12:25 +0100 Subject: [PATCH 14/51] =?UTF-8?q?SeminarModelBase.admin=5Furl=20nyn=C3=AD?= =?UTF-8?q?=20um=C3=AD=20i=20modely=20v=20jin=C3=BDch=20appk=C3=A1ch?= =?UTF-8?q?=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/models/base.py | 5 +++-- soustredeni/models.py | 4 ---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/seminar/models/base.py b/seminar/models/base.py index 1069f165..6ea34242 100644 --- a/seminar/models/base.py +++ b/seminar/models/base.py @@ -14,8 +14,9 @@ class SeminarModelBase(models.Model): # return "https://" + str(get_current_site(None)) + self.verejne_url() def admin_url(self): - model_name = self.__class__.__name__.lower() - return reverse('admin:seminar_{}_change'.format(model_name), args=(self.id, )) + app_name = self._meta.app_label + model_name = self._meta.model_name + return reverse('admin:{}_{}_change'.format(app_name, model_name), args=(self.id, )) # def verejne_url(self): # return None diff --git a/soustredeni/models.py b/soustredeni/models.py index 56db6974..fb5c5239 100644 --- a/soustredeni/models.py +++ b/soustredeni/models.py @@ -77,10 +77,6 @@ class Soustredeni(SeminarModelBase): #return reverse('seminar_soustredeni', kwargs={'pk': self.id}) return reverse('seminar_seznam_soustredeni') - def admin_url(self): - model_name = self.__class__.__name__.lower() - return reverse('admin:soustredeni_{}_change'.format(model_name), args=(self.id, )) - @reversion.register(ignore_duplicates=True) class Soustredeni_Ucastnici(SeminarModelBase): From a6085a64f434c2c015ae381217ecfb84d0773982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 29 Oct 2024 19:21:00 +0100 Subject: [PATCH 15/51] =?UTF-8?q?P=C5=99epojen=C3=AD=20import=C5=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- personalni/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/personalni/models.py b/personalni/models.py index 8f6cd63f..b8bcbdec 100644 --- a/personalni/models.py +++ b/personalni/models.py @@ -296,7 +296,7 @@ class Resitel(SeminarModelBase): def vsechny_body(self): "Spočítá body odjakživa." vsechna_reseni = self.reseni_set.all() - from seminar.models.odevzdavatko import Hodnoceni + from odevzdavatko.models import Hodnoceni vsechna_hodnoceni = Hodnoceni.objects.filter( reseni__in=vsechna_reseni) return sum(h.body for h in list(vsechna_hodnoceni) if h.body is not None) @@ -343,7 +343,7 @@ class Resitel(SeminarModelBase): # - body z 25. ročníku a dříve byly shledány dvakrát hodnotnějšími # - proto se započítávají dvojnásobně a byly posunuté hranice titulů # - staré tituly se ale nemají odebrat, pokud řešitel v t.č. minulém (26.) ročníku měl titul, má ho mít pořád. - from seminar.models.odevzdavatko import Hodnoceni + from odevzdavatko.models import Hodnoceni hodnoceni_do_25_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=25,reseni__in=self.reseni_set.all()) novejsi_hodnoceni = Hodnoceni.objects.filter(reseni__in=self.reseni_set.all()).difference(hodnoceni_do_25_rocniku) @@ -381,7 +381,7 @@ class Resitel(SeminarModelBase): else: return Titul.akad - from seminar.models.odevzdavatko import Hodnoceni + from odevzdavatko.models import Hodnoceni hodnoceni_do_26_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=26,reseni__in=self.reseni_set.all()) novejsi_body = body_z_hodnoceni( Hodnoceni.objects.filter(reseni__in=self.reseni_set.all()) From 92c05342fb9163be03a0488dcac66af6b0830285 Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Wed, 30 Oct 2024 14:36:06 +0100 Subject: [PATCH 16/51] =?UTF-8?q?odst=C5=99el=20tvorby:=20pre=20=E2=80=93?= =?UTF-8?q?=20relink?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- odevzdavatko/migrations/0004_tvorba_pre.py | 13 + odevzdavatko/migrations/0005_tvorba_relink.py | 35 + odevzdavatko/models.py | 2 +- personalni/migrations/0014_tvorba_pre.py | 13 + seminar/migrations/0136_tvorba_pre.py | 21 + seminar/migrations/0137_tvorba_unmanage.py | 59 ++ seminar/models/__init__.py | 1 + seminar/models/tvorba.py | 52 +- soustredeni/migrations/0004_tvorba_pre.py | 13 + soustredeni/migrations/0005_tvorba_relink.py | 25 + soustredeni/models.py | 2 +- split-apps-meta/polymorphic | 3 + tvorba/admin.py | 2 +- tvorba/migrations/0001_tvorba_create.py | 197 +++++ tvorba/models.py | 717 ++++++++++++++++++ various/migrations/0004_tvorba_pre.py | 13 + various/migrations/0005_tvorba_relink.py | 20 + various/models.py | 2 +- 18 files changed, 1172 insertions(+), 18 deletions(-) create mode 100644 odevzdavatko/migrations/0004_tvorba_pre.py create mode 100644 odevzdavatko/migrations/0005_tvorba_relink.py create mode 100644 personalni/migrations/0014_tvorba_pre.py create mode 100644 seminar/migrations/0136_tvorba_pre.py create mode 100644 seminar/migrations/0137_tvorba_unmanage.py create mode 100644 soustredeni/migrations/0004_tvorba_pre.py create mode 100644 soustredeni/migrations/0005_tvorba_relink.py create mode 100644 split-apps-meta/polymorphic create mode 100644 tvorba/migrations/0001_tvorba_create.py create mode 100644 tvorba/models.py create mode 100644 various/migrations/0004_tvorba_pre.py create mode 100644 various/migrations/0005_tvorba_relink.py diff --git a/odevzdavatko/migrations/0004_tvorba_pre.py b/odevzdavatko/migrations/0004_tvorba_pre.py new file mode 100644 index 00000000..a571c07e --- /dev/null +++ b/odevzdavatko/migrations/0004_tvorba_pre.py @@ -0,0 +1,13 @@ +# Generated by Django 4.2.16 on 2024-10-30 01:06 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('odevzdavatko', '0003_odstrel_odevzdavatka_post'), + ] + + operations = [ + ] diff --git a/odevzdavatko/migrations/0005_tvorba_relink.py b/odevzdavatko/migrations/0005_tvorba_relink.py new file mode 100644 index 00000000..2d235410 --- /dev/null +++ b/odevzdavatko/migrations/0005_tvorba_relink.py @@ -0,0 +1,35 @@ +# Generated by Django 4.2.16 on 2024-10-30 13:18 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('tvorba', '0001_tvorba_create'), + ('odevzdavatko', '0004_tvorba_pre'), + ] + + operations = [ + migrations.AlterField( + model_name='hodnoceni', + name='cislo_body', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='tvorba.cislo', verbose_name='číslo pro body'), + ), + migrations.AlterField( + model_name='hodnoceni', + name='deadline_body', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='tvorba.deadline', verbose_name='deadline pro body'), + ), + migrations.AlterField( + model_name='hodnoceni', + name='problem', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='tvorba.problem', verbose_name='problém'), + ), + migrations.AlterField( + model_name='reseni', + name='problem', + field=models.ManyToManyField(help_text='Problém', through='odevzdavatko.Hodnoceni', to='tvorba.problem', verbose_name='problém'), + ), + ] diff --git a/odevzdavatko/models.py b/odevzdavatko/models.py index a5b6a7e1..a52f370f 100644 --- a/odevzdavatko/models.py +++ b/odevzdavatko/models.py @@ -9,7 +9,7 @@ from django.urls import reverse_lazy from django.utils import timezone from django.conf import settings -import seminar.models as am # tvorba +import tvorba.models as am from seminar.models import base as bm from odevzdavatko.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet diff --git a/personalni/migrations/0014_tvorba_pre.py b/personalni/migrations/0014_tvorba_pre.py new file mode 100644 index 00000000..2d976333 --- /dev/null +++ b/personalni/migrations/0014_tvorba_pre.py @@ -0,0 +1,13 @@ +# Generated by Django 4.2.16 on 2024-10-30 01:07 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('personalni', '0013_odstrel_odevzdavatka_post'), + ] + + operations = [ + ] diff --git a/seminar/migrations/0136_tvorba_pre.py b/seminar/migrations/0136_tvorba_pre.py new file mode 100644 index 00000000..07f7d234 --- /dev/null +++ b/seminar/migrations/0136_tvorba_pre.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.16 on 2024-10-30 01:06 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0135_odstrel_odevzdavatka_post'), + ('odevzdavatko', '0004_tvorba_pre'), + ('various', '0004_tvorba_pre'), + ('soustredeni', '0004_tvorba_pre'), + ('personalni', '0014_tvorba_pre'), + # Polymorphic: + ('contenttypes', '0002_remove_content_type_name'), + # Taggit + ('taggit', '0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx'), + ] + + operations = [ + ] diff --git a/seminar/migrations/0137_tvorba_unmanage.py b/seminar/migrations/0137_tvorba_unmanage.py new file mode 100644 index 00000000..132f979b --- /dev/null +++ b/seminar/migrations/0137_tvorba_unmanage.py @@ -0,0 +1,59 @@ +# Generated by Django 4.2.16 on 2024-10-30 11:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0136_tvorba_pre'), + ] + + operations = [ + migrations.CreateModel( + name='Problemy_Opravovatele', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ], + options={ + 'db_table': 'seminar_problemy_opravovatele', + 'managed': False, + }, + ), + migrations.AlterModelOptions( + name='cislo', + options={'managed': False, 'ordering': ['-rocnik__rocnik', '-poradi'], 'verbose_name': 'Číslo', 'verbose_name_plural': 'Čísla'}, + ), + migrations.AlterModelOptions( + name='clanek', + options={'managed': False, 'verbose_name': 'Článek', 'verbose_name_plural': 'Články'}, + ), + migrations.AlterModelOptions( + name='deadline', + options={'managed': False, 'ordering': ['deadline'], 'verbose_name': 'Deadline', 'verbose_name_plural': 'Deadliny'}, + ), + migrations.AlterModelOptions( + name='pohadka', + options={'managed': False, 'ordering': ['vytvoreno'], 'verbose_name': 'Pohádka', 'verbose_name_plural': 'Pohádky'}, + ), + migrations.AlterModelOptions( + name='problem', + options={'managed': False, 'ordering': ['nazev'], 'verbose_name': 'Problém', 'verbose_name_plural': 'Problémy'}, + ), + migrations.AlterModelOptions( + name='rocnik', + options={'managed': False, 'ordering': ['-rocnik'], 'verbose_name': 'Ročník', 'verbose_name_plural': 'Ročníky'}, + ), + migrations.AlterModelOptions( + name='tema', + options={'managed': False, 'verbose_name': 'Téma', 'verbose_name_plural': 'Témata'}, + ), + migrations.AlterModelOptions( + name='uloha', + options={'managed': False, 'verbose_name': 'Úloha', 'verbose_name_plural': 'Úlohy'}, + ), + migrations.AlterModelOptions( + name='zmrazenavysledkovka', + options={'managed': False, 'verbose_name': 'Zmražená výsledkovka', 'verbose_name_plural': 'Zmražené výsledkovky'}, + ), + ] diff --git a/seminar/models/__init__.py b/seminar/models/__init__.py index 4bc85266..2eabe50f 100644 --- a/seminar/models/__init__.py +++ b/seminar/models/__init__.py @@ -9,6 +9,7 @@ from personalni.models import Organizator, Resitel, Skola, Prijemce, Osoba from soustredeni.models import Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Konfera, Konfery_Ucastnici 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 # Kvůli migr. 0041 from soustredeni.models import generate_filename_konfera diff --git a/seminar/models/tvorba.py b/seminar/models/tvorba.py index c11a3861..a8aea6ae 100644 --- a/seminar/models/tvorba.py +++ b/seminar/models/tvorba.py @@ -54,6 +54,7 @@ class Rocnik(SeminarModelBase): verbose_name = 'Ročník' verbose_name_plural = 'Ročníky' ordering = ['-rocnik'] + managed = False # Interní ID id = models.AutoField(primary_key = True) @@ -144,11 +145,12 @@ class Cislo(SeminarModelBase): verbose_name = 'Číslo' verbose_name_plural = 'Čísla' ordering = ['-rocnik__rocnik', '-poradi'] + managed = False # Interní ID id = models.AutoField(primary_key = True) - rocnik = models.ForeignKey(Rocnik, verbose_name='ročník', related_name='cisla', + rocnik = models.ForeignKey(Rocnik, verbose_name='ročník', related_name='cisla_old', db_index=True,on_delete=models.PROTECT) poradi = models.CharField('název čísla', max_length=32, db_index=True, @@ -338,6 +340,7 @@ class Deadline(SeminarModelBase): verbose_name = 'Deadline' verbose_name_plural = 'Deadliny' ordering = ['deadline'] + managed = False def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -349,7 +352,7 @@ class Deadline(SeminarModelBase): deadline = models.DateTimeField(blank=False, default=timezone.make_aware(datetime.datetime.combine(timezone.now(), datetime.time.max))) cislo = models.ForeignKey(Cislo, verbose_name='deadline v čísle', - related_name='deadline_v_cisle', blank=False, + related_name='deadline_v_cisle_old', blank=False, on_delete=models.CASCADE) TYP_CISLA = 'cisla' @@ -400,16 +403,31 @@ class ZmrazenaVysledkovka(SeminarModelBase): db_table = 'seminar_vysledkovky' verbose_name = 'Zmražená výsledkovka' verbose_name_plural = 'Zmražené výsledkovky' + managed = False deadline = models.OneToOneField( Deadline, on_delete=models.CASCADE, primary_key=True, - related_name="vysledkovka_v_deadlinu" + related_name="vysledkovka_v_deadlinu_old" ) html = models.TextField(null=False, blank=False) +class Problemy_Opravovatele(SeminarModelBase): + """Jen vazebná tabulka pro opravovatele. + + Ona stejně existovala, při přesunu mezi aplikacemi jen potřebujeme zajistit nepřejmenování DB tabulky. + Proto taky nepotřebuje žádná specifika, ze :py:class:SeminarModelBase: dědí ze zvyku než že by to k něčemu kdy měo být. + """ + class Meta: + db_table = 'seminar_problemy_opravovatele' + managed = False + + id = models.AutoField(primary_key = True) + + problem = models.ForeignKey('Problem', on_delete=models.CASCADE, related_name='awawa1_old') + organizator = models.ForeignKey(Organizator, on_delete=models.CASCADE, related_name='awawa2_old') @reversion.register(ignore_duplicates=True) # Pozor na následující řádek. *Nekrmit, asi kouše!* @@ -426,6 +444,7 @@ class Problem(SeminarModelBase,PolymorphicModel): verbose_name = 'Problém' verbose_name_plural = 'Problémy' ordering = ['nazev'] + managed = False # Interní ID id = models.AutoField(primary_key = True) @@ -435,7 +454,7 @@ class Problem(SeminarModelBase,PolymorphicModel): # Problém má podproblémy nadproblem = models.ForeignKey('self', verbose_name='nadřazený problém', - related_name='podproblem', null=True, blank=True, + related_name='podproblem_old', null=True, blank=True, on_delete=models.SET_NULL) STAV_NAVRH = 'navrh' @@ -451,22 +470,22 @@ class Problem(SeminarModelBase,PolymorphicModel): stav = models.CharField('stav problému', max_length=32, choices=STAV_CHOICES, blank=False, default=STAV_NAVRH) # Téma je taky Problém, takže má stavy, "zadané" témátko je aktuálně otevřené a dá se k němu něco poslat (řešení nebo článek) - zamereni = TaggableManager(verbose_name='zaměření', + zamereni = TaggableManager(verbose_name='zaměření', related_name='zamereni_old', help_text='Zaměření M/F/I/O problému, příp. další tagy', blank=True) poznamka = models.TextField('org poznámky (HTML)', blank=True, help_text='Neveřejný návrh úlohy, návrh řešení, text zadání, poznámky ...') autor = models.ForeignKey(Organizator, verbose_name='autor problému', - related_name='autor_problemu_%(class)s', null=True, blank=True, + related_name='autor_problemu_%(class)s_old', null=True, blank=True, on_delete=models.SET_NULL) garant = models.ForeignKey(Organizator, verbose_name='garant zadaného problému', - related_name='garant_problemu_%(class)s', null=True, blank=True, + related_name='garant_problemu_%(class)s_old', null=True, blank=True, on_delete=models.SET_NULL) opravovatele = models.ManyToManyField(Organizator, verbose_name='opravovatelé', - blank=True, related_name='opravovatele_%(class)s') + blank=True, related_name='opravovatele_%(class)s_old', through=Problemy_Opravovatele) kod = models.CharField('lokální kód', max_length=32, blank=True, default='', help_text='Číslo/kód úlohy v čísle nebo kód tématu/článku/seriálu v ročníku') @@ -545,6 +564,7 @@ class Tema(Problem): db_table = 'seminar_temata' verbose_name = 'Téma' verbose_name_plural = 'Témata' + managed = False TEMA_TEMA = 'tema' TEMA_SERIAL = 'serial' @@ -555,7 +575,7 @@ class Tema(Problem): tema_typ = models.CharField('Typ tématu', max_length=16, choices=TEMA_CHOICES, blank=False, default=TEMA_TEMA) - rocnik = models.ForeignKey(Rocnik, verbose_name='ročník',related_name='temata',blank=True, null=True, + rocnik = models.ForeignKey(Rocnik, verbose_name='ročník',related_name='temata_old',blank=True, null=True, on_delete=models.PROTECT) abstrakt = models.TextField('Abstrakt na rozcestník', blank=True) @@ -592,9 +612,10 @@ class Clanek(Problem): db_table = 'seminar_clanky' verbose_name = 'Článek' verbose_name_plural = 'Články' + managed = False cislo = models.ForeignKey(Cislo, blank=True, null=True, on_delete=models.PROTECT, - verbose_name='číslo vydání', related_name='vydane_clanky') + verbose_name='číslo vydání', related_name='vydane_clanky_old') strana = models.PositiveIntegerField(verbose_name="první strana", blank=True, null=True) @@ -617,15 +638,16 @@ class Uloha(Problem): db_table = 'seminar_ulohy' verbose_name = 'Úloha' verbose_name_plural = 'Úlohy' + managed = False cislo_zadani = models.ForeignKey(Cislo, verbose_name='číslo zadání', blank=True, - null=True, related_name='zadane_ulohy', on_delete=models.PROTECT) + null=True, related_name='zadane_ulohy_old', on_delete=models.PROTECT) cislo_deadline = models.ForeignKey(Cislo, verbose_name='číslo deadlinu', blank=True, - null=True, related_name='deadlinove_ulohy', on_delete=models.PROTECT) + null=True, related_name='deadlinove_ulohy_old', on_delete=models.PROTECT) cislo_reseni = models.ForeignKey(Cislo, verbose_name='číslo řešení', blank=True, - null=True, related_name='resene_ulohy', + null=True, related_name='resene_ulohy_old', help_text='Číslo s řešením úlohy, jen pro úlohy', on_delete=models.PROTECT) @@ -683,6 +705,7 @@ class Pohadka(SeminarModelBase): verbose_name = 'Pohádka' verbose_name_plural = 'Pohádky' ordering = ['vytvoreno'] + managed = False # Interní ID id = models.AutoField(primary_key=True) @@ -694,7 +717,8 @@ class Pohadka(SeminarModelBase): # Při nahrávání z TeXu není vyplnění vyžadováno, v adminu je null=True, blank=False, - on_delete=models.SET_NULL + on_delete=models.SET_NULL, + related_name='awawa3_old', ) vytvoreno = models.DateTimeField( diff --git a/soustredeni/migrations/0004_tvorba_pre.py b/soustredeni/migrations/0004_tvorba_pre.py new file mode 100644 index 00000000..76461971 --- /dev/null +++ b/soustredeni/migrations/0004_tvorba_pre.py @@ -0,0 +1,13 @@ +# Generated by Django 4.2.16 on 2024-10-30 01:07 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('soustredeni', '0003_post_split_soustredeni'), + ] + + operations = [ + ] diff --git a/soustredeni/migrations/0005_tvorba_relink.py b/soustredeni/migrations/0005_tvorba_relink.py new file mode 100644 index 00000000..7786449f --- /dev/null +++ b/soustredeni/migrations/0005_tvorba_relink.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.16 on 2024-10-30 13:18 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('tvorba', '0001_tvorba_create'), + ('soustredeni', '0004_tvorba_pre'), + ] + + operations = [ + migrations.AlterField( + model_name='konfera', + name='problem_ptr', + field=models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='tvorba.problem'), + ), + migrations.AlterField( + model_name='soustredeni', + name='rocnik', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='soustredeni', to='tvorba.rocnik', verbose_name='ročník'), + ), + ] diff --git a/soustredeni/models.py b/soustredeni/models.py index fb5c5239..23553c34 100644 --- a/soustredeni/models.py +++ b/soustredeni/models.py @@ -10,7 +10,7 @@ from django.conf import settings from personalni.models import Resitel, Organizator from seminar.models.base import SeminarModelBase -import seminar.models as am # tvorba +import tvorba.models as am logger = logging.getLogger(__name__) diff --git a/split-apps-meta/polymorphic b/split-apps-meta/polymorphic new file mode 100644 index 00000000..9c6ba514 --- /dev/null +++ b/split-apps-meta/polymorphic @@ -0,0 +1,3 @@ +django-polymorphic by *nemělo* být potřeba řešit, protože se odkazuje na id contenttype a tedy když přepisujeme ctype na správném místě rovnou, tak to bude fungovat. IN THEORY. + +Better safe than sorry: přidáme si v seminar.pre vazbu na model contenttypes. (technicky asi měl být všude?) diff --git a/tvorba/admin.py b/tvorba/admin.py index 01880d5b..f090062b 100644 --- a/tvorba/admin.py +++ b/tvorba/admin.py @@ -9,7 +9,7 @@ from django.utils.safestring import mark_safe import soustredeni.models -from seminar.models import Rocnik, ZmrazenaVysledkovka, Deadline, Uloha, Problem, Tema, Clanek, Cislo # tvorba +from tvorba.models import Rocnik, ZmrazenaVysledkovka, Deadline, Uloha, Problem, Tema, Clanek, Cislo # tvorba admin.site.register(Rocnik) admin.site.register(ZmrazenaVysledkovka) diff --git a/tvorba/migrations/0001_tvorba_create.py b/tvorba/migrations/0001_tvorba_create.py new file mode 100644 index 00000000..085cb5b4 --- /dev/null +++ b/tvorba/migrations/0001_tvorba_create.py @@ -0,0 +1,197 @@ +# Generated by Django 4.2.16 on 2024-10-30 11:37 + +import datetime +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import seminar.models.tvorba +import tvorba.models +import taggit.managers + +def nastav_nove_contenttypes(apps, schema_editor): + ContentType = apps.get_model('contenttypes', 'ContentType') + for m in ('zmrazenavysledkovka', 'deadline', 'cislo', 'rocnik', 'pohadka', 'tema', 'problem', 'problemy_opravovatele', 'uloha', 'clanek'): + ContentType.objects.filter(app_label='seminar', model=m).update(app_label='tvorba') + +def nastav_stare_contenttypes(apps, schema_editor): + ContentType = apps.get_model('contenttypes', 'ContentType') + for m in ('zmrazenavysledkovka', 'deadline', 'cislo', 'rocnik', 'pohadka', 'tema', 'problem', 'problemy_opravovatele', 'uloha', 'clanek'): + ContentType.objects.filter(app_label='tvorba', model=m).update(app_label='seminar') + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('seminar', '0137_tvorba_unmanage'), + ] + + operations = [ + migrations.CreateModel( + name='Cislo', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('poradi', models.CharField(db_index=True, help_text='Většinou jen "1", vyjímečně "7-8", lexikograficky určuje pořadí v ročníku!', max_length=32, verbose_name='název čísla')), + ('datum_vydani', models.DateField(blank=True, help_text='Datum vydání finální verze', null=True, verbose_name='datum vydání')), + ('verejne_db', models.BooleanField(db_column='verejne', default=False, verbose_name='číslo zveřejněno')), + ('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k číslu (plain text)', verbose_name='neveřejná poznámka')), + ('pdf', models.FileField(blank=True, help_text='PDF čísla, které si mohou řešitelé stáhnout', null=True, storage=seminar.models.tvorba.OverwriteStorage(), upload_to=tvorba.models.cislo_pdf_filename, verbose_name='pdf')), + ('titulka_nahled', models.ImageField(blank=True, help_text='Obrázek titulní strany, generuje se automaticky', null=True, upload_to=tvorba.models.cislo_png_filename, verbose_name='Obrázek titulní strany')), + ('rocnik', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='cisla', to='tvorba.rocnik', verbose_name='ročník')), + ], + options={ + 'verbose_name': 'Číslo', + 'verbose_name_plural': 'Čísla', + 'db_table': 'seminar_cisla', + 'ordering': ['-rocnik__rocnik', '-poradi'], + 'managed': False, + }, + ), + migrations.CreateModel( + name='Deadline', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('deadline', models.DateTimeField(default=datetime.datetime(2024, 10, 30, 22, 59, 59, 999999, tzinfo=datetime.timezone.utc))), + ('cislo', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='deadline_v_cisle', to='tvorba.cislo', verbose_name='deadline v čísle')), + ('typ', models.CharField(choices=[('cisla', 'Deadline celého čísla'), ('prvni', 'První deadline'), ('prvniasous', 'Sousový a první deadline'), ('sous', 'Sousový deadline')], max_length=32, verbose_name='typ deadlinu')), + ('verejna_vysledkovka', models.BooleanField(db_column='verejna_vysledkovka', default=False, verbose_name='veřejná výsledkovka')), + ], + options={ + 'verbose_name': 'Deadline', + 'verbose_name_plural': 'Deadliny', + 'db_table': 'seminar_deadliny', + 'ordering': ['deadline'], + 'managed': False, + }, + ), + migrations.CreateModel( + name='Pohadka', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('vytvoreno', models.DateTimeField(blank=True, default=django.utils.timezone.now, editable=False, verbose_name='Vytvořeno')), + ('autor', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='personalni.organizator', verbose_name='Autor pohádky')), + ], + options={ + 'verbose_name': 'Pohádka', + 'verbose_name_plural': 'Pohádky', + 'db_table': 'seminar_pohadky', + 'ordering': ['vytvoreno'], + 'managed': False, + }, + ), + migrations.CreateModel( + name='Problem', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('nazev', models.CharField(max_length=256, verbose_name='název')), + ('stav', models.CharField(choices=[('navrh', 'Návrh'), ('zadany', 'Zadaný'), ('vyreseny', 'Vyřešený'), ('smazany', 'Smazaný')], default='navrh', max_length=32, verbose_name='stav problému')), + ('autor', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='autor_problemu_%(class)s', to='personalni.organizator', verbose_name='autor problému')), + ('garant', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='garant_problemu_%(class)s', to='personalni.organizator', verbose_name='garant zadaného problému')), + ('nadproblem', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='podproblem', to='tvorba.problem', verbose_name='nadřazený problém')), + ('opravovatele', models.ManyToManyField(blank=True, related_name='opravovatele_%(class)s', through='tvorba.Problemy_Opravovatele', to='personalni.organizator', verbose_name='opravovatelé')), + ('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')), + ('zamereni', taggit.managers.TaggableManager(blank=True, help_text='Zaměření M/F/I/O problému, příp. další tagy', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='zaměření')), + ('poznamka', models.TextField(blank=True, help_text='Neveřejný návrh úlohy, návrh řešení, text zadání, poznámky ...', verbose_name='org poznámky (HTML)')), + ('kod', models.CharField(blank=True, default='', help_text='Číslo/kód úlohy v čísle nebo kód tématu/článku/seriálu v ročníku', max_length=32, verbose_name='lokální kód')), + ('vytvoreno', models.DateTimeField(blank=True, default=django.utils.timezone.now, editable=False, verbose_name='vytvořeno')), + ], + options={ + 'verbose_name': 'Problém', + 'verbose_name_plural': 'Problémy', + 'db_table': 'seminar_problemy', + 'ordering': ['nazev'], + 'managed': False, + }, + ), + migrations.CreateModel( + name='Problemy_Opravovatele', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('problem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tvorba.problem')), + ('organizator', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='personalni.organizator')), + ], + options={ + 'db_table': 'seminar_problemy_opravovatele', + 'managed': False, + }, + ), + migrations.CreateModel( + name='Rocnik', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('prvni_rok', models.IntegerField(db_index=True, unique=True, verbose_name='první rok')), + ('rocnik', models.IntegerField(db_index=True, unique=True, verbose_name='číslo ročníku')), + ('exportovat', models.BooleanField(db_column='exportovat', default=False, help_text='Exportuje se jen podle tohoto flagu (ne veřejnosti), a to jen čísla s veřejnou výsledkovkou', verbose_name='export do AESOPa')), + ], + options={ + 'verbose_name': 'Ročník', + 'verbose_name_plural': 'Ročníky', + 'db_table': 'seminar_rocniky', + 'ordering': ['-rocnik'], + 'managed': False, + }, + ), + migrations.CreateModel( + name='Clanek', + fields=[ + ('problem_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='tvorba.problem')), + ('strana', models.PositiveIntegerField(blank=True, null=True, verbose_name='první strana')), + ('cislo', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vydane_clanky', to='tvorba.cislo', verbose_name='číslo vydání')), + ], + options={ + 'verbose_name': 'Článek', + 'verbose_name_plural': 'Články', + 'db_table': 'seminar_clanky', + 'managed': False, + }, + bases=('tvorba.problem',), + ), + migrations.CreateModel( + name='Tema', + fields=[ + ('problem_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='tvorba.problem')), + ('tema_typ', models.CharField(choices=[('tema', 'Téma'), ('serial', 'Seriál')], default='tema', max_length=16, verbose_name='Typ tématu')), + ('rocnik', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='temata', to='tvorba.rocnik', verbose_name='ročník')), + ('abstrakt', models.TextField(blank=True, verbose_name='Abstrakt na rozcestník')), + ('obrazek', models.ImageField(blank=True, null=True, upload_to='', verbose_name='Obrázek na rozcestník')), + ], + options={ + 'verbose_name': 'Téma', + 'verbose_name_plural': 'Témata', + 'db_table': 'seminar_temata', + 'managed': False, + }, + bases=('tvorba.problem',), + ), + migrations.CreateModel( + name='Uloha', + fields=[ + ('problem_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='tvorba.problem')), + ('max_body', models.DecimalField(blank=True, decimal_places=1, max_digits=8, null=True, verbose_name='maximum bodů')), + ('cislo_zadani', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='zadane_ulohy', to='tvorba.cislo', verbose_name='číslo zadání')), + ('cislo_reseni', models.ForeignKey(blank=True, help_text='Číslo s řešením úlohy, jen pro úlohy', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='resene_ulohy', to='tvorba.cislo', verbose_name='číslo řešení')), + ('cislo_deadline', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='deadlinove_ulohy', to='tvorba.cislo', verbose_name='číslo deadlinu')), + ], + options={ + 'verbose_name': 'Úloha', + 'verbose_name_plural': 'Úlohy', + 'db_table': 'seminar_ulohy', + 'managed': False, + }, + bases=('tvorba.problem',), + ), + migrations.CreateModel( + name='ZmrazenaVysledkovka', + fields=[ + ('deadline', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='vysledkovka_v_deadlinu', serialize=False, to='tvorba.deadline')), + ('html', models.TextField()), + ], + options={ + 'verbose_name': 'Zmražená výsledkovka', + 'verbose_name_plural': 'Zmražené výsledkovky', + 'db_table': 'seminar_vysledkovky', + 'managed': False, + }, + ), + migrations.RunPython(nastav_nove_contenttypes, nastav_stare_contenttypes), + ] diff --git a/tvorba/models.py b/tvorba/models.py new file mode 100644 index 00000000..40d46797 --- /dev/null +++ b/tvorba/models.py @@ -0,0 +1,717 @@ +import datetime +import os +import subprocess +import pathlib +import tempfile +import logging + +from django.contrib.sites.shortcuts import get_current_site +from django.db import models +from django.db.models import Q +from django.template.loader import render_to_string +from django.utils import timezone +from django.conf import settings +from django.urls import reverse +from django.core.cache import cache +from django.core.exceptions import ObjectDoesNotExist, ValidationError +from django.utils.text import get_valid_filename +from django.utils.functional import cached_property + +from solo.models import SingletonModel +from taggit.managers import TaggableManager + +from reversion import revisions as reversion + +from tvorba.utils import roman, aktivniResitele +from treenode import treelib + +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 django.core.mail import EmailMessage + +from seminar.models import SeminarModelBase, OverwriteStorage +from personalni.models import Prijemce, Organizator + +logger = logging.getLogger(__name__) + +@reversion.register(ignore_duplicates=True) +class Rocnik(SeminarModelBase): + + class Meta: + db_table = 'seminar_rocniky' + verbose_name = 'Ročník' + verbose_name_plural = 'Ročníky' + ordering = ['-rocnik'] + managed = False + + # Interní ID + id = models.AutoField(primary_key = True) + + prvni_rok = models.IntegerField('první rok', db_index=True, unique=True) + + rocnik = models.IntegerField('číslo ročníku', db_index=True, unique=True) + + exportovat = models.BooleanField('export do AESOPa', db_column='exportovat', default=False, + help_text='Exportuje se jen podle tohoto flagu (ne veřejnosti),' + ' a to jen čísla s veřejnou výsledkovkou') + + def __str__(self): + return '{} ({}/{})'.format(self.rocnik, self.prvni_rok, self.prvni_rok+1) + + # Ročník v římských číslech + def roman(self): + return roman(int(self.rocnik)) + + def verejne(self): + return len(self.verejna_cisla()) > 0 + verejne.boolean = True + verejne.short_description = 'Veřejný (jen dle čísel)' + + def neverejna_cisla(self): + vc = [c for c in self.cisla.all() if not c.verejne()] + vc.sort(key=lambda c: c.poradi) + return vc + + def verejna_cisla(self): + vc = [c for c in self.cisla.all() if c.verejne()] + vc.sort(key=lambda c: c.poradi) + return vc + + def posledni_verejne_cislo(self): + vc = self.verejna_cisla() + return vc[-1] if vc else None + + def verejne_vysledkovky_cisla(self): + vc = list(self.cisla.filter(deadline_v_cisle__verejna_vysledkovka=True).distinct()) + vc.sort(key=lambda c: c.poradi) + return vc + + def posledni_zverejnena_vysledkovka_cislo(self): + vc = self.verejne_vysledkovky_cisla() + return vc[-1] if vc else None + + def druhy_rok(self): + return self.prvni_rok + 1 + + def verejne_url(self): + return reverse('seminar_rocnik', kwargs={'rocnik': self.rocnik}) + + @classmethod + def cached_rocnik(cls, r_id): + name = 'rocnik_%s' % (r_id, ) + c = cache.get(name) + if c is None: + c = cls.objects.get(id=r_id) + cache.set(name, c, 300) + return c + + def save(self, *args, **kwargs): + super().save(*args, **kwargs) + # *Node.save() aktualizuje název *Nodu. + try: + self.rocniknode.save() + except ObjectDoesNotExist: + # Neexistující *Node nemá smysl aktualizovat. + pass + +def cislo_pdf_filename(self, filename): + rocnik = str(self.rocnik.rocnik) + return pathlib.Path('cislo', 'pdf', rocnik, '{}-{}.pdf'.format(rocnik, self.poradi)) + +def cislo_png_filename(self, filename): + rocnik = str(self.rocnik.rocnik) + return pathlib.Path('cislo', 'png', rocnik, '{}-{}.png'.format(rocnik, self.poradi)) + +@reversion.register(ignore_duplicates=True) +class Cislo(SeminarModelBase): + + class Meta: + db_table = 'seminar_cisla' + verbose_name = 'Číslo' + verbose_name_plural = 'Čísla' + ordering = ['-rocnik__rocnik', '-poradi'] + managed = False + + # Interní ID + id = models.AutoField(primary_key = True) + + rocnik = models.ForeignKey(Rocnik, verbose_name='ročník', related_name='cisla', + db_index=True,on_delete=models.PROTECT) + + poradi = models.CharField('název čísla', max_length=32, db_index=True, + help_text='Většinou jen "1", vyjímečně "7-8", lexikograficky určuje pořadí v ročníku!') + + datum_vydani = models.DateField('datum vydání', blank=True, null=True, + help_text='Datum vydání finální verze') + + verejne_db = models.BooleanField('číslo zveřejněno', + db_column='verejne', default=False) + + poznamka = models.TextField('neveřejná poznámka', blank=True, + help_text='Neveřejná poznámka k číslu (plain text)') + + pdf = models.FileField('pdf', upload_to=cislo_pdf_filename, null=True, blank=True, + help_text='PDF čísla, které si mohou řešitelé stáhnout', storage=OverwriteStorage()) + + titulka_nahled = models.ImageField('Obrázek titulní strany', upload_to=cislo_png_filename, null=True, blank=True, + help_text='Obrázek titulní strany, generuje se automaticky') + + def kod(self): + return '%s.%s' % (self.rocnik.rocnik, self.poradi) + kod.short_description = 'Kód čísla' + + def __str__(self): + # Potenciální DB HOG, pokud by se ročník necachoval + r = Rocnik.cached_rocnik(self.rocnik_id) + return '{}.{}'.format(r.rocnik, self.poradi) + + def verejne(self): + return self.verejne_db + verejne.boolean = True + + def verejne_url(self): + return reverse('seminar_cislo', kwargs={'rocnik': self.rocnik.rocnik, 'cislo': self.poradi}) + + def absolute_url(self): + return "https://" + str(get_current_site(None)) + self.verejne_url() + + def nasledujici(self): + "Vrací None, pokud je toto poslední" + return self.relativni_v_rocniku(1) + + def predchozi(self): + "Vrací None, pokud je toto první" + return self.relativni_v_rocniku(-1) + + def relativni_v_rocniku(self, rel_index): + "Číslo o `index` dále v ročníku. None pokud neexistuje." + cs = self.rocnik.cisla.order_by('poradi').all() + i = list(cs).index(self) + rel_index + if (i < 0) or (i >= len(cs)): + return None + return cs[i] + + def vygeneruj_nahled(self): + VYSKA = 594 + sirka = int(VYSKA*210/297) + if not self.pdf: + return + + + # Pokud obrázek neexistuje nebo není aktuální, vytvoř jej + if not self.titulka_nahled or os.path.getmtime(self.titulka_nahled.path) < os.path.getmtime(self.pdf.path): + png_filename = pathlib.Path(tempfile.mkdtemp(), 'nahled.png') + + subprocess.run([ + "gs", + "-sstdout=%stderr", + "-dSAFER", + "-dNOPAUSE", + "-dBATCH", + "-dNOPROMPT", + "-sDEVICE=png16m", + "-r300x300", + "-dFirstPage=1d", + "-dLastPage=1d", + "-sOutputFile=" + str(png_filename), + "-f%s" % self.pdf.path + ], + check=True, + capture_output=True + ) + + with open(png_filename,'rb') as f: + self.titulka_nahled.save('',f,True) + + png_filename.unlink() + png_filename.parent.rmdir() + + @classmethod + def get(cls, rocnik, cislo): + try: + r = Rocnik.objects.get(rocnik=rocnik) + c = r.cisla.get(poradi=cislo) + except ObjectDoesNotExist: + return None + return c + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.__original_verejne = self.verejne_db + + def posli_cislo_mailem(self): + # parametry e-mailu + odkaz = self.absolute_url() + + poslat_z_mailu = 'zadani@mam.mff.cuni.cz' + predmet = 'Vyšlo číslo {}'.format(self.kod()) + # TODO Možná nechceme všem psát „Ahoj“, např. příjemcům… + text_mailu = 'Ahoj,\n' \ + 'na adrese {} najdete nejnovější číslo.\n' \ + 'Vaše M&M\n'.format(odkaz) + + predmet_prvni = 'Právě vyšlo 1. číslo M&M, pomoz nám ho poslat dál!' + text_mailu_prvni = 'Milý řešiteli,\n'\ + 'právě jsme na našem webu zveřejnili první číslo {}. ročníku, najdeš ho na tomto odkazu: {}.\n\n'\ + 'Doufáme, že tě M&M baví, a byli bychom rádi, kdyby mohlo dělat radost i dalším středoškolákům. Máme na tebe proto jednu prosbu. Sdílej prosím odkaz alespoň s jedním svým kamarádem, který by mohl mít o řešení M&M zájem. Je to pro nás moc důležité a velmi nám tím pomůžeš. Díky!\n\n'\ + 'Organizátoři M&M\n'.format(self.rocnik.rocnik, odkaz) + + predmet_resitel = predmet_prvni if self.poradi == "1" else predmet + text_mailu_resitel = text_mailu_prvni if self.poradi == "1" else text_mailu + + # Prijemci e-mailu + resitele_vsichni = aktivniResitele(self).filter(zasilat_cislo_emailem=True) + + def posli(subject, text, resitele): + emaily = map(lambda resitel: resitel.osoba.email, resitele) + + email = EmailMessage( + subject=subject, + body=text, + from_email=poslat_z_mailu, + bcc=list(emaily) + #bcc = příjemci skryté kopie + ) + + email.send() + + paticka = "---\nK odběru těchto e-mailů jste se přihlásili na stránkách https://mam.matfyz.cz. Z odběru se lze odhlásit na https://mam.matfyz.cz/resitel/osobni-udaje/" + + posli(predmet_resitel, text_mailu_resitel + paticka, resitele_vsichni.filter(zasilat_cislo_papirove=False)) + posli(predmet_resitel, text_mailu_resitel + 'P. S. Brzy budeme též rozesílat papírovou verzi čísla. Připomínáme, že pokud papírovou verzi čísla nevyužijete, můžete v https://mam.mff.cuni.cz/resitel/osobni-udaje/ zaškrtnout, abychom vám ji neposílali. Čísla vždy můžete nalézt v našem archivu a dál vám budou chodit e-mailem. Děkujeme.\n' + paticka, + resitele_vsichni.filter(zasilat_cislo_papirove=True)) + + paticka_prijemce = "---\nPokud tyto e-maily nechcete nadále dostávat, prosíme, ozvěte se nám na mam@matfyz.cz." + posli(predmet, text_mailu + paticka_prijemce, Prijemce.objects.filter(zasilat_cislo_emailem=True)) + + def save(self, *args, **kwargs): + super().save(*args, **kwargs) + self.vygeneruj_nahled() + # Při zveřejnění pošle mail + if self.verejne_db and not self.__original_verejne: + self.posli_cislo_mailem() + # *Node.save() aktualizuje název *Nodu. + try: + self.cislonode.save() + 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 + CisloNode.objects.create(cislo=self) + + def zlomovy_deadline_pro_papirove_cislo(self): + prvni_deadline = Deadline.objects.filter(Q(typ=Deadline.TYP_PRVNI) | Q(typ=Deadline.TYP_PRVNI_A_SOUS), cislo=self).first() + if prvni_deadline is None: + posledni_deadline = self.posledni_deadline + if posledni_deadline is None: + # TODO promyslet, co se má stát tady + return Deadline.objects.filter(Q(cislo__poradi__lt=self.poradi, cislo__rocnik=self.rocnik) | Q(cislo__rocnik__rocnik__lt=self.rocnik.rocnik)).order_by("deadline").last() + return posledni_deadline + return prvni_deadline + + @property + def posledni_deadline(self): + return self.deadline_v_cisle.all().order_by("deadline").last() + +class Deadline(SeminarModelBase): + class Meta: + db_table = 'seminar_deadliny' + verbose_name = 'Deadline' + verbose_name_plural = 'Deadliny' + ordering = ['deadline'] + managed = False + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.__original_verejna_vysledkovka = self.verejna_vysledkovka + + id = models.AutoField(primary_key=True) + + # V ročníku < 26 nastaveno na datetime.datetime.combine(datetime.date(1994 + cislo.rocnik.rocnik, 6, int(cislo.poradi[0])), datetime.time.min) + deadline = models.DateTimeField(blank=False, default=timezone.make_aware(datetime.datetime.combine(timezone.now(), datetime.time.max))) + + cislo = models.ForeignKey(Cislo, verbose_name='deadline v čísle', + related_name='deadline_v_cisle', blank=False, + on_delete=models.CASCADE) + + TYP_CISLA = 'cisla' + TYP_PRVNI_A_SOUS = 'prvniasous' + TYP_PRVNI = 'prvni' + TYP_SOUS = 'sous' + TYP_CHOICES = [ + (TYP_CISLA, 'Deadline celého čísla'), + (TYP_PRVNI, 'První deadline'), + (TYP_PRVNI_A_SOUS, 'Sousový a první deadline'), + (TYP_SOUS, 'Sousový deadline'), + ] + CHOICES_MAP = dict(TYP_CHOICES) + typ = models.CharField('typ deadlinu', max_length=32, + choices=TYP_CHOICES, blank=False) + + verejna_vysledkovka = models.BooleanField('veřejná výsledkovka', + db_column='verejna_vysledkovka', + default=False) + + def __str__(self): + return self.CHOICES_MAP[self.typ] + " " + str(self.cislo) + + def save(self, *args, **kwargs): + super().save(*args, **kwargs) + if self.verejna_vysledkovka and not self.__original_verejna_vysledkovka: + self.vygeneruj_vysledkovku() + if not self.verejna_vysledkovka and hasattr(self, "vysledkovka_v_deadlinu"): + self.vysledkovka_v_deadlinu.delete() + + def vygeneruj_vysledkovku(self): + from vysledkovky.utils import VysledkovkaCisla + if hasattr(self, "vysledkovka_v_deadlinu"): + self.vysledkovka_v_deadlinu.delete() + vysledkovka = VysledkovkaCisla(self.cislo, jen_verejne=True, do_deadlinu=self) + if len(vysledkovka.radky_vysledkovky) != 0: + ZmrazenaVysledkovka.objects.create( + deadline=self, + html=render_to_string( + "vysledkovky/vysledkovka_cisla.html", + context={"vysledkovka": vysledkovka, "oznaceni_vysledkovky": self.id} + ) + ) + + +class ZmrazenaVysledkovka(SeminarModelBase): + class Meta: + db_table = 'seminar_vysledkovky' + verbose_name = 'Zmražená výsledkovka' + verbose_name_plural = 'Zmražené výsledkovky' + managed = False + + deadline = models.OneToOneField( + Deadline, + on_delete=models.CASCADE, + primary_key=True, + related_name="vysledkovka_v_deadlinu" + ) + + html = models.TextField(null=False, blank=False) + +class Problemy_Opravovatele(SeminarModelBase): + """Jen vazebná tabulka pro opravovatele. + + Ona stejně existovala, při přesunu mezi aplikacemi jen potřebujeme zajistit nepřejmenování DB tabulky. + Proto taky nepotřebuje žádná specifika, ze :py:class:SeminarModelBase: dědí ze zvyku než že by to k něčemu kdy měo být. + """ + class Meta: + db_table = 'seminar_problemy_opravovatele' + managed = False + + id = models.AutoField(primary_key = True) + + problem = models.ForeignKey('Problem', on_delete=models.CASCADE) + organizator = models.ForeignKey(Organizator, on_delete=models.CASCADE) + +@reversion.register(ignore_duplicates=True) +# Pozor na následující řádek. *Nekrmit, asi kouše!* +class Problem(SeminarModelBase,PolymorphicModel): + + class Meta: + # Není abstraktní, protože se na něj jinak nedají dělat ForeignKeys. + # TODO: Udělat to polymorfní (pomocí django-polymorphic), abychom dostali + # po těch vazbách přímo tu úlohu/témátko vč. fieldů, které nejsou součástí + # modelu Problem? + + #abstract = True + db_table = 'seminar_problemy' + verbose_name = 'Problém' + verbose_name_plural = 'Problémy' + ordering = ['nazev'] + managed = False + + # Interní ID + id = models.AutoField(primary_key = True) + + # Název + nazev = models.CharField('název', max_length=256) # Zveřejnitelný na stránky + + # Problém má podproblémy + nadproblem = models.ForeignKey('self', verbose_name='nadřazený problém', + related_name='podproblem', null=True, blank=True, + on_delete=models.SET_NULL) + + STAV_NAVRH = 'navrh' + STAV_ZADANY = 'zadany' + STAV_VYRESENY = 'vyreseny' + STAV_SMAZANY = 'smazany' + STAV_CHOICES = [ + (STAV_NAVRH, 'Návrh'), + (STAV_ZADANY, 'Zadaný'), + (STAV_VYRESENY, 'Vyřešený'), + (STAV_SMAZANY, 'Smazaný'), + ] + stav = models.CharField('stav problému', max_length=32, choices=STAV_CHOICES, blank=False, default=STAV_NAVRH) + # Téma je taky Problém, takže má stavy, "zadané" témátko je aktuálně otevřené a dá se k němu něco poslat (řešení nebo článek) + + zamereni = TaggableManager(verbose_name='zaměření', + help_text='Zaměření M/F/I/O problému, příp. další tagy', blank=True) + + poznamka = models.TextField('org poznámky (HTML)', blank=True, + help_text='Neveřejný návrh úlohy, návrh řešení, text zadání, poznámky ...') + + autor = models.ForeignKey(Organizator, verbose_name='autor problému', + related_name='autor_problemu_%(class)s', null=True, blank=True, + on_delete=models.SET_NULL) + + garant = models.ForeignKey(Organizator, verbose_name='garant zadaného problému', + related_name='garant_problemu_%(class)s', null=True, blank=True, + on_delete=models.SET_NULL) + + opravovatele = models.ManyToManyField(Organizator, verbose_name='opravovatelé', + blank=True, related_name='opravovatele_%(class)s', through=Problemy_Opravovatele) + + kod = models.CharField('lokální kód', max_length=32, blank=True, default='', + help_text='Číslo/kód úlohy v čísle nebo kód tématu/článku/seriálu v ročníku') + + vytvoreno = models.DateTimeField('vytvořeno', default=timezone.now, blank=True, editable=False) + + def __str__(self): + return self.nazev + + # Implicitini implementace, jednotlivé dědící třídy si přepíšou + @cached_property + def kod_v_rocniku(self): + if self.stav == Problem.STAV_ZADANY or self.stav == Problem.STAV_VYRESENY: + if self.nadproblem: + return self.nadproblem.kod_v_rocniku+".{}".format(self.kod) + return str(self.kod) + logger.warning(f"K problému {self} byl vyžadován kód v ročníku, i když není zadaný ani vyřešený.") + return f'' + +# def verejne(self): +# # aktuálně podle stavu problému +# # FIXME pro některé problémy možná chceme override +# # FIXME vrací veřejnost čistě problému, nezávisle na čísle, ve kterém je. +# # Je to tak správně? Podle aktuální představy ano. +# stav_verejny = False +# if self.stav == 'zadany' or self.stav == 'vyreseny': +# stav_verejny = True +# print("stav_verejny: {}".format(stav_verejny)) +# +# cislo_verejne = False +# cislonode = self.cislo_node() +# if cislonode is None: +# # problém nemá vlastní node, veřejnost posuzujeme jen podle stavu +# print("empty node") +# return stav_verejny +# else: +# cislo_zadani = cislonode.cislo +# if (cislo_zadani and cislo_zadani.verejne()): +# print("cislo: {}".format(cislo_zadani)) +# cislo_verejne = True +# print("stav_verejny: {}".format(stav_verejny)) +# print("cislo_verejne: {}".format(cislo_verejne)) +# return (stav_verejny and cislo_verejne) +# verejne.boolean = True + + def verejne_url(self): + return reverse('seminar_problem', kwargs={'pk': self.id}) + + def admin_url(self): + return reverse('admin:seminar_problem_change', args=(self.id, )) + + @cached_property + def hlavni_problem(self): + """ Pro daný problém vrátí jeho nejvyšší nadproblém.""" + problem = self + while not (problem.nadproblem is None): + problem = problem.nadproblem + return problem + +# FIXME - k úloze + def body_v_zavorce(self): + """Vrať string s body v závorce jsou-li u problému vyplněné, jinak '' + + Je-li desetinná část nulová, nezobrazuj ji. + """ + pocet_bodu = None + if self.body: + b = self.body + pocet_bodu = int(b) if int(b) == b else b + return "({}\u2009b)".format(pocet_bodu) if self.body else "" + +class Tema(Problem): + class Meta: + db_table = 'seminar_temata' + verbose_name = 'Téma' + verbose_name_plural = 'Témata' + managed = False + + TEMA_TEMA = 'tema' + TEMA_SERIAL = 'serial' + TEMA_CHOICES = [ + (TEMA_TEMA, 'Téma'), + (TEMA_SERIAL, 'Seriál'), + ] + tema_typ = models.CharField('Typ tématu', max_length=16, choices=TEMA_CHOICES, + blank=False, default=TEMA_TEMA) + + rocnik = models.ForeignKey(Rocnik, verbose_name='ročník',related_name='temata',blank=True, null=True, + on_delete=models.PROTECT) + + abstrakt = models.TextField('Abstrakt na rozcestník', blank=True) + obrazek = models.ImageField('Obrázek na rozcestník', null=True, blank=True) + + @cached_property + def kod_v_rocniku(self): + if self.stav == Problem.STAV_ZADANY or self.stav == Problem.STAV_VYRESENY: + if self.nadproblem: + return self.nadproblem.kod_v_rocniku+".t{}".format(self.kod) + return 't'+self.kod + logger.warning(f"K problému {self} byl vyžadován kód v ročníku, i když není zadaný ani vyřešený.") + return f'' + + def save(self, *args, **kwargs): + super().save(*args, **kwargs) + # *Node.save() aktualizuje název *Nodu. + for tvcn in self.temavcislenode_set.all(): + tvcn.save() + + def cislo_node(self): + tema_node_set = self.temavcislenode_set.all() + tema_cisla_vyskyt = [] + from seminar.models.treenode import CisloNode + for tn in tema_node_set: + tema_cisla_vyskyt.append( + treelib.get_upper_node_of_type(tn, CisloNode).cislo) + tema_cisla_vyskyt.sort(key=lambda x:x.datum_vydani) + prvni_zadani = tema_cisla_vyskyt[0] + return prvni_zadani.cislonode + +class Clanek(Problem): + class Meta: + db_table = 'seminar_clanky' + verbose_name = 'Článek' + verbose_name_plural = 'Články' + managed = False + + cislo = models.ForeignKey(Cislo, blank=True, null=True, on_delete=models.PROTECT, + verbose_name='číslo vydání', related_name='vydane_clanky') + + strana = models.PositiveIntegerField(verbose_name="první strana", blank=True, null=True) + + @cached_property + def kod_v_rocniku(self): + if self.stav == Problem.STAV_ZADANY or self.stav == Problem.STAV_VYRESENY: +# Nemělo by být potřeba +# if self.nadproblem: +# return self.nadproblem.kod_v_rocniku+".c{}".format(self.kod) + return "c" + self.kod + logger.warning(f"K problému {self} byl vyžadován kód v ročníku, i když není zadaný ani vyřešený.") + return f'' + + def node(self): + return None + + +class Uloha(Problem): + class Meta: + db_table = 'seminar_ulohy' + verbose_name = 'Úloha' + verbose_name_plural = 'Úlohy' + managed = False + + cislo_zadani = models.ForeignKey(Cislo, verbose_name='číslo zadání', blank=True, + null=True, related_name='zadane_ulohy', on_delete=models.PROTECT) + + cislo_deadline = models.ForeignKey(Cislo, verbose_name='číslo deadlinu', blank=True, + null=True, related_name='deadlinove_ulohy', on_delete=models.PROTECT) + + cislo_reseni = models.ForeignKey(Cislo, verbose_name='číslo řešení', blank=True, + null=True, related_name='resene_ulohy', + help_text='Číslo s řešením úlohy, jen pro úlohy', + on_delete=models.PROTECT) + + max_body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='maximum bodů', + blank=True, null=True) + + @cached_property + def kod_v_rocniku(self): + if self.stav == Problem.STAV_ZADANY or self.stav == Problem.STAV_VYRESENY: + return f"{self.cislo_zadani.poradi}.{self.kod}" + logger.warning(f"K problému {self} byl vyžadován kód v ročníku, i když není zadaný ani vyřešený.") + return f'' + + def save(self, *args, **kwargs): + super().save(*args, **kwargs) + # *Node.save() aktualizuje název *Nodu. + try: + self.ulohazadaninode.save() + except ObjectDoesNotExist: + # Neexistující *Node nemá smysl aktualizovat. + pass + try: + self.ulohavzoraknode.save() + except ObjectDoesNotExist: + # Neexistující *Node nemá smysl aktualizovat. + pass + + def cislo_node(self): + zadani_node = self.ulohazadaninode + from seminar.models.treenode import CisloNode + return treelib.get_upper_node_of_type(zadani_node, CisloNode) + + +def aux_generate_filename(self, filename): + """Pomocná funkce generující ošetřený název souboru v adresáři s datem""" + clean = get_valid_filename( + unidecode(filename.replace('/', '-').replace('\0', '')) + ) + datedir = timezone.now().strftime('%Y-%m') + fname = "{}/{}".format( + timezone.now().strftime('%Y-%m-%d-%H:%M'), + clean) + return os.path.join(datedir, fname) + + +class Pohadka(SeminarModelBase): + """Kus pohádky před/za úlohou v čísle""" + + class Meta: + db_table = 'seminar_pohadky' + verbose_name = 'Pohádka' + verbose_name_plural = 'Pohádky' + ordering = ['vytvoreno'] + managed = False + + # Interní ID + id = models.AutoField(primary_key=True) + + autor = models.ForeignKey( + Organizator, + verbose_name="Autor pohádky", + + # Při nahrávání z TeXu není vyplnění vyžadováno, v adminu je + null=True, + blank=False, + on_delete=models.SET_NULL, + ) + + vytvoreno = models.DateTimeField( + 'Vytvořeno', + default=timezone.now, + blank=True, + editable=False + ) + + def __str__(self): + uryvek = self.text if len(self.text) < 50 else self.text[:(50-3)]+"..." + return uryvek + + def save(self, *args, **kwargs): + super().save(*args, **kwargs) + # *Node.save() aktualizuje název *Nodu. + try: + self.pohadkanode.save() + except ObjectDoesNotExist: + # Neexistující *Node nemá smysl aktualizovat. + pass + diff --git a/various/migrations/0004_tvorba_pre.py b/various/migrations/0004_tvorba_pre.py new file mode 100644 index 00000000..20c1a3a0 --- /dev/null +++ b/various/migrations/0004_tvorba_pre.py @@ -0,0 +1,13 @@ +# Generated by Django 4.2.16 on 2024-10-30 01:06 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('various', '0003_fix_permissions'), + ] + + operations = [ + ] diff --git a/various/migrations/0005_tvorba_relink.py b/various/migrations/0005_tvorba_relink.py new file mode 100644 index 00000000..5ec264e8 --- /dev/null +++ b/various/migrations/0005_tvorba_relink.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.16 on 2024-10-30 13:18 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('tvorba', '0001_tvorba_create'), + ('various', '0004_tvorba_pre'), + ] + + operations = [ + migrations.AlterField( + model_name='nastaveni', + name='aktualni_cislo', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='tvorba.cislo', verbose_name='Aktuální číslo'), + ), + ] diff --git a/various/models.py b/various/models.py index 17632c46..e31b4e72 100644 --- a/various/models.py +++ b/various/models.py @@ -3,7 +3,7 @@ from django.db import models from reversion import revisions as reversion from solo.models import SingletonModel -from seminar.models import Cislo +from tvorba.models import Cislo from django.urls import reverse From 5db14ea24214360104445359a4f10910c4600654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Wed, 30 Oct 2024 15:03:17 +0100 Subject: [PATCH 17/51] =?UTF-8?q?=C4=8C=C3=A1ste=C4=8Dn=C3=A9=20=C5=99e?= =?UTF-8?q?=C5=A1en=C3=AD=20#1465=20(Podez=C5=99el=C3=A9=20`seminar`e)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/tests/test_skola_autocomplete.py | 4 ++-- api/views/exports.py | 2 +- galerie/views.py | 2 +- novinky/templates/novinky/novinky.html | 2 +- odevzdavatko/admin.py | 2 +- odevzdavatko/forms.py | 2 +- odevzdavatko/templatetags/barvy_reseni.py | 4 ++-- odevzdavatko/templatetags/jmena.py | 4 ++-- personalni/admin.py | 2 +- personalni/forms.py | 2 +- personalni/tests.py | 2 +- personalni/utils.py | 6 ++--- personalni/views.py | 24 ++++++++++--------- prednasky/admin.py | 2 +- prednasky/views.py | 3 ++- .../treenode/treenode_recursive.html | 12 +++++----- .../tvorba/archiv/problem_clanek.html | 2 +- .../templates/tvorba/archiv/problem_tema.html | 2 +- .../tvorba/archiv/problem_uloha.html | 2 +- .../tvorba/archiv/problem_uloha_tema.html | 2 +- .../tvorba/clanky/organizatorske_clanky.html | 2 +- vyroci/views.py | 2 +- 22 files changed, 45 insertions(+), 42 deletions(-) diff --git a/api/tests/test_skola_autocomplete.py b/api/tests/test_skola_autocomplete.py index 75019983..f5bf420a 100644 --- a/api/tests/test_skola_autocomplete.py +++ b/api/tests/test_skola_autocomplete.py @@ -1,6 +1,6 @@ from django.test import TestCase, tag from django.urls import reverse -import seminar.models as m +from personalni.models import Skola from personalni.utils import sync_skoly @tag('stejny-model-na-produkci') @@ -48,7 +48,7 @@ class OrgSkolyAutocompleteTestCase(TestCase): """Testuje, že pro každého orga je jeho škola ve výsledném QuerySetu""" for pfx, id in self.spravna_data: with self.subTest(prefix=pfx, spravne_id=id): - spravna_skola = m.Skola.objects.get(id=id) + spravna_skola = Skola.objects.get(id=id) # Zeptáme se view, co si myslí resp = self.client.get(reverse('autocomplete_skola')+'?q='+pfx).json() ids = [int(x['id']) for x in resp['results']] diff --git a/api/views/exports.py b/api/views/exports.py index 5c1d57af..1545c2bc 100644 --- a/api/views/exports.py +++ b/api/views/exports.py @@ -1,4 +1,4 @@ -import seminar.models as m +import personalni.models as m from django.core import serializers as ser from django.http import HttpResponse def exportSkolView(request): diff --git a/galerie/views.py b/galerie/views.py index 872bb46e..cf9ade8b 100644 --- a/galerie/views.py +++ b/galerie/views.py @@ -6,7 +6,7 @@ from django.template import RequestContext from datetime import datetime from galerie.models import Obrazek, Galerie -from seminar.models import Soustredeni +from soustredeni.models import Soustredeni from galerie.forms import KomentarForm, NewGalerieForm def zobrazit(galerie, request): diff --git a/novinky/templates/novinky/novinky.html b/novinky/templates/novinky/novinky.html index 535349a5..59f9377b 100644 --- a/novinky/templates/novinky/novinky.html +++ b/novinky/templates/novinky/novinky.html @@ -4,7 +4,7 @@ {% if not novinka.zverejneno and user.je_org %}
{% endif %} {% if novinka.zverejneno or user.je_org %} diff --git a/odevzdavatko/admin.py b/odevzdavatko/admin.py index 6048eb36..b789e3c5 100644 --- a/odevzdavatko/admin.py +++ b/odevzdavatko/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin from django_reverse_admin import ReverseModelAdmin -import seminar.models as m +import odevzdavatko.models as m class PrilohaReseniInline(admin.TabularInline): diff --git a/odevzdavatko/forms.py b/odevzdavatko/forms.py index 583523e3..0ba86654 100644 --- a/odevzdavatko/forms.py +++ b/odevzdavatko/forms.py @@ -4,7 +4,7 @@ from django.forms import formset_factory from django.forms.models import inlineformset_factory from django.utils import timezone -from seminar.models import Resitel +from personalni.models import Resitel import seminar.models as m import logging diff --git a/odevzdavatko/templatetags/barvy_reseni.py b/odevzdavatko/templatetags/barvy_reseni.py index 5a3791fd..f0324ad9 100644 --- a/odevzdavatko/templatetags/barvy_reseni.py +++ b/odevzdavatko/templatetags/barvy_reseni.py @@ -2,11 +2,11 @@ from django import template register = template.Library() from functools import cache -import seminar.models as m +from odevzdavatko.models import Reseni @register.filter @cache -def barva_reseni(r: m.Reseni): +def barva_reseni(r: Reseni): """Vrátí nějakou barvu pro daný problém, ve tvaru '#RRGGBB' Efektivně hešujeme do barev.""" diff --git a/odevzdavatko/templatetags/jmena.py b/odevzdavatko/templatetags/jmena.py index 9fe91ff5..9a08acaa 100644 --- a/odevzdavatko/templatetags/jmena.py +++ b/odevzdavatko/templatetags/jmena.py @@ -2,8 +2,8 @@ from django import template register = template.Library() from personalni.utils import normalizuj_jmeno -import seminar.models as m # jen kvůli typové anotaci… +from personalni.models import Osoba # jen kvůli typové anotaci… @register.filter -def jmeno_jako_prefix(o: m.Osoba): +def jmeno_jako_prefix(o: Osoba): return normalizuj_jmeno(o).replace(' ', '_') diff --git a/personalni/admin.py b/personalni/admin.py index 3418769a..f940ca4a 100644 --- a/personalni/admin.py +++ b/personalni/admin.py @@ -2,7 +2,7 @@ from django.contrib import admin from django.contrib.auth.models import Group from django_reverse_admin import ReverseModelAdmin from django.contrib.messages import WARNING, ERROR, SUCCESS -import seminar.models as m +import personalni.models as m from datetime import datetime @admin.action(description="Sjednoť telefony") diff --git a/personalni/forms.py b/personalni/forms.py index ea0891e7..57e1b398 100644 --- a/personalni/forms.py +++ b/personalni/forms.py @@ -4,7 +4,7 @@ from django.contrib.auth.forms import PasswordResetForm from django.core.exceptions import ObjectDoesNotExist from django.contrib.auth.models import User -from seminar.models import Skola, Resitel, Osoba +from personalni.models import Skola, Resitel, Osoba from datetime import date import logging diff --git a/personalni/tests.py b/personalni/tests.py index 31aac8e8..a73d7863 100644 --- a/personalni/tests.py +++ b/personalni/tests.py @@ -4,7 +4,7 @@ from django.contrib.auth.models import User, Group from django.contrib.admin.sites import AdminSite from personalni.admin import OsobaAdmin # Tohle bude peklo, až jednou ty modely fakt rozstřelíme… Možná vyrobit various.all_models, které půjdou importovat jako m? :-) -import seminar.models as m +import personalni.models as m import logging logger = logging.getLogger(__name__) diff --git a/personalni/utils.py b/personalni/utils.py index 4aac1e28..02e541da 100644 --- a/personalni/utils.py +++ b/personalni/utils.py @@ -1,4 +1,3 @@ -import seminar.models as m from various.utils import bez_diakritiky_translate import re @@ -7,9 +6,10 @@ from django.contrib.auth.decorators import permission_required, user_passes_test from django.contrib.auth.models import AnonymousUser from django.db import transaction -import seminar.models as m import soustredeni.models +from odevzdavatko.models import Reseni_Resitele + from .models import Osoba, Organizator, Skola, Resitel, Prijemce @@ -97,7 +97,7 @@ def merge_resitele(cilovy, zdrojovy): # Přepojit všechny vazby ze zdrojového na cílového print('Přepojuji vazby') # Vazby: Škola (hotovo), Řešení_Řešitelé, Konfery_Účastníci, Soustředění_Účastníci, Osoba (vyřeší se později, nejde přepojit) - ct = m.Reseni_Resitele.objects.filter(resitele=zdrojovy).update(resitele=cilovy) + ct = Reseni_Resitele.objects.filter(resitele=zdrojovy).update(resitele=cilovy) print(f' Přepojeno {ct} řešení') ct = soustredeni.models.Konfery_Ucastnici.objects.filter(resitel=zdrojovy).update(resitel=cilovy) print(f' Přepojeno {ct} konfer') diff --git a/personalni/views.py b/personalni/views.py index 12768c34..d994974d 100644 --- a/personalni/views.py +++ b/personalni/views.py @@ -17,7 +17,9 @@ from django.http import HttpResponse from django.utils import timezone import seminar.models as s -import seminar.models as m +import personalni.models as m +from soustredeni.models import Soustredeni +from odevzdavatko.models import Hodnoceni from .forms import PrihlaskaForm, ProfileEditForm, PoMaturiteProfileEditForm from datetime import date @@ -91,7 +93,7 @@ class OrgoRozcestnikView(TemplateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['posledni_soustredeni'] = s.Soustredeni.objects.order_by('-datum_konce').first() + context['posledni_soustredeni'] = Soustredeni.objects.order_by('-datum_konce').first() nastaveni = s.Nastaveni.objects.first() aktualni_rocnik = nastaveni.aktualni_rocnik context['posledni_cislo_url'] = nastaveni.aktualni_cislo.verejne_url() @@ -99,14 +101,14 @@ class OrgoRozcestnikView(TemplateView): # pokud nechceme haluzit kód (= poradi) dalšího čísla, bude asi potřeba jít # přes treenody (a dát si přitom pozor na MezicisloNode) - neobodovana_reseni = s.Hodnoceni.objects.filter(body__isnull=True) - reseni_mimo_cislo = s.Hodnoceni.objects.filter(deadline_body__isnull=True) + neobodovana_reseni = Hodnoceni.objects.filter(body__isnull=True) + reseni_mimo_cislo = Hodnoceni.objects.filter(deadline_body__isnull=True) context['pocet_neobodovanych_reseni'] = neobodovana_reseni.count() context['pocet_reseni_mimo_cislo'] = reseni_mimo_cislo.count() u = self.request.user - os = s.Osoba.objects.get(user=u) - organizator = s.Organizator.objects.get(osoba=os) + os = m.Osoba.objects.get(user=u) + organizator = m.Organizator.objects.get(osoba=os) context['muj_pocet_neobodovanych_reseni'] = neobodovana_reseni.filter(Q(problem__garant=organizator) | Q(problem__autor=organizator) | Q(problem__opravovatele__in=[organizator])).distinct().count() context['muj_pocet_reseni_mimo_cislo'] = reseni_mimo_cislo.filter(Q(problem__garant=organizator) | Q(problem__autor=organizator) | Q(problem__opravovatele__in=[organizator])).count() @@ -134,12 +136,12 @@ class OrgoRozcestnikView(TemplateView): class ResitelView(LoginRequiredMixin,generic.DetailView): - model = s.Resitel + model = m.Resitel template_name = 'personalni/profil/resitel.html' def get_object(self, queryset=None): print(self.request.user) - return s.Resitel.objects.get(osoba__user=self.request.user) + return m.Resitel.objects.get(osoba__user=self.request.user) ### Formulare @@ -160,7 +162,7 @@ def resitelEditView(request): err_logger = logging.getLogger('seminar.prihlaska.problem') ## Načtení objektů Osoba a Resitel patřících k aktuálně přihlášenému uživateli u = request.user - osoba_edit = s.Osoba.objects.get(user=u) + osoba_edit = m.Osoba.objects.get(user=u) if hasattr(osoba_edit,'resitel'): resitel_edit = osoba_edit.resitel else: @@ -266,7 +268,7 @@ def prihlaskaView(request): resitel_grp = Group.objects.filter(name__exact='resitel').first() u.groups.add(resitel_grp) - o = s.Osoba( + o = m.Osoba( jmeno = fcd['jmeno'], prijmeni = fcd['prijmeni'], osloveni = fcd['osloveni'], @@ -328,7 +330,7 @@ def prihlaskaView(request): if kolize.count() > 1: # Jednu z nich jsme právě uložili err_logger.warning(f'Zaregistrovala se osoba s kolizním jménem. ID osob: {[o.id for o in kolize]}') - r = s.Resitel( + r = m.Resitel( prezdivka_resitele=fcd['prezdivka_resitele'] if fcd['prezdivka_resitele'] != "" else None, rok_maturity = fcd['rok_maturity'], zasilat = fcd['zasilat'], diff --git a/prednasky/admin.py b/prednasky/admin.py index d086cd0c..19eace7f 100644 --- a/prednasky/admin.py +++ b/prednasky/admin.py @@ -5,7 +5,7 @@ from django.utils.safestring import mark_safe from django.utils.html import escape from .models import Prednaska, Seznam, STAV_NAVRH -from seminar.models import Soustredeni +from soustredeni.models import Soustredeni class Seznam_PrednaskaInline(admin.TabularInline): diff --git a/prednasky/views.py b/prednasky/views.py index 699b690d..368c98d9 100644 --- a/prednasky/views.py +++ b/prednasky/views.py @@ -6,7 +6,8 @@ from django.db.models import Sum from django.forms import Form from prednasky.models import Prednaska, Hlasovani, Seznam, STAV_NAVRH -from seminar.models import Soustredeni, Osoba +from soustredeni.models import Soustredeni +from personalni.models import Osoba def newPrednaska(request): # hlasovani se vztahuje k nejnovejsimu soustredeni diff --git a/treenode/templates/treenode/treenode_recursive.html b/treenode/templates/treenode/treenode_recursive.html index 6da7a96f..4a54f41f 100644 --- a/treenode/templates/treenode/treenode_recursive.html +++ b/treenode/templates/treenode/treenode_recursive.html @@ -25,20 +25,20 @@ - nejsou testovací data
{% endif %} -{% include "seminar/treenode_name.html" %} +{% include "treenode/treenode_name.html" %} {%if obj.children %}
- {% with kam="před" kam_slug="syn" %} {% include "seminar/treenode_add_stub.html" %} {% endwith %} + {% with kam="před" kam_slug="syn" %} {% include "treenode/treenode_add_stub.html" %} {% endwith %} {%for ch in obj.children %} {# ----------- Vypisujeme podstrom ----------#} - {%with obj=ch depth=depth|add:"1" %} {%include "seminar/treenode_recursive.html" %} {%endwith%} + {%with obj=ch depth=depth|add:"1" %} {%include "treenode/treenode_recursive.html" %} {%endwith%} {# ----------- Přidáváme mezi syny / za posledního -------- #} {% if forloop.last %} - {% with kam="za" kam_slug="za" obj=ch %} {% include "seminar/treenode_add_stub.html" %} {% endwith %} + {% with kam="za" kam_slug="za" obj=ch %} {% include "treenode/treenode_add_stub.html" %} {% endwith %} {% else %} - {% with kam="mezi" obj=ch kam_slug="za" %} {% include "seminar/treenode_add_stub.html" %} {% endwith %} + {% with kam="mezi" obj=ch kam_slug="za" %} {% include "treenode/treenode_add_stub.html" %} {% endwith %} {% endif %} {# ----------- Prohazujeme sousedy ----------#}
@@ -50,6 +50,6 @@
{% else %} {# ----------- Přidáváme prvního syna ----------#} - {% with kam="jako syna" kam_slug="syn" %} {% include "seminar/treenode_add_stub.html" %} {% endwith %} + {% with kam="jako syna" kam_slug="syn" %} {% include "treenode/treenode_add_stub.html" %} {% endwith %} {%endif%}
diff --git a/tvorba/templates/tvorba/archiv/problem_clanek.html b/tvorba/templates/tvorba/archiv/problem_clanek.html index a59e8b87..f14eeb7f 100644 --- a/tvorba/templates/tvorba/archiv/problem_clanek.html +++ b/tvorba/templates/tvorba/archiv/problem_clanek.html @@ -1,4 +1,4 @@ -{% extends "seminar/archiv/problem.html" %} +{% extends "tvorba/archiv/problem.html" %} {% block problem %} {% if problem.cislo_zadani %} diff --git a/tvorba/templates/tvorba/archiv/problem_tema.html b/tvorba/templates/tvorba/archiv/problem_tema.html index 8bb851ea..322f4200 100644 --- a/tvorba/templates/tvorba/archiv/problem_tema.html +++ b/tvorba/templates/tvorba/archiv/problem_tema.html @@ -1,4 +1,4 @@ -{% extends "seminar/archiv/problem.html" %} +{% extends "tvorba/archiv/problem.html" %} {% block problem %}

diff --git a/tvorba/templates/tvorba/archiv/problem_uloha.html b/tvorba/templates/tvorba/archiv/problem_uloha.html index 9a146389..a9d96479 100644 --- a/tvorba/templates/tvorba/archiv/problem_uloha.html +++ b/tvorba/templates/tvorba/archiv/problem_uloha.html @@ -1,4 +1,4 @@ -{% extends "seminar/archiv/problem.html" %} +{% extends "tvorba/archiv/problem.html" %} {% block problem %}

diff --git a/tvorba/templates/tvorba/archiv/problem_uloha_tema.html b/tvorba/templates/tvorba/archiv/problem_uloha_tema.html index 9a146389..a9d96479 100644 --- a/tvorba/templates/tvorba/archiv/problem_uloha_tema.html +++ b/tvorba/templates/tvorba/archiv/problem_uloha_tema.html @@ -1,4 +1,4 @@ -{% extends "seminar/archiv/problem.html" %} +{% extends "tvorba/archiv/problem.html" %} {% block problem %}

diff --git a/tvorba/templates/tvorba/clanky/organizatorske_clanky.html b/tvorba/templates/tvorba/clanky/organizatorske_clanky.html index a7fb03c9..816daad8 100644 --- a/tvorba/templates/tvorba/clanky/organizatorske_clanky.html +++ b/tvorba/templates/tvorba/clanky/organizatorske_clanky.html @@ -1,4 +1,4 @@ -{% extends 'seminar/clanky/resitelske_clanky.html' %} +{% extends 'tvorba/clanky/resitelske_clanky.html' %} {% block nadpis1a %} Organizátorské články diff --git a/vyroci/views.py b/vyroci/views.py index 455d6e25..9c5b5194 100644 --- a/vyroci/views.py +++ b/vyroci/views.py @@ -1,6 +1,6 @@ from django.views.generic import FormView, ListView -from seminar.models import Osoba +from personalni.models import Osoba from various.views.pomocne import formularOKView from .forms import UcastnikVyrociForm from .models import UcastnikVyroci From 062f70e9471a9c2693452f6b65e975c16aa36fde Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Wed, 30 Oct 2024 22:41:11 +0100 Subject: [PATCH 18/51] =?UTF-8?q?odst=C5=99el=20tvorby:=20relink=20?= =?UTF-8?q?=E2=80=93=20post?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy_v2/admin_org_prava.json | 62 +- odevzdavatko/migrations/0006_tvorba_post.py | 14 + personalni/migrations/0015_tvorba_post.py | 14 + seminar/migrations/0138_tvorba_delete.py | 150 ++++ seminar/migrations/0139_tvorba_post.py | 14 + seminar/models/__init__.py | 6 + seminar/models/treenode.py | 2 +- seminar/models/tvorba.py | 731 ------------------ soustredeni/migrations/0005_tvorba_relink.py | 13 +- soustredeni/migrations/0006_tvorba_relink2.py | 17 + soustredeni/migrations/0007_tvorba_relink3.py | 15 + soustredeni/migrations/0008_tvorba_relink4.py | 34 + soustredeni/migrations/0009_tvorba_relink5.py | 17 + soustredeni/migrations/0010_tvorba_post.py | 14 + tvorba/migrations/0002_tvorba_manage.py | 54 ++ tvorba/migrations/0003_tvorba_post.py | 13 + tvorba/models.py | 13 +- various/migrations/0006_tvorba_post.py | 14 + 18 files changed, 418 insertions(+), 779 deletions(-) create mode 100644 odevzdavatko/migrations/0006_tvorba_post.py create mode 100644 personalni/migrations/0015_tvorba_post.py create mode 100644 seminar/migrations/0138_tvorba_delete.py create mode 100644 seminar/migrations/0139_tvorba_post.py create mode 100644 soustredeni/migrations/0006_tvorba_relink2.py create mode 100644 soustredeni/migrations/0007_tvorba_relink3.py create mode 100644 soustredeni/migrations/0008_tvorba_relink4.py create mode 100644 soustredeni/migrations/0009_tvorba_relink5.py create mode 100644 soustredeni/migrations/0010_tvorba_post.py create mode 100644 tvorba/migrations/0002_tvorba_manage.py create mode 100644 tvorba/migrations/0003_tvorba_post.py create mode 100644 various/migrations/0006_tvorba_post.py diff --git a/deploy_v2/admin_org_prava.json b/deploy_v2/admin_org_prava.json index 370aec4f..c7fc8c7d 100644 --- a/deploy_v2/admin_org_prava.json +++ b/deploy_v2/admin_org_prava.json @@ -216,57 +216,57 @@ }, { "codename": "add_cislo", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "cislo" }, { "codename": "change_cislo", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "cislo" }, { "codename": "delete_cislo", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "cislo" }, { "codename": "view_cislo", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "cislo" }, { "codename": "add_clanek", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "clanek" }, { "codename": "change_clanek", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "clanek" }, { "codename": "delete_clanek", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "clanek" }, { "codename": "view_clanek", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "clanek" }, { "codename": "add_deadline", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "deadline" }, { "codename": "change_deadline", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "deadline" }, { "codename": "view_deadline", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "deadline" }, { @@ -371,22 +371,22 @@ }, { "codename": "add_pohadka", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "pohadka" }, { "codename": "change_pohadka", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "pohadka" }, { "codename": "delete_pohadka", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "pohadka" }, { "codename": "view_pohadka", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "pohadka" }, { @@ -411,22 +411,22 @@ }, { "codename": "add_problem", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "problem" }, { "codename": "change_problem", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "problem" }, { "codename": "delete_problem", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "problem" }, { "codename": "view_problem", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "problem" }, { @@ -441,22 +441,22 @@ }, { "codename": "add_rocnik", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "rocnik" }, { "codename": "change_rocnik", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "rocnik" }, { "codename": "delete_rocnik", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "rocnik" }, { "codename": "view_rocnik", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "rocnik" }, { @@ -541,42 +541,42 @@ }, { "codename": "add_tema", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "tema" }, { "codename": "change_tema", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "tema" }, { "codename": "delete_tema", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "tema" }, { "codename": "view_tema", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "tema" }, { "codename": "add_uloha", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "uloha" }, { "codename": "change_uloha", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "uloha" }, { "codename": "delete_uloha", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "uloha" }, { "codename": "view_uloha", - "ct_app_label": "seminar", + "ct_app_label": "tvorba", "ct_model": "uloha" }, { diff --git a/odevzdavatko/migrations/0006_tvorba_post.py b/odevzdavatko/migrations/0006_tvorba_post.py new file mode 100644 index 00000000..13ab895c --- /dev/null +++ b/odevzdavatko/migrations/0006_tvorba_post.py @@ -0,0 +1,14 @@ +# Generated by Django 4.2.16 on 2024-10-30 21:34 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('odevzdavatko', '0005_tvorba_relink'), + ('tvorba', '0003_tvorba_post'), + ] + + operations = [ + ] diff --git a/personalni/migrations/0015_tvorba_post.py b/personalni/migrations/0015_tvorba_post.py new file mode 100644 index 00000000..6580b1e8 --- /dev/null +++ b/personalni/migrations/0015_tvorba_post.py @@ -0,0 +1,14 @@ +# Generated by Django 4.2.16 on 2024-10-30 21:35 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('personalni', '0014_tvorba_pre'), + ('tvorba', '0003_tvorba_post'), + ] + + operations = [ + ] diff --git a/seminar/migrations/0138_tvorba_delete.py b/seminar/migrations/0138_tvorba_delete.py new file mode 100644 index 00000000..0448a160 --- /dev/null +++ b/seminar/migrations/0138_tvorba_delete.py @@ -0,0 +1,150 @@ +# Generated by Django 4.2.16 on 2024-10-30 14:03 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('tvorba', '0001_tvorba_create'), + ('seminar', '0137_tvorba_unmanage'), + ('odevzdavatko', '0005_tvorba_relink'), + ('soustredeni', '0009_tvorba_relink5'), + ('various', '0005_tvorba_relink'), + ] + + operations = [ + migrations.RemoveField( + model_name='cislo', + name='rocnik', + ), + migrations.RemoveField( + model_name='clanek', + name='cislo', + ), + migrations.RemoveField( + model_name='clanek', + name='problem_ptr', + ), + migrations.RemoveField( + model_name='deadline', + name='cislo', + ), + migrations.RemoveField( + model_name='pohadka', + name='autor', + ), + migrations.RemoveField( + model_name='problem', + name='autor', + ), + migrations.RemoveField( + model_name='problem', + name='garant', + ), + migrations.RemoveField( + model_name='problem', + name='nadproblem', + ), + migrations.RemoveField( + model_name='problem', + name='opravovatele', + ), + migrations.RemoveField( + model_name='problem', + name='polymorphic_ctype', + ), + migrations.RemoveField( + model_name='problem', + name='zamereni', + ), + migrations.DeleteModel( + name='Problemy_Opravovatele', + ), + migrations.RemoveField( + model_name='tema', + name='problem_ptr', + ), + migrations.RemoveField( + model_name='tema', + name='rocnik', + ), + migrations.RemoveField( + model_name='uloha', + name='cislo_deadline', + ), + migrations.RemoveField( + model_name='uloha', + name='cislo_reseni', + ), + migrations.RemoveField( + model_name='uloha', + name='cislo_zadani', + ), + migrations.RemoveField( + model_name='uloha', + name='problem_ptr', + ), + migrations.RemoveField( + model_name='zmrazenavysledkovka', + name='deadline', + ), + migrations.AlterField( + model_name='cislonode', + name='cislo', + field=models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to='tvorba.cislo', verbose_name='číslo'), + ), + migrations.AlterField( + model_name='pohadkanode', + name='pohadka', + field=models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to='tvorba.pohadka', verbose_name='pohádka'), + ), + migrations.AlterField( + model_name='rocniknode', + name='rocnik', + field=models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to='tvorba.rocnik', verbose_name='ročník'), + ), + migrations.AlterField( + model_name='temavcislenode', + name='tema', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tvorba.tema', verbose_name='téma v čísle'), + ), + migrations.AlterField( + model_name='ulohavzoraknode', + name='uloha', + field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.PROTECT, to='tvorba.uloha', verbose_name='úloha'), + ), + migrations.AlterField( + model_name='ulohazadaninode', + name='uloha', + field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.PROTECT, to='tvorba.uloha', verbose_name='úloha'), + ), + migrations.DeleteModel( + name='Cislo', + ), + migrations.DeleteModel( + name='Clanek', + ), + migrations.DeleteModel( + name='Deadline', + ), + migrations.DeleteModel( + name='Pohadka', + ), + migrations.DeleteModel( + name='Problem', + ), + migrations.DeleteModel( + name='Rocnik', + ), + migrations.DeleteModel( + name='Tema', + ), + migrations.DeleteModel( + name='Uloha', + ), + migrations.DeleteModel( + name='ZmrazenaVysledkovka', + ), + ] diff --git a/seminar/migrations/0139_tvorba_post.py b/seminar/migrations/0139_tvorba_post.py new file mode 100644 index 00000000..560ddde4 --- /dev/null +++ b/seminar/migrations/0139_tvorba_post.py @@ -0,0 +1,14 @@ +# Generated by Django 4.2.16 on 2024-10-30 21:35 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0138_tvorba_delete'), + ('tvorba', '0003_tvorba_post'), + ] + + operations = [ + ] diff --git a/seminar/models/__init__.py b/seminar/models/__init__.py index 2eabe50f..95e449ab 100644 --- a/seminar/models/__init__.py +++ b/seminar/models/__init__.py @@ -15,3 +15,9 @@ from tvorba.models import ZmrazenaVysledkovka, Deadline, Cislo, Rocnik, Pohadka, from soustredeni.models import generate_filename_konfera # migr. 0001 from odevzdavatko.models import generate_filename +# migr. 0031, 0032, 0081 +from tvorba.models import cislo_pdf_filename +# migr. 0082 +from tvorba.models import cislo_png_filename +# migr 0100 (hack) +import tvorba.models as tvorba diff --git a/seminar/models/treenode.py b/seminar/models/treenode.py index abc20eab..eee40281 100644 --- a/seminar/models/treenode.py +++ b/seminar/models/treenode.py @@ -14,7 +14,7 @@ from .pomocne import Text logger = logging.getLogger(__name__) -from seminar.models import tvorba as am +import tvorba.models as am class TreeNode(PolymorphicModel): class Meta: diff --git a/seminar/models/tvorba.py b/seminar/models/tvorba.py index a8aea6ae..c1f91196 100644 --- a/seminar/models/tvorba.py +++ b/seminar/models/tvorba.py @@ -1,40 +1,7 @@ -import datetime import os -import subprocess -import pathlib -import tempfile import logging -from django.contrib.sites.shortcuts import get_current_site -from django.db import models -from django.db.models import Q -from django.template.loader import render_to_string -from django.utils import timezone -from django.conf import settings -from django.urls import reverse -from django.core.cache import cache -from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.core.files.storage import FileSystemStorage -from django.utils.text import get_valid_filename -from django.utils.functional import cached_property - -from solo.models import SingletonModel -from taggit.managers import TaggableManager - -from reversion import revisions as reversion - -from tvorba.utils import roman, aktivniResitele -from treenode import treelib - -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 django.core.mail import EmailMessage - -from personalni.models import Prijemce, Organizator - -from .base import SeminarModelBase logger = logging.getLogger(__name__) @@ -45,701 +12,3 @@ class OverwriteStorage(FileSystemStorage): if self.exists(name): os.remove(os.path.join(self.location,name)) return super().get_available_name(name,max_length) - -@reversion.register(ignore_duplicates=True) -class Rocnik(SeminarModelBase): - - class Meta: - db_table = 'seminar_rocniky' - verbose_name = 'Ročník' - verbose_name_plural = 'Ročníky' - ordering = ['-rocnik'] - managed = False - - # Interní ID - id = models.AutoField(primary_key = True) - - prvni_rok = models.IntegerField('první rok', db_index=True, unique=True) - - rocnik = models.IntegerField('číslo ročníku', db_index=True, unique=True) - - exportovat = models.BooleanField('export do AESOPa', db_column='exportovat', default=False, - help_text='Exportuje se jen podle tohoto flagu (ne veřejnosti),' - ' a to jen čísla s veřejnou výsledkovkou') - - # má OneToOneField s: - # RocnikNode - - def __str__(self): - return '{} ({}/{})'.format(self.rocnik, self.prvni_rok, self.prvni_rok+1) - - # Ročník v římských číslech - def roman(self): - return roman(int(self.rocnik)) - - def verejne(self): - return len(self.verejna_cisla()) > 0 - verejne.boolean = True - verejne.short_description = 'Veřejný (jen dle čísel)' - - def neverejna_cisla(self): - vc = [c for c in self.cisla.all() if not c.verejne()] - vc.sort(key=lambda c: c.poradi) - return vc - - def verejna_cisla(self): - vc = [c for c in self.cisla.all() if c.verejne()] - vc.sort(key=lambda c: c.poradi) - return vc - - def posledni_verejne_cislo(self): - vc = self.verejna_cisla() - return vc[-1] if vc else None - - def verejne_vysledkovky_cisla(self): - vc = list(self.cisla.filter(deadline_v_cisle__verejna_vysledkovka=True).distinct()) - vc.sort(key=lambda c: c.poradi) - return vc - - def posledni_zverejnena_vysledkovka_cislo(self): - vc = self.verejne_vysledkovky_cisla() - return vc[-1] if vc else None - - def druhy_rok(self): - return self.prvni_rok + 1 - - def verejne_url(self): - return reverse('seminar_rocnik', kwargs={'rocnik': self.rocnik}) - - @classmethod - def cached_rocnik(cls, r_id): - name = 'rocnik_%s' % (r_id, ) - c = cache.get(name) - if c is None: - c = cls.objects.get(id=r_id) - cache.set(name, c, 300) - return c - - def save(self, *args, **kwargs): - super().save(*args, **kwargs) - # *Node.save() aktualizuje název *Nodu. - try: - self.rocniknode.save() - except ObjectDoesNotExist: - # Neexistující *Node nemá smysl aktualizovat. - pass - -def cislo_pdf_filename(self, filename): - rocnik = str(self.rocnik.rocnik) - return pathlib.Path('cislo', 'pdf', rocnik, '{}-{}.pdf'.format(rocnik, self.poradi)) - -def cislo_png_filename(self, filename): - rocnik = str(self.rocnik.rocnik) - return pathlib.Path('cislo', 'png', rocnik, '{}-{}.png'.format(rocnik, self.poradi)) - -@reversion.register(ignore_duplicates=True) -class Cislo(SeminarModelBase): - - class Meta: - db_table = 'seminar_cisla' - verbose_name = 'Číslo' - verbose_name_plural = 'Čísla' - ordering = ['-rocnik__rocnik', '-poradi'] - managed = False - - # Interní ID - id = models.AutoField(primary_key = True) - - rocnik = models.ForeignKey(Rocnik, verbose_name='ročník', related_name='cisla_old', - db_index=True,on_delete=models.PROTECT) - - poradi = models.CharField('název čísla', max_length=32, db_index=True, - help_text='Většinou jen "1", vyjímečně "7-8", lexikograficky určuje pořadí v ročníku!') - - datum_vydani = models.DateField('datum vydání', blank=True, null=True, - help_text='Datum vydání finální verze') - - verejne_db = models.BooleanField('číslo zveřejněno', - db_column='verejne', default=False) - - poznamka = models.TextField('neveřejná poznámka', blank=True, - help_text='Neveřejná poznámka k číslu (plain text)') - - pdf = models.FileField('pdf', upload_to=cislo_pdf_filename, null=True, blank=True, - help_text='PDF čísla, které si mohou řešitelé stáhnout', storage=OverwriteStorage()) - - titulka_nahled = models.ImageField('Obrázek titulní strany', upload_to=cislo_png_filename, null=True, blank=True, - help_text='Obrázek titulní strany, generuje se automaticky') - - # má OneToOneField s: - # CisloNode - - def kod(self): - return '%s.%s' % (self.rocnik.rocnik, self.poradi) - kod.short_description = 'Kód čísla' - - def __str__(self): - # Potenciální DB HOG, pokud by se ročník necachoval - r = Rocnik.cached_rocnik(self.rocnik_id) - return '{}.{}'.format(r.rocnik, self.poradi) - - def verejne(self): - return self.verejne_db - verejne.boolean = True - - def verejne_url(self): - return reverse('seminar_cislo', kwargs={'rocnik': self.rocnik.rocnik, 'cislo': self.poradi}) - - def absolute_url(self): - return "https://" + str(get_current_site(None)) + self.verejne_url() - - def nasledujici(self): - "Vrací None, pokud je toto poslední" - return self.relativni_v_rocniku(1) - - def predchozi(self): - "Vrací None, pokud je toto první" - return self.relativni_v_rocniku(-1) - - def relativni_v_rocniku(self, rel_index): - "Číslo o `index` dále v ročníku. None pokud neexistuje." - cs = self.rocnik.cisla.order_by('poradi').all() - i = list(cs).index(self) + rel_index - if (i < 0) or (i >= len(cs)): - return None - return cs[i] - - def vygeneruj_nahled(self): - VYSKA = 594 - sirka = int(VYSKA*210/297) - if not self.pdf: - return - - - # Pokud obrázek neexistuje nebo není aktuální, vytvoř jej - if not self.titulka_nahled or os.path.getmtime(self.titulka_nahled.path) < os.path.getmtime(self.pdf.path): - png_filename = pathlib.Path(tempfile.mkdtemp(), 'nahled.png') - - subprocess.run([ - "gs", - "-sstdout=%stderr", - "-dSAFER", - "-dNOPAUSE", - "-dBATCH", - "-dNOPROMPT", - "-sDEVICE=png16m", - "-r300x300", - "-dFirstPage=1d", - "-dLastPage=1d", - "-sOutputFile=" + str(png_filename), - "-f%s" % self.pdf.path - ], - check=True, - capture_output=True - ) - - with open(png_filename,'rb') as f: - self.titulka_nahled.save('',f,True) - - png_filename.unlink() - png_filename.parent.rmdir() - - - - @classmethod - def get(cls, rocnik, cislo): - try: - r = Rocnik.objects.get(rocnik=rocnik) - c = r.cisla.get(poradi=cislo) - except ObjectDoesNotExist: - return None - return c - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.__original_verejne = self.verejne_db - - def posli_cislo_mailem(self): - # parametry e-mailu - odkaz = self.absolute_url() - - poslat_z_mailu = 'zadani@mam.mff.cuni.cz' - predmet = 'Vyšlo číslo {}'.format(self.kod()) - # TODO Možná nechceme všem psát „Ahoj“, např. příjemcům… - text_mailu = 'Ahoj,\n' \ - 'na adrese {} najdete nejnovější číslo.\n' \ - 'Vaše M&M\n'.format(odkaz) - - predmet_prvni = 'Právě vyšlo 1. číslo M&M, pomoz nám ho poslat dál!' - text_mailu_prvni = 'Milý řešiteli,\n'\ - 'právě jsme na našem webu zveřejnili první číslo {}. ročníku, najdeš ho na tomto odkazu: {}.\n\n'\ - 'Doufáme, že tě M&M baví, a byli bychom rádi, kdyby mohlo dělat radost i dalším středoškolákům. Máme na tebe proto jednu prosbu. Sdílej prosím odkaz alespoň s jedním svým kamarádem, který by mohl mít o řešení M&M zájem. Je to pro nás moc důležité a velmi nám tím pomůžeš. Díky!\n\n'\ - 'Organizátoři M&M\n'.format(self.rocnik.rocnik, odkaz) - - predmet_resitel = predmet_prvni if self.poradi == "1" else predmet - text_mailu_resitel = text_mailu_prvni if self.poradi == "1" else text_mailu - - - # Prijemci e-mailu - resitele_vsichni = aktivniResitele(self).filter(zasilat_cislo_emailem=True) - - def posli(subject, text, resitele): - emaily = map(lambda resitel: resitel.osoba.email, resitele) - - email = EmailMessage( - subject=subject, - body=text, - from_email=poslat_z_mailu, - bcc=list(emaily) - #bcc = příjemci skryté kopie - ) - - email.send() - - paticka = "---\nK odběru těchto e-mailů jste se přihlásili na stránkách https://mam.matfyz.cz. Z odběru se lze odhlásit na https://mam.matfyz.cz/resitel/osobni-udaje/" - - posli(predmet_resitel, text_mailu_resitel + paticka, resitele_vsichni.filter(zasilat_cislo_papirove=False)) - posli(predmet_resitel, text_mailu_resitel + 'P. S. Brzy budeme též rozesílat papírovou verzi čísla. Připomínáme, že pokud papírovou verzi čísla nevyužijete, můžete v https://mam.mff.cuni.cz/resitel/osobni-udaje/ zaškrtnout, abychom vám ji neposílali. Čísla vždy můžete nalézt v našem archivu a dál vám budou chodit e-mailem. Děkujeme.\n' + paticka, - resitele_vsichni.filter(zasilat_cislo_papirove=True)) - - paticka_prijemce = "---\nPokud tyto e-maily nechcete nadále dostávat, prosíme, ozvěte se nám na mam@matfyz.cz." - posli(predmet, text_mailu + paticka_prijemce, Prijemce.objects.filter(zasilat_cislo_emailem=True)) - - def save(self, *args, **kwargs): - super().save(*args, **kwargs) - self.vygeneruj_nahled() - # Při zveřejnění pošle mail - if self.verejne_db and not self.__original_verejne: - self.posli_cislo_mailem() - # *Node.save() aktualizuje název *Nodu. - try: - self.cislonode.save() - 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 - CisloNode.objects.create(cislo=self) - - def zlomovy_deadline_pro_papirove_cislo(self): - prvni_deadline = Deadline.objects.filter(Q(typ=Deadline.TYP_PRVNI) | Q(typ=Deadline.TYP_PRVNI_A_SOUS), cislo=self).first() - if prvni_deadline is None: - posledni_deadline = self.posledni_deadline - if posledni_deadline is None: - # TODO promyslet, co se má stát tady - return Deadline.objects.filter(Q(cislo__poradi__lt=self.poradi, cislo__rocnik=self.rocnik) | Q(cislo__rocnik__rocnik__lt=self.rocnik.rocnik)).order_by("deadline").last() - return posledni_deadline - return prvni_deadline - - @property - def posledni_deadline(self): - return self.deadline_v_cisle.all().order_by("deadline").last() - -class Deadline(SeminarModelBase): - class Meta: - db_table = 'seminar_deadliny' - verbose_name = 'Deadline' - verbose_name_plural = 'Deadliny' - ordering = ['deadline'] - managed = False - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.__original_verejna_vysledkovka = self.verejna_vysledkovka - - id = models.AutoField(primary_key=True) - - # V ročníku < 26 nastaveno na datetime.datetime.combine(datetime.date(1994 + cislo.rocnik.rocnik, 6, int(cislo.poradi[0])), datetime.time.min) - deadline = models.DateTimeField(blank=False, default=timezone.make_aware(datetime.datetime.combine(timezone.now(), datetime.time.max))) - - cislo = models.ForeignKey(Cislo, verbose_name='deadline v čísle', - related_name='deadline_v_cisle_old', blank=False, - on_delete=models.CASCADE) - - TYP_CISLA = 'cisla' - TYP_PRVNI_A_SOUS = 'prvniasous' - TYP_PRVNI = 'prvni' - TYP_SOUS = 'sous' - TYP_CHOICES = [ - (TYP_CISLA, 'Deadline celého čísla'), - (TYP_PRVNI, 'První deadline'), - (TYP_PRVNI_A_SOUS, 'Sousový a první deadline'), - (TYP_SOUS, 'Sousový deadline'), - ] - CHOICES_MAP = dict(TYP_CHOICES) - typ = models.CharField('typ deadlinu', max_length=32, - choices=TYP_CHOICES, blank=False) - - verejna_vysledkovka = models.BooleanField('veřejná výsledkovka', - db_column='verejna_vysledkovka', - default=False) - - def __str__(self): - return self.CHOICES_MAP[self.typ] + " " + str(self.cislo) - - def save(self, *args, **kwargs): - super().save(*args, **kwargs) - if self.verejna_vysledkovka and not self.__original_verejna_vysledkovka: - self.vygeneruj_vysledkovku() - if not self.verejna_vysledkovka and hasattr(self, "vysledkovka_v_deadlinu"): - self.vysledkovka_v_deadlinu.delete() - - def vygeneruj_vysledkovku(self): - from vysledkovky.utils import VysledkovkaCisla - if hasattr(self, "vysledkovka_v_deadlinu"): - self.vysledkovka_v_deadlinu.delete() - vysledkovka = VysledkovkaCisla(self.cislo, jen_verejne=True, do_deadlinu=self) - if len(vysledkovka.radky_vysledkovky) != 0: - ZmrazenaVysledkovka.objects.create( - deadline=self, - html=render_to_string( - "vysledkovky/vysledkovka_cisla.html", - context={"vysledkovka": vysledkovka, "oznaceni_vysledkovky": self.id} - ) - ) - - -class ZmrazenaVysledkovka(SeminarModelBase): - class Meta: - db_table = 'seminar_vysledkovky' - verbose_name = 'Zmražená výsledkovka' - verbose_name_plural = 'Zmražené výsledkovky' - managed = False - - deadline = models.OneToOneField( - Deadline, - on_delete=models.CASCADE, - primary_key=True, - related_name="vysledkovka_v_deadlinu_old" - ) - - html = models.TextField(null=False, blank=False) - -class Problemy_Opravovatele(SeminarModelBase): - """Jen vazebná tabulka pro opravovatele. - - Ona stejně existovala, při přesunu mezi aplikacemi jen potřebujeme zajistit nepřejmenování DB tabulky. - Proto taky nepotřebuje žádná specifika, ze :py:class:SeminarModelBase: dědí ze zvyku než že by to k něčemu kdy měo být. - """ - class Meta: - db_table = 'seminar_problemy_opravovatele' - managed = False - - id = models.AutoField(primary_key = True) - - problem = models.ForeignKey('Problem', on_delete=models.CASCADE, related_name='awawa1_old') - organizator = models.ForeignKey(Organizator, on_delete=models.CASCADE, related_name='awawa2_old') - -@reversion.register(ignore_duplicates=True) -# Pozor na následující řádek. *Nekrmit, asi kouše!* -class Problem(SeminarModelBase,PolymorphicModel): - - class Meta: - # Není abstraktní, protože se na něj jinak nedají dělat ForeignKeys. - # TODO: Udělat to polymorfní (pomocí django-polymorphic), abychom dostali - # po těch vazbách přímo tu úlohu/témátko vč. fieldů, které nejsou součástí - # modelu Problem? - - #abstract = True - db_table = 'seminar_problemy' - verbose_name = 'Problém' - verbose_name_plural = 'Problémy' - ordering = ['nazev'] - managed = False - - # Interní ID - id = models.AutoField(primary_key = True) - - # Název - nazev = models.CharField('název', max_length=256) # Zveřejnitelný na stránky - - # Problém má podproblémy - nadproblem = models.ForeignKey('self', verbose_name='nadřazený problém', - related_name='podproblem_old', null=True, blank=True, - on_delete=models.SET_NULL) - - STAV_NAVRH = 'navrh' - STAV_ZADANY = 'zadany' - STAV_VYRESENY = 'vyreseny' - STAV_SMAZANY = 'smazany' - STAV_CHOICES = [ - (STAV_NAVRH, 'Návrh'), - (STAV_ZADANY, 'Zadaný'), - (STAV_VYRESENY, 'Vyřešený'), - (STAV_SMAZANY, 'Smazaný'), - ] - stav = models.CharField('stav problému', max_length=32, choices=STAV_CHOICES, blank=False, default=STAV_NAVRH) - # Téma je taky Problém, takže má stavy, "zadané" témátko je aktuálně otevřené a dá se k němu něco poslat (řešení nebo článek) - - zamereni = TaggableManager(verbose_name='zaměření', related_name='zamereni_old', - help_text='Zaměření M/F/I/O problému, příp. další tagy', blank=True) - - poznamka = models.TextField('org poznámky (HTML)', blank=True, - help_text='Neveřejný návrh úlohy, návrh řešení, text zadání, poznámky ...') - - autor = models.ForeignKey(Organizator, verbose_name='autor problému', - related_name='autor_problemu_%(class)s_old', null=True, blank=True, - on_delete=models.SET_NULL) - - garant = models.ForeignKey(Organizator, verbose_name='garant zadaného problému', - related_name='garant_problemu_%(class)s_old', null=True, blank=True, - on_delete=models.SET_NULL) - - opravovatele = models.ManyToManyField(Organizator, verbose_name='opravovatelé', - blank=True, related_name='opravovatele_%(class)s_old', through=Problemy_Opravovatele) - - kod = models.CharField('lokální kód', max_length=32, blank=True, default='', - help_text='Číslo/kód úlohy v čísle nebo kód tématu/článku/seriálu v ročníku') - - - vytvoreno = models.DateTimeField('vytvořeno', default=timezone.now, blank=True, editable=False) - - - def __str__(self): - return self.nazev - - # Implicitini implementace, jednotlivé dědící třídy si přepíšou - @cached_property - def kod_v_rocniku(self): - if self.stav == Problem.STAV_ZADANY or self.stav == Problem.STAV_VYRESENY: - if self.nadproblem: - return self.nadproblem.kod_v_rocniku+".{}".format(self.kod) - return str(self.kod) - logger.warning(f"K problému {self} byl vyžadován kód v ročníku, i když není zadaný ani vyřešený.") - return f'' - -# def verejne(self): -# # aktuálně podle stavu problému -# # FIXME pro některé problémy možná chceme override -# # FIXME vrací veřejnost čistě problému, nezávisle na čísle, ve kterém je. -# # Je to tak správně? Podle aktuální představy ano. -# stav_verejny = False -# if self.stav == 'zadany' or self.stav == 'vyreseny': -# stav_verejny = True -# print("stav_verejny: {}".format(stav_verejny)) -# -# cislo_verejne = False -# cislonode = self.cislo_node() -# if cislonode is None: -# # problém nemá vlastní node, veřejnost posuzujeme jen podle stavu -# print("empty node") -# return stav_verejny -# else: -# cislo_zadani = cislonode.cislo -# if (cislo_zadani and cislo_zadani.verejne()): -# print("cislo: {}".format(cislo_zadani)) -# cislo_verejne = True -# print("stav_verejny: {}".format(stav_verejny)) -# print("cislo_verejne: {}".format(cislo_verejne)) -# return (stav_verejny and cislo_verejne) -# verejne.boolean = True - - def verejne_url(self): - return reverse('seminar_problem', kwargs={'pk': self.id}) - - def admin_url(self): - return reverse('admin:seminar_problem_change', args=(self.id, )) - - @cached_property - def hlavni_problem(self): - """ Pro daný problém vrátí jeho nejvyšší nadproblém.""" - problem = self - while not (problem.nadproblem is None): - problem = problem.nadproblem - return problem - -# FIXME - k úloze - def body_v_zavorce(self): - """Vrať string s body v závorce jsou-li u problému vyplněné, jinak '' - - Je-li desetinná část nulová, nezobrazuj ji. - """ - pocet_bodu = None - if self.body: - b = self.body - pocet_bodu = int(b) if int(b) == b else b - return "({}\u2009b)".format(pocet_bodu) if self.body else "" - -class Tema(Problem): - class Meta: - db_table = 'seminar_temata' - verbose_name = 'Téma' - verbose_name_plural = 'Témata' - managed = False - - TEMA_TEMA = 'tema' - TEMA_SERIAL = 'serial' - TEMA_CHOICES = [ - (TEMA_TEMA, 'Téma'), - (TEMA_SERIAL, 'Seriál'), - ] - tema_typ = models.CharField('Typ tématu', max_length=16, choices=TEMA_CHOICES, - blank=False, default=TEMA_TEMA) - - rocnik = models.ForeignKey(Rocnik, verbose_name='ročník',related_name='temata_old',blank=True, null=True, - on_delete=models.PROTECT) - - abstrakt = models.TextField('Abstrakt na rozcestník', blank=True) - obrazek = models.ImageField('Obrázek na rozcestník', null=True, blank=True) - - @cached_property - def kod_v_rocniku(self): - if self.stav == Problem.STAV_ZADANY or self.stav == Problem.STAV_VYRESENY: - if self.nadproblem: - return self.nadproblem.kod_v_rocniku+".t{}".format(self.kod) - return 't'+self.kod - logger.warning(f"K problému {self} byl vyžadován kód v ročníku, i když není zadaný ani vyřešený.") - return f'' - - def save(self, *args, **kwargs): - super().save(*args, **kwargs) - # *Node.save() aktualizuje název *Nodu. - for tvcn in self.temavcislenode_set.all(): - tvcn.save() - - def cislo_node(self): - tema_node_set = self.temavcislenode_set.all() - tema_cisla_vyskyt = [] - from seminar.models.treenode import CisloNode - for tn in tema_node_set: - tema_cisla_vyskyt.append( - treelib.get_upper_node_of_type(tn, CisloNode).cislo) - tema_cisla_vyskyt.sort(key=lambda x:x.datum_vydani) - prvni_zadani = tema_cisla_vyskyt[0] - return prvni_zadani.cislonode - -class Clanek(Problem): - class Meta: - db_table = 'seminar_clanky' - verbose_name = 'Článek' - verbose_name_plural = 'Články' - managed = False - - cislo = models.ForeignKey(Cislo, blank=True, null=True, on_delete=models.PROTECT, - verbose_name='číslo vydání', related_name='vydane_clanky_old') - - strana = models.PositiveIntegerField(verbose_name="první strana", blank=True, null=True) - - @cached_property - def kod_v_rocniku(self): - if self.stav == Problem.STAV_ZADANY or self.stav == Problem.STAV_VYRESENY: -# Nemělo by být potřeba -# if self.nadproblem: -# return self.nadproblem.kod_v_rocniku+".c{}".format(self.kod) - return "c" + self.kod - logger.warning(f"K problému {self} byl vyžadován kód v ročníku, i když není zadaný ani vyřešený.") - return f'' - - def node(self): - return None - - -class Uloha(Problem): - class Meta: - db_table = 'seminar_ulohy' - verbose_name = 'Úloha' - verbose_name_plural = 'Úlohy' - managed = False - - cislo_zadani = models.ForeignKey(Cislo, verbose_name='číslo zadání', blank=True, - null=True, related_name='zadane_ulohy_old', on_delete=models.PROTECT) - - cislo_deadline = models.ForeignKey(Cislo, verbose_name='číslo deadlinu', blank=True, - null=True, related_name='deadlinove_ulohy_old', on_delete=models.PROTECT) - - cislo_reseni = models.ForeignKey(Cislo, verbose_name='číslo řešení', blank=True, - null=True, related_name='resene_ulohy_old', - help_text='Číslo s řešením úlohy, jen pro úlohy', - on_delete=models.PROTECT) - - max_body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='maximum bodů', - blank=True, null=True) - - # má OneToOneField s: - # UlohaZadaniNode - # UlohaVzorakNode - - @cached_property - def kod_v_rocniku(self): - if self.stav == Problem.STAV_ZADANY or self.stav == Problem.STAV_VYRESENY: - return f"{self.cislo_zadani.poradi}.{self.kod}" - logger.warning(f"K problému {self} byl vyžadován kód v ročníku, i když není zadaný ani vyřešený.") - return f'' - - def save(self, *args, **kwargs): - super().save(*args, **kwargs) - # *Node.save() aktualizuje název *Nodu. - try: - self.ulohazadaninode.save() - except ObjectDoesNotExist: - # Neexistující *Node nemá smysl aktualizovat. - pass - try: - self.ulohavzoraknode.save() - except ObjectDoesNotExist: - # Neexistující *Node nemá smysl aktualizovat. - pass - - def cislo_node(self): - zadani_node = self.ulohazadaninode - from seminar.models.treenode import CisloNode - return treelib.get_upper_node_of_type(zadani_node, CisloNode) - - -def aux_generate_filename(self, filename): - """Pomocná funkce generující ošetřený název souboru v adresáři s datem""" - clean = get_valid_filename( - unidecode(filename.replace('/', '-').replace('\0', '')) - ) - datedir = timezone.now().strftime('%Y-%m') - fname = "{}/{}".format( - timezone.now().strftime('%Y-%m-%d-%H:%M'), - clean) - return os.path.join(datedir, fname) - - -class Pohadka(SeminarModelBase): - """Kus pohádky před/za úlohou v čísle""" - - class Meta: - db_table = 'seminar_pohadky' - verbose_name = 'Pohádka' - verbose_name_plural = 'Pohádky' - ordering = ['vytvoreno'] - managed = False - - # Interní ID - id = models.AutoField(primary_key=True) - - autor = models.ForeignKey( - Organizator, - verbose_name="Autor pohádky", - - # Při nahrávání z TeXu není vyplnění vyžadováno, v adminu je - null=True, - blank=False, - on_delete=models.SET_NULL, - related_name='awawa3_old', - ) - - vytvoreno = models.DateTimeField( - 'Vytvořeno', - default=timezone.now, - blank=True, - editable=False - ) - - # má OneToOneField s: - # PohadkaNode - - def __str__(self): - uryvek = self.text if len(self.text) < 50 else self.text[:(50-3)]+"..." - return uryvek - - def save(self, *args, **kwargs): - super().save(*args, **kwargs) - # *Node.save() aktualizuje název *Nodu. - try: - self.pohadkanode.save() - except ObjectDoesNotExist: - # Neexistující *Node nemá smysl aktualizovat. - pass diff --git a/soustredeni/migrations/0005_tvorba_relink.py b/soustredeni/migrations/0005_tvorba_relink.py index 7786449f..1a3a09bf 100644 --- a/soustredeni/migrations/0005_tvorba_relink.py +++ b/soustredeni/migrations/0005_tvorba_relink.py @@ -12,11 +12,14 @@ class Migration(migrations.Migration): ] operations = [ - migrations.AlterField( - model_name='konfera', - name='problem_ptr', - field=models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='tvorba.problem'), - ), + ## Konferu zmigrujeme jinak, kvůli jí nejde přepsat někde ve stavu `bases`. + ## Proto si ji unmanagujeme a vyrobíme celou znovu, to by nemělo vadit (zvlášť když t.č. v DB žádná instance Konfery není). + ## (Šlo by `SeparateStateAndData`, což v principu děláme taky ale ty migrace jsou lehce čitelnější a o poznání konzistentnější.) + #migrations.AlterField( + # model_name='konfera', + # name='problem_ptr', + # field=models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='tvorba.problem'), + #), migrations.AlterField( model_name='soustredeni', name='rocnik', diff --git a/soustredeni/migrations/0006_tvorba_relink2.py b/soustredeni/migrations/0006_tvorba_relink2.py new file mode 100644 index 00000000..0fe70b8c --- /dev/null +++ b/soustredeni/migrations/0006_tvorba_relink2.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.16 on 2024-10-30 19:37 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('soustredeni', '0005_tvorba_relink'), + ] + + operations = [ + migrations.AlterModelOptions( + name='konfera', + options={'managed': False, 'verbose_name': 'Konfera', 'verbose_name_plural': 'Konfery'}, + ), + ] diff --git a/soustredeni/migrations/0007_tvorba_relink3.py b/soustredeni/migrations/0007_tvorba_relink3.py new file mode 100644 index 00000000..48a84686 --- /dev/null +++ b/soustredeni/migrations/0007_tvorba_relink3.py @@ -0,0 +1,15 @@ +# Generated by Django 4.2.16 on 2024-10-30 19:38 + +from django.db import migrations + +class Migration(migrations.Migration): + + dependencies = [ + ('soustredeni', '0006_tvorba_relink2'), + ] + + operations = [ + migrations.DeleteModel( + name='Konfera', + ), + ] diff --git a/soustredeni/migrations/0008_tvorba_relink4.py b/soustredeni/migrations/0008_tvorba_relink4.py new file mode 100644 index 00000000..d2792c8e --- /dev/null +++ b/soustredeni/migrations/0008_tvorba_relink4.py @@ -0,0 +1,34 @@ +# Generated by Django 4.2.16 on 2024-10-30 19:45 + +from django.db import migrations,models +import django.db.models.deletion +import soustredeni.models + +class Migration(migrations.Migration): + + dependencies = [ + ('soustredeni', '0007_tvorba_relink3'), + ] + + operations = [ + migrations.CreateModel( + name='Konfera', + fields=[ + ('problem_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='tvorba.problem')), + ('anotace', models.TextField(blank=True, help_text='Popis, o čem bude konfera.', verbose_name='anotace')), + ('abstrakt', models.TextField(blank=True, help_text='Abstrakt konfery tak, jak byl uveden ve sborníku', verbose_name='abstrakt')), + ('typ_prezentace', models.CharField(choices=[('veletrh', 'Veletrh (postery)'), ('prezentace', 'Prezentace (přednáška)')], default='veletrh', max_length=16, verbose_name='typ prezentace')), + ('prezentace', models.FileField(blank=True, help_text='Prezentace nebo fotka posteru', upload_to=soustredeni.models.generate_filename_konfera, verbose_name='prezentace')), + ('materialy', models.FileField(blank=True, help_text='Další materiály ke konfeře zabalené do jednoho souboru', upload_to=soustredeni.models.generate_filename_konfera, verbose_name='materialy')), + ('soustredeni', models.ForeignKey(to='soustredeni.soustredeni', verbose_name='soustředění', on_delete=models.SET_NULL, null=True, related_name='konfery')), + ('ucastnici', models.ManyToManyField(help_text='Seznam účastníků konfery', through='soustredeni.Konfery_Ucastnici', to='personalni.resitel', verbose_name='účastníci konfery')), + ], + options={ + 'verbose_name': 'Konfera', + 'verbose_name_plural': 'Konfery', + 'db_table': 'seminar_konfera', + 'managed': False, + }, + bases=('tvorba.problem',), + ), + ] diff --git a/soustredeni/migrations/0009_tvorba_relink5.py b/soustredeni/migrations/0009_tvorba_relink5.py new file mode 100644 index 00000000..cfe5c97b --- /dev/null +++ b/soustredeni/migrations/0009_tvorba_relink5.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.16 on 2024-10-30 20:03 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('soustredeni', '0008_tvorba_relink4'), + ] + + operations = [ + migrations.AlterModelOptions( + name='konfera', + options={'verbose_name': 'Konfera', 'verbose_name_plural': 'Konfery'}, + ), + ] diff --git a/soustredeni/migrations/0010_tvorba_post.py b/soustredeni/migrations/0010_tvorba_post.py new file mode 100644 index 00000000..1a700298 --- /dev/null +++ b/soustredeni/migrations/0010_tvorba_post.py @@ -0,0 +1,14 @@ +# Generated by Django 4.2.16 on 2024-10-30 21:35 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('soustredeni', '0009_tvorba_relink5'), + ('tvorba', '0003_tvorba_post'), + ] + + operations = [ + ] diff --git a/tvorba/migrations/0002_tvorba_manage.py b/tvorba/migrations/0002_tvorba_manage.py new file mode 100644 index 00000000..593d3263 --- /dev/null +++ b/tvorba/migrations/0002_tvorba_manage.py @@ -0,0 +1,54 @@ +# Generated by Django 4.2.16 on 2024-10-30 21:29 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('tvorba', '0001_tvorba_create'), + ('seminar', '0138_tvorba_delete'), + ] + + operations = [ + migrations.AlterModelOptions( + name='cislo', + options={'ordering': ['-rocnik__rocnik', '-poradi'], 'verbose_name': 'Číslo', 'verbose_name_plural': 'Čísla'}, + ), + migrations.AlterModelOptions( + name='clanek', + options={'verbose_name': 'Článek', 'verbose_name_plural': 'Články'}, + ), + migrations.AlterModelOptions( + name='deadline', + options={'ordering': ['deadline'], 'verbose_name': 'Deadline', 'verbose_name_plural': 'Deadliny'}, + ), + migrations.AlterModelOptions( + name='pohadka', + options={'ordering': ['vytvoreno'], 'verbose_name': 'Pohádka', 'verbose_name_plural': 'Pohádky'}, + ), + migrations.AlterModelOptions( + name='problem', + options={'ordering': ['nazev'], 'verbose_name': 'Problém', 'verbose_name_plural': 'Problémy'}, + ), + migrations.AlterModelOptions( + name='problemy_opravovatele', + options={}, + ), + migrations.AlterModelOptions( + name='rocnik', + options={'ordering': ['-rocnik'], 'verbose_name': 'Ročník', 'verbose_name_plural': 'Ročníky'}, + ), + migrations.AlterModelOptions( + name='tema', + options={'verbose_name': 'Téma', 'verbose_name_plural': 'Témata'}, + ), + migrations.AlterModelOptions( + name='uloha', + options={'verbose_name': 'Úloha', 'verbose_name_plural': 'Úlohy'}, + ), + migrations.AlterModelOptions( + name='zmrazenavysledkovka', + options={'verbose_name': 'Zmražená výsledkovka', 'verbose_name_plural': 'Zmražené výsledkovky'}, + ), + ] diff --git a/tvorba/migrations/0003_tvorba_post.py b/tvorba/migrations/0003_tvorba_post.py new file mode 100644 index 00000000..16e6d203 --- /dev/null +++ b/tvorba/migrations/0003_tvorba_post.py @@ -0,0 +1,13 @@ +# Generated by Django 4.2.16 on 2024-10-30 21:34 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('tvorba', '0002_tvorba_manage'), + ] + + operations = [ + ] diff --git a/tvorba/models.py b/tvorba/models.py index 40d46797..5abbe748 100644 --- a/tvorba/models.py +++ b/tvorba/models.py @@ -31,7 +31,8 @@ from polymorphic.models import PolymorphicModel from django.core.mail import EmailMessage -from seminar.models import SeminarModelBase, OverwriteStorage +from seminar.models.base import SeminarModelBase +from seminar.models.tvorba import OverwriteStorage from personalni.models import Prijemce, Organizator logger = logging.getLogger(__name__) @@ -44,7 +45,6 @@ class Rocnik(SeminarModelBase): verbose_name = 'Ročník' verbose_name_plural = 'Ročníky' ordering = ['-rocnik'] - managed = False # Interní ID id = models.AutoField(primary_key = True) @@ -132,7 +132,6 @@ class Cislo(SeminarModelBase): verbose_name = 'Číslo' verbose_name_plural = 'Čísla' ordering = ['-rocnik__rocnik', '-poradi'] - managed = False # Interní ID id = models.AutoField(primary_key = True) @@ -321,7 +320,6 @@ class Deadline(SeminarModelBase): verbose_name = 'Deadline' verbose_name_plural = 'Deadliny' ordering = ['deadline'] - managed = False def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -384,7 +382,6 @@ class ZmrazenaVysledkovka(SeminarModelBase): db_table = 'seminar_vysledkovky' verbose_name = 'Zmražená výsledkovka' verbose_name_plural = 'Zmražené výsledkovky' - managed = False deadline = models.OneToOneField( Deadline, @@ -403,7 +400,6 @@ class Problemy_Opravovatele(SeminarModelBase): """ class Meta: db_table = 'seminar_problemy_opravovatele' - managed = False id = models.AutoField(primary_key = True) @@ -425,7 +421,6 @@ class Problem(SeminarModelBase,PolymorphicModel): verbose_name = 'Problém' verbose_name_plural = 'Problémy' ordering = ['nazev'] - managed = False # Interní ID id = models.AutoField(primary_key = True) @@ -543,7 +538,6 @@ class Tema(Problem): db_table = 'seminar_temata' verbose_name = 'Téma' verbose_name_plural = 'Témata' - managed = False TEMA_TEMA = 'tema' TEMA_SERIAL = 'serial' @@ -591,7 +585,6 @@ class Clanek(Problem): db_table = 'seminar_clanky' verbose_name = 'Článek' verbose_name_plural = 'Články' - managed = False cislo = models.ForeignKey(Cislo, blank=True, null=True, on_delete=models.PROTECT, verbose_name='číslo vydání', related_name='vydane_clanky') @@ -617,7 +610,6 @@ class Uloha(Problem): db_table = 'seminar_ulohy' verbose_name = 'Úloha' verbose_name_plural = 'Úlohy' - managed = False cislo_zadani = models.ForeignKey(Cislo, verbose_name='číslo zadání', blank=True, null=True, related_name='zadane_ulohy', on_delete=models.PROTECT) @@ -680,7 +672,6 @@ class Pohadka(SeminarModelBase): verbose_name = 'Pohádka' verbose_name_plural = 'Pohádky' ordering = ['vytvoreno'] - managed = False # Interní ID id = models.AutoField(primary_key=True) diff --git a/various/migrations/0006_tvorba_post.py b/various/migrations/0006_tvorba_post.py new file mode 100644 index 00000000..4b339a05 --- /dev/null +++ b/various/migrations/0006_tvorba_post.py @@ -0,0 +1,14 @@ +# Generated by Django 4.2.16 on 2024-10-30 21:35 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('various', '0005_tvorba_relink'), + ('tvorba', '0003_tvorba_post'), + ] + + operations = [ + ] From 26d37d96f7c4c755b80810057399445e15c7a918 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Thu, 31 Oct 2024 00:01:07 +0100 Subject: [PATCH 19/51] =?UTF-8?q?Frontendov=C3=A9=20n=C3=A1hrady=20semin?= =?UTF-8?q?=C3=A1=C5=99e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (některé netestované, smůla.) --- personalni/templates/personalni/profil/orgorozcestnik.html | 2 +- tvorba/admin.py | 3 ++- tvorba/models.py | 2 +- various/models.py | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/personalni/templates/personalni/profil/orgorozcestnik.html b/personalni/templates/personalni/profil/orgorozcestnik.html index 90d5867d..9d0bbdba 100644 --- a/personalni/templates/personalni/profil/orgorozcestnik.html +++ b/personalni/templates/personalni/profil/orgorozcestnik.html @@ -20,7 +20,7 @@

Tvorba čísla

    -
  • přidat téma
  • +
  • přidat téma
  • korektury
    • korekturování
    • diff --git a/tvorba/admin.py b/tvorba/admin.py index f090062b..f62d2576 100644 --- a/tvorba/admin.py +++ b/tvorba/admin.py @@ -1,6 +1,7 @@ from django.contrib import admin from django.forms import ModelForm from django.core.exceptions import ValidationError +from django.urls import reverse from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter from django.utils.safestring import mark_safe @@ -70,7 +71,7 @@ class CisloForm(ModelForm): ValidationError('Úloha %(uloha)s není zadaná ani vyřešená', params={'uloha': ch})) if errors: errors.append(ValidationError(mark_safe( - 'Pokud chceš učinit všechny problémy, co nejsou zadané ani vyřešené, zadanými a číslo zveřejnit, můžeš to udělat pomocí akce v seznamu čísel'))) + 'Pokud chceš učinit všechny problémy, co nejsou zadané ani vyřešené, zadanými a číslo zveřejnit, můžeš to udělat pomocí akce v seznamu čísel'))) if self.cleaned_data.get('datum_vydani') == None: self.add_error('datum_vydani','Číslo určené ke zveřejnění nemá nastavené datum vydání') diff --git a/tvorba/models.py b/tvorba/models.py index 5abbe748..d7290ae3 100644 --- a/tvorba/models.py +++ b/tvorba/models.py @@ -511,7 +511,7 @@ class Problem(SeminarModelBase,PolymorphicModel): return reverse('seminar_problem', kwargs={'pk': self.id}) def admin_url(self): - return reverse('admin:seminar_problem_change', args=(self.id, )) + return reverse('admin:tvorba_problem_change', args=(self.id, )) @cached_property def hlavni_problem(self): diff --git a/various/models.py b/various/models.py index e31b4e72..85ba4702 100644 --- a/various/models.py +++ b/various/models.py @@ -33,7 +33,7 @@ class Nastaveni(SingletonModel): return 'Nastavení semináře' def admin_url(self): - return reverse('admin:seminar_nastaveni_change', args=(self.id, )) + return reverse('admin:various_nastaveni_change', args=(self.id, )) def verejne(self): return False From ad9a496ceea67c1a3592f69c65078f6652d5d8e0 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Thu, 31 Oct 2024 00:03:00 +0100 Subject: [PATCH 20/51] =?UTF-8?q?Kus=20k=C3=B3du=20nen=C3=AD=20pot=C5=99eb?= =?UTF-8?q?a=20(a=20nav=C3=ADc=20obsahuje=20slovo=20semin=C3=A1=C5=99=20:-?= =?UTF-8?q?P)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tvorba/admin.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tvorba/admin.py b/tvorba/admin.py index f62d2576..0e873c29 100644 --- a/tvorba/admin.py +++ b/tvorba/admin.py @@ -60,9 +60,6 @@ class CisloForm(ModelForm): # if problem not in \ # (Problem.STAV_ZADANY, Problem.STAV_VYRESENY): # errors.append(ValidationError('Problém %s není zadaný ani vyřešený', code=problem)) - # if errors: - # errors.append(ValidationError(mark_safe('Pokud chceš učinit všechny problémy, co nejsou zadané ani vyřešené, zadanými a číslo zveřejnit, můžeš to udělat pomocí akce v seznamu čísel'))) - # raise ValidationError(errors) errors = [] for ch in Uloha.objects.filter(cislo_zadani=self.instance): From 8fd582d194599e681447a718bccd51dada10fa28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 1 Nov 2024 11:44:17 +0100 Subject: [PATCH 21/51] =?UTF-8?q?Dal=C5=A1=C3=AD=20=C4=8D=C3=A1ste=C4=8Dn?= =?UTF-8?q?=C3=A9=20=C5=99e=C5=A1en=C3=AD=20#1465=20(Podez=C5=99el=C3=A9?= =?UTF-8?q?=20`seminar`e).=20Z=C3=A1m=C4=9Brn=C4=9B=20se=20vyh=C3=BDb?= =?UTF-8?q?=C3=A1=20treenode.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/views/autocomplete.py | 18 ++-- mamweb/admin.py | 4 +- odevzdavatko/forms.py | 23 +++-- odevzdavatko/models.py | 16 ++-- odevzdavatko/views.py | 92 ++++++++++--------- personalni/views.py | 12 ++- soustredeni/models.py | 8 +- soustredeni/testutils.py | 4 +- tvorba/templatetags/deadliny.py | 22 ++--- tvorba/testutils.py | 56 +++++------ tvorba/utils.py | 2 +- tvorba/views/__init__.py | 55 ++++++----- tvorba/views/docasne.py | 14 +-- various/autentizace/views.py | 1 - .../commands/generate_thumbnails.py | 2 +- .../pregeneruj_zmrazene_vysledkovky.py | 4 +- various/management/commands/testdata.py | 4 +- various/testutils.py | 4 +- various/views/final.py | 27 +++--- vysledkovky/utils.py | 77 ++++++++-------- 20 files changed, 232 insertions(+), 213 deletions(-) diff --git a/api/views/autocomplete.py b/api/views/autocomplete.py index edc81ff7..006b7185 100644 --- a/api/views/autocomplete.py +++ b/api/views/autocomplete.py @@ -5,7 +5,9 @@ from dal import autocomplete from django.shortcuts import get_object_or_404 from django.db.models import Q -import seminar.models as m +from personalni.models import Skola, Resitel +from tvorba.models import Problem +from various.models import Nastaveni from .helpers import LoginRequiredAjaxMixin # TODO filosofie - zkratky, jak v databázi, tak ve vyhledávání (SPŠE, GASOŠ, Kpt., soukr) @@ -13,7 +15,7 @@ class SkolaAutocomplete(autocomplete.Select2QuerySetView): """ View k :mod:`dal.autocomplete` pro vyhledávání škol hlavně při registraci. """ def get_queryset(self): # Don't forget to filter out results depending on the visitor ! - qs = m.Skola.objects.all() + qs = Skola.objects.all() if self.q: words = self.q.split(' ') #TODO re split podle bileho znaku partq = Q() @@ -31,7 +33,7 @@ class SkolaAutocomplete(autocomplete.Select2QuerySetView): class ResitelAutocomplete(LoginRequiredAjaxMixin,autocomplete.Select2QuerySetView): """ View k :mod:`dal.autocomplete` pro vyhledávání řešitelů především v odevzdávátku. """ def get_queryset(self): - qs = m.Resitel.objects.all() + qs = Resitel.objects.all() if self.q: parts = self.q.split() query = Q() @@ -51,8 +53,8 @@ class PublicResitelAutocomplete(LoginRequiredAjaxMixin, autocomplete.Select2Quer především v odevzdávátku. """ def get_queryset(self): - letos = m.Nastaveni.get_solo().aktualni_rocnik - qs = m.Resitel.objects.filter( + letos = Nastaveni.get_solo().aktualni_rocnik + qs = Resitel.objects.filter( rok_maturity__gte=letos.druhy_rok() ).filter( prezdivka_resitele__isnull=False @@ -70,7 +72,7 @@ class PublicResitelAutocomplete(LoginRequiredAjaxMixin, autocomplete.Select2Quer class OdevzdatelnyProblemAutocomplete(autocomplete.Select2QuerySetView): """ View k :mod:`dal.autocomplete` pro vyhledávání problémů především v odevzdávátku. """ def get_queryset(self): - qs = m.Problem.objects.filter(stav=m.Problem.STAV_ZADANY) + qs = Problem.objects.filter(stav=Problem.STAV_ZADANY) if self.q: qs = qs.filter( Q(nazev__icontains=self.q)) @@ -87,12 +89,12 @@ class ProblemAutocomplete(autocomplete.Select2QuerySetView): """ View k :mod:`dal.autocomplete` pro vyhledávání problémů především v odevzdávátku. """ def get_queryset(self): # FIXME i starší úlohy - nastaveni = get_object_or_404(m.Nastaveni) + nastaveni = get_object_or_404(Nastaveni) rocnik = nastaveni.aktualni_rocnik temaQ = Q(Tema___rocnik = rocnik) ulohaQ = Q(Uloha___cislo_zadani__rocnik=rocnik) clanekQ = Q(Clanek___cislo__rocnik=rocnik) - qs = m.Problem.objects.filter(temaQ | ulohaQ | clanekQ).order_by("-stav", "nazev") + qs = Problem.objects.filter(temaQ | ulohaQ | clanekQ).order_by("-stav", "nazev") if self.q: qs = qs.filter( Q(nazev__icontains=self.q)) diff --git a/mamweb/admin.py b/mamweb/admin.py index 04d564cc..ba3d9648 100644 --- a/mamweb/admin.py +++ b/mamweb/admin.py @@ -43,7 +43,7 @@ def get_app_list(self, request, app_label=None): app_dict = self._build_app_dict(request, label=app_label) aplikace_nahore = [ - 'seminar', + 'tvorba', 'personalni', 'novinky', 'korektury', @@ -57,7 +57,7 @@ def get_app_list(self, request, app_label=None): # Sort the models alphabetically within each app. for app in app_list: - app['models'].sort(key=lambda x: locale.strxfrm('žž' + x['name'].lower()) if (x['name'].endswith("(Node)")) else locale.strxfrm(x['name'].lower())) + app['models'].sort(key=lambda x: locale.strxfrm(x['name'].lower())) return app_list diff --git a/odevzdavatko/forms.py b/odevzdavatko/forms.py index 0ba86654..1b5c43a3 100644 --- a/odevzdavatko/forms.py +++ b/odevzdavatko/forms.py @@ -5,7 +5,10 @@ from django.forms.models import inlineformset_factory from django.utils import timezone from personalni.models import Resitel -import seminar.models as m +from tvorba.models import Problem, Deadline +from various.models import Nastaveni + +from odevzdavatko.models import Reseni, PrilohaReseni, Hodnoceni import logging @@ -22,7 +25,7 @@ class DateInput(forms.DateInput): class PosliReseniForm(forms.Form): problem = forms.ModelMultipleChoiceField( - queryset=m.Problem.objects.all(), + queryset=Problem.objects.all(), label="Problémy", widget=autocomplete.ModelSelect2Multiple( url='autocomplete_problem', @@ -58,7 +61,7 @@ class PosliReseniForm(forms.Form): #cas_doruceni = models.DateTimeField('čas_doručení', default=timezone.now, blank=True) - forma = forms.ChoiceField(label="Forma řešení",choices = m.Reseni.FORMA_CHOICES) + forma = forms.ChoiceField(label="Forma řešení",choices = Reseni.FORMA_CHOICES) #forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False, # default=FORMA_EMAIL) @@ -69,7 +72,7 @@ class PosliReseniForm(forms.Form): class NahrajReseniForm(forms.ModelForm): class Meta: - model = m.Reseni + model = Reseni fields = ('problem', 'resitele') help_texts = {'problem':''} # Nezobrazovat help text ve formuláři @@ -109,11 +112,11 @@ class NahrajReseniForm(forms.ModelForm): def clean_problem(self): problem = self.cleaned_data.get('problem') for p in problem: - if p.stav != m.Problem.STAV_ZADANY: + if p.stav != Problem.STAV_ZADANY: raise forms.ValidationError("Problém " + str(p) + " již nelze řešit!") return problem -ReseniSPrilohamiFormSet = inlineformset_factory(m.Reseni,m.PrilohaReseni, +ReseniSPrilohamiFormSet = inlineformset_factory(Reseni, PrilohaReseni, form = NahrajReseniForm, fields = ('soubor','res_poznamka'), widgets = {'res_poznamka':forms.TextInput()}, @@ -125,7 +128,7 @@ ReseniSPrilohamiFormSet = inlineformset_factory(m.Reseni,m.PrilohaReseni, class JednoHodnoceniForm(forms.ModelForm): class Meta: - model = m.Hodnoceni + model = Hodnoceni fields = ('problem', 'body', 'deadline_body', 'feedback',) widgets = { 'problem': autocomplete.ModelSelect2( @@ -158,7 +161,7 @@ OhodnoceniReseniFormSet = formset_factory(JednoHodnoceniForm, class PoznamkaReseniForm(forms.ModelForm): class Meta: - model = m.Reseni + model = Reseni fields = ('poznamka',) # FIXME: Ideálně by mělo být součástí třídy níž, ale neumím to udělat @@ -198,7 +201,7 @@ class OdevzdavatkoTabulkaFiltrForm(forms.Form): from django.db.utils import OperationalError try: - aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik + aktualni_rocnik = Nastaveni.get_solo().aktualni_rocnik except OperationalError: # django.db.utils.OperationalError: no such table: seminar_nastaveni # Nemáme databázi, takže to selhalo. Pro jistotu vrátíme aspoň dvě možnosti, ať to nepadá dál @@ -214,7 +217,7 @@ class OdevzdavatkoTabulkaFiltrForm(forms.Form): result.append(("0001-01-01", f"Odjakživa")) - for deadline in m.Deadline.objects.filter( + for deadline in Deadline.objects.filter( deadline__lte=timezone.now(), cislo__rocnik=aktualni_rocnik ).order_by("deadline"): diff --git a/odevzdavatko/models.py b/odevzdavatko/models.py index a52f370f..600b9b48 100644 --- a/odevzdavatko/models.py +++ b/odevzdavatko/models.py @@ -9,7 +9,7 @@ from django.urls import reverse_lazy from django.utils import timezone from django.conf import settings -import tvorba.models as am +from tvorba.models import Problem, Deadline, Cislo, Uloha, aux_generate_filename from seminar.models import base as bm from odevzdavatko.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet @@ -29,7 +29,7 @@ class Reseni(bm.SeminarModelBase): id = models.AutoField(primary_key = True) # Ke každé dvojici řešní a problém existuje nanejvýš jedno hodnocení, doplnění vazby. - problem = models.ManyToManyField(am.Problem, verbose_name='problém', help_text='Problém', + problem = models.ManyToManyField(Problem, verbose_name='problém', help_text='Problém', through='Hodnoceni') resitele = models.ManyToManyField(Resitel, verbose_name='autoři řešení', @@ -79,7 +79,7 @@ class Reseni(bm.SeminarModelBase): # NOTE: Potenciální DB HOG (bez select_related) def deadline_reseni(self): - return am.Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first() + return Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first() ## Pravdepodobne uz nebude potreba: # def save(self, *args, **kwargs): @@ -101,16 +101,16 @@ class Hodnoceni(bm.SeminarModelBase): body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body', blank=True, null=True) - cislo_body = models.ForeignKey(am.Cislo, verbose_name='číslo pro body', + cislo_body = models.ForeignKey(Cislo, verbose_name='číslo pro body', related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT) # V ročníku < 26 nastaveno na deadline vygenerovaný pro původní cislo_body - deadline_body = models.ForeignKey(am.Deadline, verbose_name='deadline pro body', + deadline_body = models.ForeignKey(Deadline, verbose_name='deadline pro body', related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT) reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) - problem = models.ForeignKey(am.Problem, verbose_name='problém', + problem = models.ForeignKey(Problem, verbose_name='problém', related_name='hodnoceni', on_delete=models.PROTECT) feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)') @@ -166,7 +166,7 @@ class Hodnoceni(bm.SeminarModelBase): @property def body_neprepocitane_max(self): - if not isinstance(self.problem.get_real_instance(), am.Uloha): + if not isinstance(self.problem.get_real_instance(), Uloha): return None return self.problem.uloha.max_body @@ -176,7 +176,7 @@ class Hodnoceni(bm.SeminarModelBase): def generate_filename(self, filename): return os.path.join( settings.SEMINAR_RESENI_DIR, - am.aux_generate_filename(self, filename) + aux_generate_filename(self, filename) ) diff --git a/odevzdavatko/views.py b/odevzdavatko/views.py index cbe9019e..1a294c4e 100644 --- a/odevzdavatko/views.py +++ b/odevzdavatko/views.py @@ -17,10 +17,14 @@ from decimal import Decimal from itertools import groupby import logging -import seminar.models as m from . import forms as f from .forms import OdevzdavatkoTabulkaFiltrForm as FiltrForm +from .models import Hodnoceni, Reseni + +from personalni.models import Resitel, Osoba, Organizator +from tvorba.models import Problem, Deadline, Rocnik from tvorba.utils import resi_v_rocniku +from various.models import Nastaveni from various.views.pomocne import formularOKView logger = logging.getLogger(__name__) @@ -40,20 +44,20 @@ logger = logging.getLogger(__name__) class TabulkaOdevzdanychReseniView(ListView): template_name = 'odevzdavatko/tabulka.html' - model = m.Hodnoceni + model = Hodnoceni def inicializuj_osy_tabulky(self): """Vyrobí prvotní querysety pro sloupce a řádky, tj. seznam všech řešitelů a problémů""" # FIXME: jméno metody není vypovídající... # NOTE: Tenhle blok nemůže být přímo ve třídě, protože před vyrobením databáze neexistují ty objekty (?). TODO: Otestovat # TODO: Prefetches, Select related, ... - self.resitele = m.Resitel.objects.all() - self.problemy = m.Problem.objects.all() - self.reseni = m.Reseni.objects.all() + self.resitele = Resitel.objects.all() + self.problemy = Problem.objects.all() + self.reseni = Reseni.objects.all() - self.aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci + self.aktualni_rocnik = Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci if 'rocnik' in self.kwargs: - self.aktualni_rocnik = get_object_or_404(m.Rocnik, rocnik=self.kwargs['rocnik']) + self.aktualni_rocnik = get_object_or_404(Rocnik, rocnik=self.kwargs['rocnik']) form = FiltrForm(self.request.GET, rocnik=self.aktualni_rocnik) if form.is_valid(): @@ -86,14 +90,14 @@ class TabulkaOdevzdanychReseniView(ListView): self.resitele = self.resitele.filter(rok_maturity__gt=self.aktualni_rocnik.prvni_rok) if problemy == FiltrForm.PROBLEMY_MOJE: - org = m.Organizator.objects.get(osoba__user=self.request.user) + org = Organizator.objects.get(osoba__user=self.request.user) self.problemy = self.problemy.filter( Q(autor=org)|Q(garant=org)|Q(opravovatele=org), - Q(stav=m.Problem.STAV_ZADANY)|Q(stav=m.Problem.STAV_VYRESENY), + Q(stav=Problem.STAV_ZADANY)|Q(stav=Problem.STAV_VYRESENY), ) elif problemy == FiltrForm.PROBLEMY_LETOSNI: self.problemy = self.problemy.filter( - Q(stav=m.Problem.STAV_ZADANY)|Q(stav=m.Problem.STAV_VYRESENY), + Q(stav=Problem.STAV_ZADANY)|Q(stav=Problem.STAV_VYRESENY), ) #self.problemy = list(filter(lambda problem: problem.rocnik() == self.aktualni_rocnik, self.problemy)) # DB HOG? # FIXME: některé problémy nemají ročník.... # NOTE: Protože řešení odkazuje přímo na Problém a QuerySet na Hodnocení je nepolymorfní, musíme porovnávat taky s nepolymorfními Problémy. @@ -121,8 +125,8 @@ class TabulkaOdevzdanychReseniView(ListView): ctx = super().get_context_data(*args, **kwargs) ctx['problemy'] = self.problemy ctx['resitele'] = self.resitele - tabulka: dict[m.Problem, dict[m.Resitel, list[tuple[m.Reseni, m.Hodnoceni]]]] = dict() - soucty: dict[m.Problem, dict[m.Resitel, Decimal]] = dict() + tabulka: dict[Problem, dict[Resitel, list[tuple[Reseni, Hodnoceni]]]] = dict() + soucty: dict[Problem, dict[Resitel, Decimal]] = dict() def pridej_reseni(resitel, hodnoceni): problem = hodnoceni.problem @@ -143,11 +147,11 @@ class TabulkaOdevzdanychReseniView(ListView): for resitel in hodnoceni.reseni.resitele.all(): pridej_reseni(resitel, hodnoceni) - hodnoty: list[list[tuple[Decimal,list[tuple[m.Reseni, m.Hodnoceni]]]]] = [] # Seznam řádků výsledné tabulky podle self.resitele, v každém řádku buňky v pořadí podle self.problemy + jejich součty, v každé buňce seznam řešení k danému řešiteli a problému. - resitele_do_tabulky: list[m.Resitel] = [] + hodnoty: list[list[tuple[Decimal,list[tuple[Reseni, Hodnoceni]]]]] = [] # Seznam řádků výsledné tabulky podle self.resitele, v každém řádku buňky v pořadí podle self.problemy + jejich součty, v každé buňce seznam řešení k danému řešiteli a problému. + resitele_do_tabulky: list[Resitel] = [] for resitel in self.resitele: dostal_body = False - resiteluv_radek: list[tuple[Decimal,list[tuple[m.Reseni, m.Hodnoceni]]]] = [] # podle pořadí v self.problemy + resiteluv_radek: list[tuple[Decimal,list[tuple[Reseni, Hodnoceni]]]] = [] # podle pořadí v self.problemy for problem in self.problemy: if problem in tabulka and resitel in tabulka[problem]: resiteluv_radek.append((soucty[problem][resitel], tabulka[problem][resitel])) @@ -162,7 +166,7 @@ class TabulkaOdevzdanychReseniView(ListView): # Pro použití hacku na automatické {{form.media}} v template: ctx['form'] = ctx['filtr'] # Pro maximum v přesměrovátku ročníků - ctx['aktualni_rocnik'] = m.Nastaveni.get_solo().aktualni_rocnik + ctx['aktualni_rocnik'] = Nastaveni.get_solo().aktualni_rocnik ctx['barvicky'] = self.barvicky if 'rocnik' in self.kwargs: ctx['rocnik'] = self.kwargs['rocnik'] @@ -178,7 +182,7 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi Asi už bude zastaralý v okamžiku, kdy se tenhle komentář nasadí na produkci :-) V případě, že takové řešení existuje jen jedno, tak na něj přesměruje.""" - model = m.Reseni + model = Reseni template_name = 'odevzdavatko/seznam.html' def get_queryset(self): @@ -190,8 +194,8 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi if problem_id is None: raise ValueError("Nemám problém! (To je problém!)") - resitel = m.Resitel.objects.get(id=resitel_id) - problem = m.Problem.objects.get(id=problem_id) + resitel = Resitel.objects.get(id=resitel_id) + problem = Problem.objects.get(id=problem_id) qs = qs.filter( problem__in=[problem], resitele__in=[resitel], @@ -221,13 +225,13 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi ## XXX: https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#avoid-anything-more-complex class DetailReseniView(DetailView): """ Náhled na řešení. Editace je v :py:class:`EditReseniView`. """ - model = m.Reseni + model = Reseni template_name = 'odevzdavatko/detail.html' def aktualni_hodnoceni(self): - self.reseni = get_object_or_404(m.Reseni, id=self.kwargs['pk']) + self.reseni = get_object_or_404(Reseni, id=self.kwargs['pk']) result = [] # Slovníky s klíči problem, body, deadline_body -- initial data pro f.OhodnoceniReseniFormSet - for hodn in m.Hodnoceni.objects.filter(reseni=self.reseni): + for hodn in Hodnoceni.objects.filter(reseni=self.reseni): seznam_atributu = [ "problem", "body", @@ -284,7 +288,7 @@ class EditReseniView(DetailReseniView): def hodnoceniReseniView(request, pk, *args, **kwargs): - reseni = get_object_or_404(m.Reseni, pk=pk) + reseni = get_object_or_404(Reseni, pk=pk) success_url = reverse('odevzdavatko_detail_reseni', kwargs={'pk': pk}) # FIXME: Použit initial i tady a nebastlit hodnocení tak nízkoúrovňově @@ -300,7 +304,7 @@ def hodnoceniReseniView(request, pk, *args, **kwargs): poznamka_form.save() # Smažeme všechna dosavadní hodnocení tohoto řešení - qs = m.Hodnoceni.objects.filter(reseni=reseni) + qs = Hodnoceni.objects.filter(reseni=reseni) logger.info(f"Will delete {qs.count()} objects: {qs}") qs.delete() @@ -311,7 +315,7 @@ def hodnoceniReseniView(request, pk, *args, **kwargs): del(data_for_hodnoceni["body_celkem"]) del(data_for_hodnoceni["body_neprepocitane"]) del(data_for_hodnoceni["body_neprepocitane_celkem"]) - hodnoceni = m.Hodnoceni( + hodnoceni = Hodnoceni( reseni=reseni, **form.cleaned_data, ) @@ -332,14 +336,14 @@ def hodnoceniReseniView(request, pk, *args, **kwargs): class PrehledOdevzdanychReseni(ListView): - model = m.Hodnoceni + model = Hodnoceni template_name = 'odevzdavatko/prehled_reseni.html' def get_queryset(self): if not self.request.user.is_authenticated: raise RuntimeError("Uživatel měl být přihlášený!") # get_or_none, aby neexistence řešitele (např. u orgů) neházela chybu - resitel = m.Resitel.objects.filter(osoba__user=self.request.user).first() + resitel = Resitel.objects.filter(osoba__user=self.request.user).first() qs = super().get_queryset() qs = qs.filter(reseni__resitele__in=[resitel]) # Setřídíme podle času doručení řešení, aby se netřídily podle okamžiku vyrobení Hodnocení @@ -360,13 +364,13 @@ class PrehledOdevzdanychReseni(ListView): # Přehled všech řešení kvůli debugování class SeznamReseniView(ListView): - model = m.Reseni + model = Reseni template_name = 'odevzdavatko/seznam.html' class SeznamAktualnichReseniView(SeznamReseniView): def get_queryset(self): qs = super().get_queryset() - akt_rocnik = m.Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci, asi... + akt_rocnik = Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci, asi... resitele = resi_v_rocniku(akt_rocnik) qs = qs.filter(resitele__in=resitele) # FIXME: Najde řešení i ze starých ročníků, která odevzdal alespoň jeden aktuální řešitel return qs @@ -378,7 +382,7 @@ class VlozReseniView(LoginRequiredMixin, FormView): def form_valid(self, form): data = form.cleaned_data - nove_reseni = m.Reseni.objects.create( + nove_reseni = Reseni.objects.create( cas_doruceni=data['cas_doruceni'], forma=data['forma'], poznamka=data['poznamka'], @@ -405,35 +409,35 @@ class VlozReseniView(LoginRequiredMixin, FormView): class NahrajReseniRozcestnikTematekView(LoginRequiredMixin, ListView): - model = m.Problem + model = Problem template_name = 'odevzdavatko/nahraj_reseni_nadproblem.html' def get_queryset(self): - return super().get_queryset().filter(stav=m.Problem.STAV_ZADANY, nadproblem__isnull=True) + return super().get_queryset().filter(stav=Problem.STAV_ZADANY, nadproblem__isnull=True) class NahrajReseniView(LoginRequiredMixin, CreateView): - model = m.Reseni + model = Reseni template_name = 'odevzdavatko/nahraj_reseni.html' form_class = f.NahrajReseniForm - nadproblem: m.Problem + nadproblem: Problem def setup(self, request, *args, **kwargs): super().setup(request, *args, **kwargs) nadproblem_id = self.kwargs["nadproblem_id"] - self.nadproblem = get_object_or_404(m.Problem, id=nadproblem_id) + self.nadproblem = get_object_or_404(Problem, id=nadproblem_id) def get(self, request, *args, **kwargs): # Zaříznutí nezadaných problémů - if self.nadproblem.stav != m.Problem.STAV_ZADANY: + if self.nadproblem.stav != Problem.STAV_ZADANY: raise PermissionDenied() # Zaříznutí starých řešitelů: # FIXME: Je to tady dost naprasené, mělo by to asi být jinde… - osoba = m.Osoba.objects.get(user=self.request.user) + osoba = Osoba.objects.get(user=self.request.user) resitel = osoba.resitel - if resitel.rok_maturity <= m.Nastaveni.get_solo().aktualni_rocnik.prvni_rok: + if resitel.rok_maturity <= Nastaveni.get_solo().aktualni_rocnik.prvni_rok: return render(request, 'universal.html', { 'title': 'Nelze odevzdat', 'error': 'Zdá se, že jsi již odmaturoval/a, a tedy nemůžeš odevzdat do našeho semináře řešení.', @@ -445,7 +449,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): nadproblem_id = self.nadproblem.id return { "nadproblem_id": nadproblem_id, - "problem": [] if self.nadproblem.podproblem.filter(stav=m.Problem.STAV_ZADANY).exists() else nadproblem_id + "problem": [] if self.nadproblem.podproblem.filter(stav=Problem.STAV_ZADANY).exists() else nadproblem_id } @@ -457,7 +461,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): data['prilohy'] = f.ReseniSPrilohamiFormSet() data["nadproblem_id"] = self.nadproblem.id - data["nadproblem"] = get_object_or_404(m.Problem, id=self.nadproblem.id) + data["nadproblem"] = get_object_or_404(Problem, id=self.nadproblem.id) return data # FIXME prepsat tak, aby form_valid se volalo jen tehdy, kdyz je form i formset validni @@ -469,17 +473,17 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): return super().form_invalid(form) with transaction.atomic(): self.object = form.save() - self.object.resitele.add(m.Resitel.objects.get(osoba__user = self.request.user)) + self.object.resitele.add(Resitel.objects.get(osoba__user = self.request.user)) self.object.resitele.add(*form.cleaned_data["resitele"]) self.object.cas_doruceni = timezone.now() - self.object.forma = m.Reseni.FORMA_UPLOAD + self.object.forma = Reseni.FORMA_UPLOAD self.object.save() prilohy.instance = self.object prilohy.save() for hodnoceni in self.object.hodnoceni_set.all(): - hodnoceni.deadline_body = m.Deadline.objects.filter(deadline__gte=self.object.cas_doruceni).first() + hodnoceni.deadline_body = Deadline.objects.filter(deadline__gte=self.object.cas_doruceni).first() hodnoceni.save() # Pošleme mail opravovatelům a garantovi @@ -497,7 +501,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): # FIXME: Víc informativní obsah mailů, možná vč. příloh? prijemci = map(lambda it: it.osoba.email, prijemci) - resitel = m.Osoba.objects.get(user = self.request.user) + resitel = Osoba.objects.get(user = self.request.user) seznam = "problému " + str(problemy[0]) if len(problemy) == 1 else 'následujícím problémům:\n' + ', \n'.join(map(str, problemy)) seznam_do_subjectu = "problému " + str(problemy[0]) + ("" if len(problemy) == 1 else f" (a dalším { len(problemy) - 1 })") diff --git a/personalni/views.py b/personalni/views.py index d994974d..9e29d5b7 100644 --- a/personalni/views.py +++ b/personalni/views.py @@ -16,10 +16,12 @@ from django.db import transaction from django.http import HttpResponse from django.utils import timezone -import seminar.models as s + import personalni.models as m from soustredeni.models import Soustredeni from odevzdavatko.models import Hodnoceni +from tvorba.models import Clanek, Uloha, Tema +from various.models import Nastaveni from .forms import PrihlaskaForm, ProfileEditForm, PoMaturiteProfileEditForm from datetime import date @@ -94,7 +96,7 @@ class OrgoRozcestnikView(TemplateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['posledni_soustredeni'] = Soustredeni.objects.order_by('-datum_konce').first() - nastaveni = s.Nastaveni.objects.first() + nastaveni = Nastaveni.objects.first() aktualni_rocnik = nastaveni.aktualni_rocnik context['posledni_cislo_url'] = nastaveni.aktualni_cislo.verejne_url() # TODO možná chceme odkazovat na právě rozpracované číslo, a ne to poslední vydané @@ -118,11 +120,11 @@ class OrgoRozcestnikView(TemplateView): context["pocty_neopravenych_reseni"] = [(it['problem__nazev'], it['cas'].date) for it in pocty_neopravenych_reseni.all()] #FIXME: přidat stav='STAV_ZADANY' - temata = s.Tema.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]), + temata = Tema.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]), rocnik=aktualni_rocnik).distinct() - ulohy = s.Uloha.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]), + ulohy = Uloha.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]), cislo_zadani__rocnik=aktualni_rocnik).distinct() - clanky = s.Clanek.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]), + clanky = Clanek.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]), cislo__rocnik=aktualni_rocnik).distinct() context['temata'] = temata diff --git a/soustredeni/models.py b/soustredeni/models.py index 23553c34..a86992f5 100644 --- a/soustredeni/models.py +++ b/soustredeni/models.py @@ -10,7 +10,7 @@ from django.conf import settings from personalni.models import Resitel, Organizator from seminar.models.base import SeminarModelBase -import tvorba.models as am +from tvorba.models import Rocnik, Problem, aux_generate_filename logger = logging.getLogger(__name__) @@ -27,7 +27,7 @@ class Soustredeni(SeminarModelBase): # Interní ID id = models.AutoField(primary_key = True) - rocnik = models.ForeignKey(am.Rocnik, verbose_name='ročník', related_name='soustredeni', + rocnik = models.ForeignKey(Rocnik, verbose_name='ročník', related_name='soustredeni', on_delete=models.PROTECT) datum_zacatku = models.DateField('datum začátku', blank=True, null=True, @@ -143,13 +143,13 @@ class Soustredeni_Organizatori(SeminarModelBase): def generate_filename_konfera(self, filename): return os.path.join( settings.SEMINAR_KONFERY_DIR, - am.aux_generate_filename(self, filename) + aux_generate_filename(self, filename) ) ## @reversion.register(ignore_duplicates=True) -class Konfera(am.Problem): +class Konfera(Problem): class Meta: db_table = 'seminar_konfera' verbose_name = 'Konfera' diff --git a/soustredeni/testutils.py b/soustredeni/testutils.py index 6591cbb3..ff51073b 100644 --- a/soustredeni/testutils.py +++ b/soustredeni/testutils.py @@ -6,7 +6,7 @@ from typing import Sequence import lorem from .models import Soustredeni, Konfera -import seminar.models as am # tvorba +from tvorba.models import Rocnik import personalni.models as pm logger = logging.getLogger(__name__) @@ -25,7 +25,7 @@ def gen_soustredeni( for _ in range(1, 10): # FIXME Tu range si změňte jak chcete, nevím, co přesně znamená size (asi Anet?) datum_zacatku = datetime.date(rnd.randint(2000, 2020), rnd.randint(1, 12), rnd.randint(1, 28)) working_sous = Soustredeni.objects.create( - rocnik=am.Rocnik.objects.order_by('?').first(), + rocnik=Rocnik.objects.order_by('?').first(), verejne_db=rnd.choice([True, False]), misto=rnd.choice(['Kremrolovice', 'Indiánov', 'U zmzliny', 'Vafláreň', 'Větrník', 'Horní Rakvička', 'Dolní cheesecake']), typ=rnd.choice(['jarni', 'podzimni', 'vikend']), diff --git a/tvorba/templatetags/deadliny.py b/tvorba/templatetags/deadliny.py index 199a1eef..837efcc0 100644 --- a/tvorba/templatetags/deadliny.py +++ b/tvorba/templatetags/deadliny.py @@ -1,30 +1,30 @@ from django import template from django.utils.safestring import mark_safe register = template.Library() -import seminar.models as m +from tvorba.models import Deadline @register.filter(name='deadline_kratseji') -def deadline_kratsi_text(deadline: m.Deadline): +def deadline_kratsi_text(deadline: Deadline): if deadline is None: return 'NONE' strings = { - m.Deadline.TYP_PRVNI: f"{deadline.cislo} ⭯", - m.Deadline.TYP_SOUS: f"{deadline.cislo} Ⓢ", - m.Deadline.TYP_PRVNI_A_SOUS: f"{deadline.cislo} ⭯Ⓢ", - m.Deadline.TYP_CISLA: f"{deadline.cislo} ✓", + Deadline.TYP_PRVNI: f"{deadline.cislo} ⭯", + Deadline.TYP_SOUS: f"{deadline.cislo} Ⓢ", + Deadline.TYP_PRVNI_A_SOUS: f"{deadline.cislo} ⭯Ⓢ", + Deadline.TYP_CISLA: f"{deadline.cislo} ✓", } return strings[deadline.typ] @register.filter(name='deadline_html') -def deadline_html(deadline: m.Deadline): +def deadline_html(deadline: Deadline): if deadline is None: return 'Neznámý deadline' text = deadline_kratsi_text(deadline) classes = { - m.Deadline.TYP_PRVNI: 'preddeadline', - m.Deadline.TYP_SOUS: 'sous_deadline', - m.Deadline.TYP_PRVNI_A_SOUS: 'sous_deadline', - m.Deadline.TYP_CISLA: 'final_deadline', + Deadline.TYP_PRVNI: 'preddeadline', + Deadline.TYP_SOUS: 'sous_deadline', + Deadline.TYP_PRVNI_A_SOUS: 'sous_deadline', + Deadline.TYP_CISLA: 'final_deadline', } return mark_safe(f'{text}') diff --git a/tvorba/testutils.py b/tvorba/testutils.py index 18440679..323acb38 100644 --- a/tvorba/testutils.py +++ b/tvorba/testutils.py @@ -6,7 +6,9 @@ import lorem import django.contrib.auth import logging -from seminar.models import Rocnik, Cislo, Deadline, Problem, Tema, Uloha, TextNode, UlohaVzorakNode, RocnikNode, CisloNode, TemaVCisleNode, Text, UlohaZadaniNode +from .models import Rocnik, Cislo, Deadline, Problem, Tema, Uloha + +from odevzdavatko.models import Reseni, Hodnoceni import seminar.models as m from treenode.treelib import all_children, insert_last_child, all_children_of_type, create_node_after @@ -54,12 +56,12 @@ def gen_zadani_ulohy(rnd, cisla, organizatori, pocet_oboru, poradi_cisla, poradi rnd.choice(jmeno), rnd.choice(kde)] ) - text_zadani = Text.objects.create( + text_zadani = m.Text.objects.create( na_web = text, do_cisla = text, ) - zad = TextNode.objects.create(text = text_zadani, root = p.cislo_zadani.rocnik.rocniknode) - uloha_zadani = UlohaZadaniNode.objects.create(uloha = p, first_child = zad, root = p.cislo_zadani.rocnik.rocniknode) + zad = m.TextNode.objects.create(text = text_zadani, root = p.cislo_zadani.rocnik.rocniknode) + uloha_zadani = m.UlohaZadaniNode.objects.create(uloha = p, first_child = zad, root = p.cislo_zadani.rocnik.rocniknode) p.ulohazadaninode = uloha_zadani otec_syn(cisla[poradi_cisla-2-1].cislonode, uloha_zadani) @@ -76,12 +78,12 @@ def gen_vzoroveho_reseni_ulohy(rnd, organizatori, uloha, pocet_opravovatelu): # Generování vzorového řešení. obsah = rnd.choice(reseni) - text_vzoraku = Text.objects.create( + text_vzoraku = m.Text.objects.create( na_web = obsah, do_cisla = obsah ) - vzorak = TextNode.objects.create(text = text_vzoraku, root = uloha.cislo_zadani.rocnik.rocniknode) - uloha_vzorak = UlohaVzorakNode.objects.create(uloha = uloha, first_child = vzorak, root = uloha.cislo_zadani.rocnik.rocniknode) + vzorak = m.TextNode.objects.create(text = text_vzoraku, root = uloha.cislo_zadani.rocnik.rocniknode) + uloha_vzorak = m.UlohaVzorakNode.objects.create(uloha = uloha, first_child = vzorak, root = uloha.cislo_zadani.rocnik.rocniknode) uloha.ulohavzoraknode = uloha_vzorak uloha.opravovatele.set(rnd.sample(organizatori, pocet_opravovatelu)) @@ -132,7 +134,7 @@ def gen_rocniky(last_rocnik, size): node = None for ri in range(min(last_rocnik - size, 1), last_rocnik + 1): rocnik = Rocnik.objects.create(prvni_rok = 1993 + ri, rocnik = ri) - node2 = RocnikNode.objects.create(rocnik = rocnik, succ = node) + node2 = m.RocnikNode.objects.create(rocnik = rocnik, succ = node) rocnik.save() node = node2 rocniky.append(rocnik) @@ -167,7 +169,7 @@ def gen_cisla(rnd, rocniky): datum_vydani=vydano, verejne_db=True, ) - node2 = CisloNode.objects.get(cislo = cislo) + node2 = m.CisloNode.objects.get(cislo = cislo) node2.succ = node node2.root = rocnik.rocniknode cislo.save() @@ -195,7 +197,7 @@ def add_first_child(node, child): def get_text(): odstavec = lorem.paragraph() - return Text.objects.create(na_web = odstavec, do_cisla = odstavec) + return m.Text.objects.create(na_web = odstavec, do_cisla = odstavec) def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod): tema = Tema.objects.create( @@ -215,32 +217,32 @@ def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod): for cislo in cisla: # Přidáme TemaVCisleNode do daného čísla cislo_node = cislo.cislonode - tema_cislo_node = TemaVCisleNode.objects.create(tema = tema, root = cislo_node.root) + tema_cislo_node = m.TemaVCisleNode.objects.create(tema = tema, root = cislo_node.root) insert_last_child(cislo_node, tema_cislo_node) # Přidávání obsahu do čísla cast_node = m.CastNode.objects.create(nadpis = "Příspěvek k číslu {}".format(cislo.kod), root=cislo_node.root) add_first_child(tema_cislo_node, cast_node) - text_node = TextNode.objects.create(text = get_text(), root=cislo_node.root) + text_node = m.TextNode.objects.create(text = get_text(), root=cislo_node.root) add_first_child(cast_node, text_node) cast_node2 = m.CastNode.objects.create(nadpis = "První podproblém", root=cislo_node.root) add_first_child(text_node, cast_node2) - text_node2 = TextNode.objects.create(text = get_text(), root=cislo_node.root) + text_node2 = m.TextNode.objects.create(text = get_text(), root=cislo_node.root) add_first_child(cast_node2, text_node2) cast_node3 = m.CastNode.objects.create(nadpis = "Druhý podproblém", root=cislo_node.root) add_first_child(text_node2, cast_node3) - text_node3 = TextNode.objects.create(text = get_text(), root=cislo_node.root) + text_node3 = m.TextNode.objects.create(text = get_text(), root=cislo_node.root) add_first_child(cast_node3, text_node3) cast_node4 = m.CastNode.objects.create(nadpis = "Třetí podproblém", root=cislo_node.root) add_first_child(text_node3, cast_node4) - text_node4 = TextNode.objects.create(text = get_text(), root=cislo_node.root) + text_node4 = m.TextNode.objects.create(text = get_text(), root=cislo_node.root) add_first_child(cast_node3, text_node4) cast_node3a = m.CastNode.objects.create(nadpis = "Podproblém paralelní s " @@ -248,7 +250,7 @@ def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod): cast_node3.succ = cast_node3a cast_node3.save() - text_node3a = TextNode.objects.create(text = get_text(), root=cislo_node.root) + text_node3a = m.TextNode.objects.create(text = get_text(), root=cislo_node.root) add_first_child(cast_node3a, text_node3a) # Občas přidáme mezičíslo @@ -261,8 +263,8 @@ def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod): add_first_child(mezicislo_node, cast_node_mezicislo) odstavec = lorem.paragraph() - text_mezicislo = Text.objects.create(na_web = odstavec, do_cisla = odstavec) - text_node_mezicislo = TextNode.objects.create(text = text_mezicislo, root=cislo_node.root) + text_mezicislo = m.Text.objects.create(na_web = odstavec, do_cisla = odstavec) + text_node_mezicislo = m.TextNode.objects.create(text = text_mezicislo, root=cislo_node.root) add_first_child(cast_node_mezicislo, text_node_mezicislo) return tema @@ -306,7 +308,7 @@ def gen_temata(rnd, rocniky, rocnik_cisla, organizatori): # Vyrobíme TemaVCisleNody pro obsah for i in range(zacatek_tematu, konec_tematu+1): - node = TemaVCisleNode.objects.create(tema = t,root=rocnik.rocniknode) + node = m.TemaVCisleNode.objects.create(tema = t,root=rocnik.rocniknode) # FIXME: Není to off-by-one? otec = cisla[i-1].cislonode otec_syn(otec, node) @@ -359,12 +361,12 @@ def gen_ulohy_tematu(rnd, organizatori, resitele, tema, kod, cislo, cislo_se_vzo rnd.choice(jmeno), rnd.choice(kde)] ) - text_zadani = Text.objects.create( + text_zadani = m.Text.objects.create( na_web = obsah, do_cisla = obsah, ) - zad = TextNode.objects.create(text = text_zadani, root=tema.temavcislenode_set.first().root) - uloha_zadani = UlohaZadaniNode.objects.create(uloha=uloha, first_child = zad, root=tema.temavcislenode_set.first().root) + zad = m.TextNode.objects.create(text = text_zadani, root=tema.temavcislenode_set.first().root) + uloha_zadani = m.UlohaZadaniNode.objects.create(uloha=uloha, first_child = zad, root=tema.temavcislenode_set.first().root) uloha.ulohazadaninode = uloha_zadani # Generování řešení a hodnocení k úloze @@ -396,7 +398,7 @@ def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori, else: cislo_se_vzorakem = cislo_se_vzorakem.first() - for tema_node in all_children_of_type(cislo.cislonode, TemaVCisleNode): + for tema_node in all_children_of_type(cislo.cislonode, m.TemaVCisleNode): tema = tema_node.tema # Pokud už témátko skončilo, žádné úložky negenerujeme @@ -419,7 +421,7 @@ def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori, # Najdeme správný TemaVCisleNode pro vložení vzoráku res_tema_node = None; for node in all_children(cislo_se_vzorakem.cislonode): - if isinstance(node, TemaVCisleNode): + if isinstance(node, m.TemaVCisleNode): if node.tema == tema: res_tema_node = node if res_tema_node is None: @@ -448,16 +450,16 @@ def gen_clanek(rnd, organizatori, resitele): ) clanek.save() - reseni = m.Reseni.objects.create( + reseni = Reseni.objects.create( zverejneno=True, ) reseni.resitele.add(rnd.choice(resitele)) reseni.save() - cislo = m.Cislo.objects.get(rocnik__rocnik=22, poradi=2) + cislo = Cislo.objects.get(rocnik__rocnik=22, poradi=2) cislonode = cislo.cislonode - hodnoceni = m.Hodnoceni.objects.create( + hodnoceni = Hodnoceni.objects.create( body=15.0, cislo_body=cislo, reseni=reseni, diff --git a/tvorba/utils.py b/tvorba/utils.py index ba0c5d5b..c2feadd9 100644 --- a/tvorba/utils.py +++ b/tvorba/utils.py @@ -2,7 +2,7 @@ from django.core.exceptions import ObjectDoesNotExist import personalni.models -import seminar.models as m +import tvorba.models as m def resi_v_rocniku(rocnik, cislo=None): diff --git a/tvorba/views/__init__.py b/tvorba/views/__init__.py index 823ddd96..e45eaaa6 100644 --- a/tvorba/views/__init__.py +++ b/tvorba/views/__init__.py @@ -14,12 +14,11 @@ from django.db.models import Q, Sum, Count from django.views.generic.base import RedirectView from django.core.exceptions import PermissionDenied -import seminar.models as s import seminar.models as m -from seminar.models import Problem, Cislo, Reseni, Nastaveni, Rocnik, \ - Resitel, Novinky, Tema, Clanek, \ - Deadline # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci -#from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva +from personalni.models import Resitel +from soustredeni.models import Konfera +from tvorba.models import Problem, Cislo, Rocnik, Tema, Clanek, Deadline, Uloha +from various.models import Nastaveni from treenode import treelib import treenode.templatetags as tnltt import treenode.serializers as vr @@ -58,7 +57,7 @@ def get_problemy_k_tematu(tema): # FIXME: Pozor, níž je ještě jeden ProblemView! #class ProblemView(generic.DetailView): -# model = s.Problem +# model = Problem # # Zkopírujeme template_name od TreeNodeView, protože jsme prakticky jen trošku upravený TreeNodeView # template_name = TreeNodeView.template_name # @@ -70,17 +69,17 @@ def get_problemy_k_tematu(tema): # if False: # # Hezčí formátování zbytku :-P # pass -# elif isinstance(self.object, s.Clanek) or isinstance(self.object, s.Konfera): +# elif isinstance(self.object, Clanek) or isinstance(self.object, Konfera): # # Tyhle Problémy mají ŘešeníNode # context['tnldata'] = TNLData.from_treenode(self.object.reseninode,user) -# elif isinstance(self.object, s.Uloha): +# elif isinstance(self.object, Uloha): # # FIXME: Teď vždycky zobrazujeme i vzorák! Možná by bylo hezčí/lepší mít to stejně jako pro Téma: procházet jen dosažitelné z Ročníku / čísla / whatever # tnl_zadani = TNLData.from_treenode(self.object.ulohazadaninode,user) # tnl_vzorak = TNLData.from_treenode(self.object.ulohavzoraknode,user) # context['tnldata'] = TNLData.from_tnldata_list([tnl_zadani, tnl_vzorak]) -# elif isinstance(self.object, s.Tema): +# elif isinstance(self.object, Tema): # rocniknode = self.object.rocnik.rocniknode -# context['tnldata'] = TNLData.filter_treenode(rocniknode, lambda x: isinstance(x, s.TemaVCisleNode)) +# context['tnldata'] = TNLData.filter_treenode(rocniknode, lambda x: isinstance(x, m.TemaVCisleNode)) # else: # raise ValueError("Obecný problém nejde zobrazit.") # return context @@ -115,7 +114,7 @@ def ZadaniTemataView(request): nastaveni = get_object_or_404(Nastaveni) verejne = nastaveni.aktualni_cislo.verejne() akt_rocnik = nastaveni.aktualni_cislo.rocnik - temata = s.Tema.objects.filter(rocnik=akt_rocnik, stav='zadany') + temata = Tema.objects.filter(rocnik=akt_rocnik, stav='zadany') return render(request, 'tvorba/tematka/rozcestnik.html', { 'tematka': temata, @@ -140,14 +139,14 @@ def ZadaniTemataView(request): # # #def TematkoView(request, rocnik, tematko): -# nastaveni = s.Nastaveni.objects.first() -# rocnik_object = s.Rocnik.objects.filter(rocnik=rocnik) -# tematko_object = s.Tema.objects.filter(rocnik=rocnik_object[0], kod=tematko) +# nastaveni = Nastaveni.objects.first() +# rocnik_object = Rocnik.objects.filter(rocnik=rocnik) +# tematko_object = Tema.objects.filter(rocnik=rocnik_object[0], kod=tematko) # seznam = vytahniZLesaSeznam(tematko_object[0], nastaveni.aktualni_rocnik().rocniknode) # for node, depth in seznam: -# if node.isinstance(node, s.KonferaNode): +# if node.isinstance(node, m.KonferaNode): # raise Exception("Not implemented yet") -# if node.isinstance(node, s.PohadkaNode): # Mohu ignorovat, má pod sebou +# if node.isinstance(node, m.PohadkaNode): # Mohu ignorovat, má pod sebou # pass # # return render(request, 'tvorba/tematka/toaletak.html', {}) @@ -155,8 +154,8 @@ def ZadaniTemataView(request): # #def TemataRozcestnikView(request): # print("=============================================") -# nastaveni = s.Nastaveni.objects.first() -# tematka_objects = s.Tema.objects.filter(rocnik=nastaveni.aktualni_rocnik()) +# nastaveni = Nastaveni.objects.first() +# tematka_objects = Tema.objects.filter(rocnik=nastaveni.aktualni_rocnik()) # tematka = [] #List tematka obsahuje pro kazde tematko object a list vsech TemaVCisleNodu - implementované pomocí slovníku # for tematko_object in tematka_objects: # print("AKTUALNI TEMATKO") @@ -278,7 +277,7 @@ def resiteleRocnikuCsvExportView(request, rocnik): assert request.method in ('GET', 'HEAD') return dataResiteluCsvResponse( utils.resi_v_rocniku( - get_object_or_404(m.Rocnik, rocnik=rocnik) + get_object_or_404(Rocnik, rocnik=rocnik) ) ) @@ -291,10 +290,10 @@ def resiteleRocnikuCsvExportView(request, rocnik): # def get_template_names(self, **kwargs): # # FIXME: Switch podle typu není hezký, ale nechtělo se mi to přepisovat celé. Správně by se tohle mělo řešit polymorfismem. # spravne_templaty = { -# s.Uloha: "uloha", -# s.Tema: "tema", -# s.Konfera: "konfera", -# s.Clanek: "clanek", +# Uloha: "uloha", +# Tema: "tema", +# Konfera: "konfera", +# Clanek: "clanek", # } # context = super().get_context_data(**kwargs) # return ['tvorba/archiv/problem_' + spravne_templaty[context['object'].__class__] + '.html'] @@ -340,10 +339,10 @@ class CisloView(generic.DetailView): deadliny_s_vysledkovkami = [] nadpisy = { - m.Deadline.TYP_CISLA: "Výsledkovka", - m.Deadline.TYP_PRVNI: "Výsledkovka do prvního deadlinu", - m.Deadline.TYP_PRVNI_A_SOUS: "Výsledkovka do prvního deadlinu a deadlinu pro účast na soustředění", - m.Deadline.TYP_SOUS: "Výsledkovka do deadlinu pro účast na soustředění", + Deadline.TYP_CISLA: "Výsledkovka", + Deadline.TYP_PRVNI: "Výsledkovka do prvního deadlinu", + Deadline.TYP_PRVNI_A_SOUS: "Výsledkovka do prvního deadlinu a deadlinu pro účast na soustředění", + Deadline.TYP_SOUS: "Výsledkovka do deadlinu pro účast na soustředění", } for deadline in deadliny: @@ -580,5 +579,5 @@ class AktualniRocnikRedirectView(RedirectView): pattern_name = 'seminar_rocnik' def get_redirect_url(self, *args, **kwargs): - aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik.rocnik + aktualni_rocnik = Nastaveni.get_solo().aktualni_rocnik.rocnik return super().get_redirect_url(rocnik=aktualni_rocnik, *args, **kwargs) diff --git a/tvorba/views/docasne.py b/tvorba/views/docasne.py index cbad789b..453909ca 100644 --- a/tvorba/views/docasne.py +++ b/tvorba/views/docasne.py @@ -5,13 +5,13 @@ from django.db import transaction from django.forms import Form, CharField, IntegerField from django.views.generic import FormView -import seminar.models as m +from tvorba.models import Cislo, Problem, Uloha, Tema from django.shortcuts import render, get_object_or_404 def problemView(request, pk): # Pokud problém neexistuje, hodíme obyčejnou 404 # Taktéž v případě, že takový problém nemá být vidět - problem = get_object_or_404(m.Problem, id=pk, stav__in=[m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY]) + problem = get_object_or_404(Problem, id=pk, stav__in=[Problem.STAV_ZADANY, Problem.STAV_VYRESENY]) # Problém existuje, neumíme ho zobrazit, renderujeme nějakou haluz template = 'universal.html' ctx = { @@ -32,7 +32,7 @@ class HromadnePridaniForm(Form): def clean_tema(self): """ Kontrola, že `tema` je název právě jednoho tématu """ - if m.Tema.objects.filter( + if Tema.objects.filter( nazev__exact=self.cleaned_data['tema'], nadproblem=None).count() != 1: raise ValidationError("Špatný nebo nepřesně zadaný název témátka") @@ -67,20 +67,20 @@ class HromadnePridaniView(FormView): dil = cd["dil"] body = list(map(int, cd["body"].split(","))) - t = m.Problem.objects.get(nazev__exact=tema, nadproblem=None) + t = Problem.objects.get(nazev__exact=tema, nadproblem=None) with transaction.atomic(): pfx = f"{t.nazev}, díl {dil}, " for k, b in enumerate(body, 1): - u = m.Uloha.objects.create( + u = Uloha.objects.create( nadproblem=t, nazev=pfx + f"{'úloha' if b > 0 else 'problém'} {k}", autor=t.autor, garant=t.garant, max_body=b, - cislo_zadani=m.Cislo.get(t.rocnik.rocnik, dil), + cislo_zadani=Cislo.get(t.rocnik.rocnik, dil), kod=k, - stav=m.Problem.STAV_ZADANY, + stav=Problem.STAV_ZADANY, ) u.opravovatele.set(t.opravovatele.all()) return super().form_valid(form) diff --git a/various/autentizace/views.py b/various/autentizace/views.py index 73b51c54..dc24984f 100644 --- a/various/autentizace/views.py +++ b/various/autentizace/views.py @@ -55,5 +55,4 @@ class PasswordResetCompleteView(auth_views.PasswordResetCompleteView): class PasswordChangeView(auth_views.PasswordChangeView): - # template_name = 'seminar/password_change.html' success_url = reverse_lazy('titulni_strana') diff --git a/various/management/commands/generate_thumbnails.py b/various/management/commands/generate_thumbnails.py index e52ea9bc..77d8a135 100644 --- a/various/management/commands/generate_thumbnails.py +++ b/various/management/commands/generate_thumbnails.py @@ -1,6 +1,6 @@ from django.core.management.base import BaseCommand -from seminar.models import Cislo +from tvorba.models import Cislo from subprocess import CalledProcessError import logging diff --git a/various/management/commands/pregeneruj_zmrazene_vysledkovky.py b/various/management/commands/pregeneruj_zmrazene_vysledkovky.py index d728ab0f..ba3f3103 100644 --- a/various/management/commands/pregeneruj_zmrazene_vysledkovky.py +++ b/various/management/commands/pregeneruj_zmrazene_vysledkovky.py @@ -1,11 +1,11 @@ from django.core.management.base import BaseCommand -import seminar.models as m +from tvorba.models import Deadline class Command(BaseCommand): help = "Všem deadlinům se zveřejněnou výsledkovkou vygeneruj výsledkovku" def handle(self, *args, **options): - for deadline in m.Deadline.objects.filter(verejna_vysledkovka=True): + for deadline in Deadline.objects.filter(verejna_vysledkovka=True): deadline.vygeneruj_vysledkovku() diff --git a/various/management/commands/testdata.py b/various/management/commands/testdata.py index 8f591fa5..7cfc726b 100644 --- a/various/management/commands/testdata.py +++ b/various/management/commands/testdata.py @@ -4,7 +4,9 @@ from django.core.management.base import BaseCommand from django.core.management import call_command from django.conf import settings -from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni +from odevzdavatko.models import Reseni +from personalni.models import Skola, Resitel +from tvorba.models import Rocnik, Cislo, Problem from various.testutils import create_test_data import django.contrib.auth User = django.contrib.auth.get_user_model() diff --git a/various/testutils.py b/various/testutils.py index 52411ef6..95433324 100644 --- a/various/testutils.py +++ b/various/testutils.py @@ -7,7 +7,9 @@ from django.contrib.flatpages.models import FlatPage from django.contrib.sites.models import Site from django.db import transaction -from seminar.models import Rocnik, Cislo, Nastaveni, Osoba, Organizator +from personalni.models import Osoba, Organizator +from tvorba.models import Rocnik, Cislo +from various.models import Nastaveni from korektury.testutils import create_test_pdf from novinky.testutils import gen_novinky diff --git a/various/views/final.py b/various/views/final.py index de23a718..d21c00be 100644 --- a/various/views/final.py +++ b/various/views/final.py @@ -14,8 +14,9 @@ from django.views import generic import novinky.views import treenode.treelib as t import tvorba.views -from personalni.models import Resitel -from seminar import models as m +import seminar.models as m +from personalni.models import Resitel, Osoba +from tvorba.models import Clanek, Deadline from ..models import Nastaveni @@ -30,7 +31,7 @@ class TitulniStranaView(generic.ListView): context = super(TitulniStranaView, self).get_context_data(**kwargs) nastaveni = get_object_or_404(Nastaveni) - deadline = m.Deadline.objects.filter( + deadline = Deadline.objects.filter( deadline__gte=timezone.now()).order_by("deadline").first() context['nejblizsi_deadline'] = deadline @@ -93,31 +94,31 @@ def seznam_problemu(): # Duplicita jmen jmena = {} - for r in m.Resitel.objects.all(): + for r in Resitel.objects.all(): j = r.osoba.plne_jmeno() if j not in jmena: jmena[j] = [] jmena[j].append(r) for j in jmena: if len(jmena[j]) > 1: - prb(m.Resitel, 'Duplicitní jméno "%s"' % (j,), jmena[j]) + prb(Resitel, 'Duplicitní jméno "%s"' % (j,), jmena[j]) # Data maturity a narození - for r in m.Resitel.objects.all(): + for r in Resitel.objects.all(): if not r.rok_maturity: - prb(m.Resitel, 'Neznámý rok maturity', [r]) + prb(Resitel, 'Neznámý rok maturity', [r]) if r.rok_maturity and (r.rok_maturity < 1990 or r.rok_maturity > datetime.date.today().year + 10): - prb(m.Resitel, 'Podezřelé datum maturity', [r]) + prb(Resitel, 'Podezřelé datum maturity', [r]) if r.osoba.datum_narozeni and ( r.osoba.datum_narozeni.year < 1970 or r.osoba.datum_narozeni.year > datetime.date.today().year - 12): - prb(m.Resitel, 'Podezřelé datum narození', [r]) + prb(Resitel, 'Podezřelé datum narození', [r]) # if not r.email: # prb(Resitel, u'Neznámý email', [r]) ## Kontroly konzistence databáze a TreeNodů # Články - for clanek in m.Clanek.objects.all(): + for clanek in Clanek.objects.all(): # získáme řešení svázané se článkem a z něj node ve stromě reseni = clanek.reseni_set if (reseni.count() != 1): @@ -136,7 +137,7 @@ def seznam_problemu(): # .cislonode je opačná vazba k treenode_ptr, abychom z TreeNode dostali # CisloNode if clanek.cislo != node.cislonode.cislo: - prb(m.Clanek, "Číslo otištění uložené u článku nesedí s " + prb(Clanek, "Číslo otištění uložené u článku nesedí s " "číslem otištění podle struktury treenodů.", [clanek]) break node = t.get_parent(node) @@ -146,8 +147,8 @@ def seznam_problemu(): def StavDatabazeView(request): # nastaveni = Nastaveni.objects.get() problemy = seznam_problemu() - muzi = Resitel.objects.filter(osoba__osloveni=m.Osoba.OSLOVENI_MUZSKE) - zeny = Resitel.objects.filter(osoba__osloveni=m.Osoba.OSLOVENI_ZENSKE) + muzi = Resitel.objects.filter(osoba__osloveni=Osoba.OSLOVENI_MUZSKE) + zeny = Resitel.objects.filter(osoba__osloveni=Osoba.OSLOVENI_ZENSKE) return render(request, 'various/stav_databaze.html', { # 'nastaveni': nastaveni, 'problemy': problemy, diff --git a/vysledkovky/utils.py b/vysledkovky/utils.py index 7cd914f4..0a6c1897 100644 --- a/vysledkovky/utils.py +++ b/vysledkovky/utils.py @@ -2,7 +2,10 @@ import abc from functools import cached_property from typing import Union, Iterable # TODO: s pythonem 3.10 přepsat na '|' -import seminar.models as m +from odevzdavatko.models import Hodnoceni +from personalni.models import Resitel +from soustredeni.models import Konfera +from tvorba.models import Cislo, Rocnik, Deadline, Problem, Clanek from django.db.models import Q, Sum from tvorba.utils import resi_v_rocniku @@ -18,11 +21,11 @@ class FixedIterator: def body_resitelu( - za: Union[m.Cislo, m.Rocnik, None] = None, - do: m.Deadline = None, - od: m.Deadline = None, + za: Union[Cislo, Rocnik, None] = None, + do: Deadline = None, + od: Deadline = None, jen_verejne: bool = True, - resitele: Iterable[m.Resitel] = None, + resitele: Iterable[Resitel] = None, null=0 # Výchozí hodnota, pokud pro daného řešitele nejsou body ) -> dict[int, int]: filtr = Q() @@ -31,9 +34,9 @@ def body_resitelu( filtr &= Q(reseni__hodnoceni__deadline_body__verejna_vysledkovka=True) # Zjistíme, typ objektu v parametru "za" - if isinstance(za, m.Rocnik): + if isinstance(za, Rocnik): filtr &= Q(reseni__hodnoceni__deadline_body__cislo__rocnik=za) - elif isinstance(za, m.Cislo): + elif isinstance(za, Cislo): filtr &= Q(reseni__hodnoceni__deadline_body__cislo=za) if do: @@ -42,7 +45,7 @@ def body_resitelu( if od: filtr &= Q(reseni__hodnoceni__deadline_body__deadline__gte=od.deadline) - resiteleQuery = m.Resitel.objects.all() + resiteleQuery = Resitel.objects.all() if resitele is not None: resitele_id = [r.id for r in resitele] @@ -63,12 +66,12 @@ def body_resitelu( class Vysledkovka(abc.ABC): jen_verejne: bool - rocnik: m.Rocnik - do_deadlinu: m.Deadline + rocnik: Rocnik + do_deadlinu: Deadline @property @abc.abstractmethod - def aktivni_resitele(self) -> list[m.Resitel]: + def aktivni_resitele(self) -> list[Resitel]: ... @cached_property @@ -143,20 +146,20 @@ class Vysledkovka(abc.ABC): class VysledkovkaRocniku(Vysledkovka): - def __init__(self, rocnik: m.Rocnik, jen_verejne: bool = True): + def __init__(self, rocnik: Rocnik, jen_verejne: bool = True): self.rocnik = rocnik self.jen_verejne = jen_verejne - deadliny = m.Deadline.objects.filter(cislo__rocnik=rocnik) + deadliny = Deadline.objects.filter(cislo__rocnik=rocnik) if jen_verejne: deadliny = deadliny.filter(verejna_vysledkovka=True) self.do_deadlinu = deadliny.order_by("deadline").last() @cached_property - def aktivni_resitele(self) -> list[m.Resitel]: + def aktivni_resitele(self) -> list[Resitel]: return list(resi_v_rocniku(self.rocnik)) @cached_property - def cisla_rocniku(self) -> list[m.Cislo]: + def cisla_rocniku(self) -> list[Cislo]: """ Vrátí všechna čísla daného ročníku. """ if self.jen_verejne: return self.rocnik.verejne_vysledkovky_cisla() @@ -164,7 +167,7 @@ class VysledkovkaRocniku(Vysledkovka): return self.rocnik.cisla.all().order_by('poradi') @cached_property - def body_za_cisla_slovnik(self) -> dict[int, dict[int, int]]: # Výstup: m.Cislo.id → ( m.Resitel.id → body ) + def body_za_cisla_slovnik(self) -> dict[int, dict[int, int]]: # Výstup: Cislo.id → ( Resitel.id → body ) # TODO: Body jsou decimal! body_cisla_slovnik = dict() for cislo in self.cisla_rocniku: @@ -197,7 +200,7 @@ class VysledkovkaRocniku(Vysledkovka): radky_vysledkovky = [] setrizeni_resitele_dict = dict() - for r in m.Resitel.objects.filter( + for r in Resitel.objects.filter( id__in=self.setrizeni_resitele_id ).select_related('osoba'): setrizeni_resitele_dict[r.id] = r @@ -227,31 +230,31 @@ class VysledkovkaRocniku(Vysledkovka): class VysledkovkaCisla(Vysledkovka): def __init__( self, - cislo: m.Cislo, + cislo: Cislo, jen_verejne: bool = True, - do_deadlinu: m.Deadline = None + do_deadlinu: Deadline = None ): self.cislo = cislo self.rocnik = cislo.rocnik self.jen_verejne = jen_verejne if do_deadlinu is None: - do_deadlinu = m.Deadline.objects.filter(cislo=cislo).last() + do_deadlinu = Deadline.objects.filter(cislo=cislo).last() self.do_deadlinu = do_deadlinu @cached_property - def aktivni_resitele(self) -> list[m.Resitel]: + def aktivni_resitele(self) -> list[Resitel]: # TODO možná chytřeji vybírat aktivní řešitele return list(resi_v_rocniku(self.rocnik)) @cached_property - def problemy(self) -> list[m.Problem]: + def problemy(self) -> list[Problem]: """ Vrátí seznam všech problémů s body v daném čísle. """ - return m.Problem.objects.filter( - hodnoceni__in=m.Hodnoceni.objects.filter(deadline_body__cislo=self.cislo) + return Problem.objects.filter( + hodnoceni__in=Hodnoceni.objects.filter(deadline_body__cislo=self.cislo) ).distinct().non_polymorphic().select_related('nadproblem').select_related('nadproblem__nadproblem') @cached_property - def hlavni_problemy(self) -> list[m.Problem]: + def hlavni_problemy(self) -> list[Problem]: """ Vrátí seznam všech problémů, které již nemají nadproblém. """ # hlavní problémy čísla # (mají vlastní sloupeček ve výsledkovce, nemají nadproblém) @@ -269,7 +272,7 @@ class VysledkovkaCisla(Vysledkovka): # Není cached, protože si myslím, že queryset lze použít ve for jen jednou. @property def hodnoceni_do_cisla(self): - hodnoceni = m.Hodnoceni.objects.prefetch_related('reseni__resitele').select_related('problem', 'reseni') + hodnoceni = Hodnoceni.objects.prefetch_related('reseni__resitele').select_related('problem', 'reseni') if self.jen_verejne: hodnoceni = hodnoceni.filter(deadline_body__verejna_vysledkovka=True) return hodnoceni.filter( @@ -347,7 +350,7 @@ class VysledkovkaCisla(Vysledkovka): return self.sectene_body[2] @cached_property - def temata_a_spol(self) -> list[m.Problem]: + def temata_a_spol(self) -> list[Problem]: if self.rocnik.rocnik < ROCNIK_ZRUSENI_TEMAT: return self.hlavni_problemy else: @@ -358,7 +361,7 @@ class VysledkovkaCisla(Vysledkovka): return len(self.hlavni_problemy) - len(self.temata_a_spol) > 0 @cached_property - def podproblemy(self) -> dict[int, list[m.Problem]]: + def podproblemy(self) -> dict[int, list[Problem]]: podproblemy = {hp.id: [] for hp in self.temata_a_spol} temata_a_spol = set(self.temata_a_spol) podproblemy[-1] = [] @@ -375,7 +378,7 @@ class VysledkovkaCisla(Vysledkovka): return podproblemy @cached_property - def podproblemy_seznam(self) -> list[list[m.Problem]]: + def podproblemy_seznam(self) -> list[list[Problem]]: return [self.podproblemy[it.id] for it in self.temata_a_spol] + [self.podproblemy[-1]] @cached_property @@ -405,7 +408,7 @@ class VysledkovkaCisla(Vysledkovka): radky_vysledkovky = [] setrizeni_resitele_slovnik = {} - setrizeni_resitele = m.Resitel.objects.filter(id__in=self.setrizeni_resitele_id).select_related('osoba') + setrizeni_resitele = Resitel.objects.filter(id__in=self.setrizeni_resitele_id).select_related('osoba') for r in setrizeni_resitele: setrizeni_resitele_slovnik[r.id] = r @@ -456,29 +459,29 @@ class VysledkovkaCisla(Vysledkovka): @staticmethod def ne_clanek_ne_konfera(problem): inst = problem.get_real_instance() - return not (isinstance(inst, m.Clanek) or isinstance(inst, m.Konfera)) + return not (isinstance(inst, Clanek) or isinstance(inst, Konfera)) class VysledkovkaDoTeXu(VysledkovkaCisla): def __init__( self, - nejake_cislo: m.Cislo, - od_vyjma: m.Deadline, - do_vcetne: m.Deadline + nejake_cislo: Cislo, + od_vyjma: Deadline, + do_vcetne: Deadline ): super().__init__(nejake_cislo, False, do_vcetne) self.od_deadlinu = od_vyjma @cached_property - def problemy(self) -> list[m.Problem]: - return m.Problem.objects.filter(hodnoceni__in=m.Hodnoceni.objects.filter( + def problemy(self) -> list[Problem]: + return Problem.objects.filter(hodnoceni__in=Hodnoceni.objects.filter( deadline_body__deadline__gt=self.od_deadlinu.deadline, deadline_body__deadline__lte=self.do_deadlinu.deadline, )).distinct().non_polymorphic().select_related('nadproblem').select_related('nadproblem__nadproblem') @property def hodnoceni_do_cisla(self): - hodnoceni = m.Hodnoceni.objects.prefetch_related( + hodnoceni = Hodnoceni.objects.prefetch_related( 'problem', 'reseni', 'reseni__resitele') if self.jen_verejne: hodnoceni = hodnoceni.filter(deadline_body__verejna_vysledkovka=True) From 6cdc37e80b0e81b564ec3ff6150b26b233f05fe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 1 Nov 2024 12:01:24 +0100 Subject: [PATCH 22/51] =?UTF-8?q?Synchronizace=20sitetree=20s=20produkc?= =?UTF-8?q?=C3=AD=20(ne=C5=BE=20se=20v=20tom=20za=C4=8Dnu=20hrabat)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/sitetree.json | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/data/sitetree.json b/data/sitetree.json index 933c628a..9ca5e178 100644 --- a/data/sitetree.json +++ b/data/sitetree.json @@ -1007,7 +1007,18 @@ "access_guest": false, "access_loggedin": false, "access_perm_type": 1, - "access_permissions": [], + "access_permissions": [ + [ + "org", + "auth", + "user" + ], + [ + "resitel", + "auth", + "user" + ] + ], "access_restricted": true, "alias": null, "description": "", @@ -1086,7 +1097,7 @@ "access_loggedin": false, "access_perm_type": 1, "access_permissions": [], - "access_restricted": true, + "access_restricted": false, "alias": null, "description": "", "hidden": false, From 5c7710ed3afb902dca0ceb9f00e2faec565ebdec Mon Sep 17 00:00:00 2001 From: MaM Web user Date: Fri, 1 Nov 2024 12:09:04 +0100 Subject: [PATCH 23/51] =?UTF-8?q?Form=C3=A1tov=C3=A1n=C3=AD=20jsonu=20(jid?= =?UTF-8?q?=C3=A1=C5=A1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/sitetree.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/data/sitetree.json b/data/sitetree.json index 9ca5e178..61868d9e 100644 --- a/data/sitetree.json +++ b/data/sitetree.json @@ -1008,17 +1008,17 @@ "access_loggedin": false, "access_perm_type": 1, "access_permissions": [ - [ - "org", - "auth", - "user" - ], - [ - "resitel", - "auth", - "user" - ] - ], + [ + "org", + "auth", + "user" + ], + [ + "resitel", + "auth", + "user" + ] + ], "access_restricted": true, "alias": null, "description": "", @@ -1115,4 +1115,4 @@ "model": "sitetree.treeitem", "pk": 54 } -] +] \ No newline at end of file From f8b1f0978c5e8001bf65b3a95b77f8440ab5a81b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 1 Nov 2024 12:38:47 +0100 Subject: [PATCH 24/51] =?UTF-8?q?P=C5=99ejmenov=C3=A1n=C3=AD=20URLs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aesop/urls.py | 6 ++-- aesop/views.py | 4 +-- api/urls.py | 2 +- data/sitetree.json | 34 +++++++++--------- .../templates/odevzdavatko/nahraj_reseni.html | 2 +- .../nahraj_reseni_nadproblem.html | 2 +- .../templates/odevzdavatko/vloz_reseni.html | 2 +- odevzdavatko/urls.py | 8 ++--- odevzdavatko/views.py | 2 +- .../personalni/profil/orgorozcestnik.html | 2 +- .../templates/personalni/profil/resitel.html | 6 ++-- .../templates/personalni/udaje/edit.html | 2 +- .../templates/personalni/udaje/prihlaska.html | 2 +- personalni/urls.py | 6 ++-- soustredeni/models.py | 2 +- .../soustredeni/seznam_soustredeni.html | 2 +- soustredeni/urls.py | 6 ++-- treenode/urls.py | 10 +++--- tvorba/models.py | 6 ++-- tvorba/templates/tvorba/archiv/cislo.html | 4 +-- tvorba/templates/tvorba/archiv/rocnik.html | 2 +- .../tvorba/zadani/AktualniVysledkovka.html | 2 +- tvorba/urls.py | 36 +++++++++---------- tvorba/views/__init__.py | 2 +- .../templates/autentizace/login.html | 2 +- various/log_filters.py | 4 +-- various/templates/various/pracuje_se.html | 2 +- various/views/pomocne.py | 2 +- 28 files changed, 81 insertions(+), 81 deletions(-) diff --git a/aesop/urls.py b/aesop/urls.py index f39a0ba8..42122cd3 100644 --- a/aesop/urls.py +++ b/aesop/urls.py @@ -5,16 +5,16 @@ urlpatterns = [ path( 'aesop-export/mam-rocnik-.csv', views.ExportRocnikView.as_view(), - name='seminar_export_rocnik' + name='aesop_export_rocnik' ), path( 'aesop-export/mam-sous-.csv', views.ExportSousView.as_view(), - name='seminar_export_sous' + name='aesop_export_sous' ), path( 'aesop-export/index.csv', views.ExportIndexView.as_view(), - name='seminar_export_index' + name='aesop_export_index' ), ] diff --git a/aesop/views.py b/aesop/views.py index 1ff6c7ee..56a6d266 100644 --- a/aesop/views.py +++ b/aesop/views.py @@ -14,10 +14,10 @@ class ExportIndexView(generic.View): def get(self, request): ls = [] for r in Rocnik.objects.filter(exportovat = True): - url = reverse('seminar_export_rocnik', kwargs={'prvni_rok': r.prvni_rok}) + url = reverse('aesop_export_rocnik', kwargs={'prvni_rok': r.prvni_rok}) ls.append(url.split('/')[-1]) for s in Soustredeni.objects.filter(exportovat = True): - url = reverse('seminar_export_sous', kwargs={'datum_zacatku': s.datum_zacatku.isoformat()}) + url = reverse('aesop_export_sous', kwargs={'datum_zacatku': s.datum_zacatku.isoformat()}) ls.append(url.split('/')[-1]) return HttpResponse('\n'.join(ls) + '\n', content_type='text/plain; charset=utf-8') diff --git a/api/urls.py b/api/urls.py index be58d3f9..ebb89534 100644 --- a/api/urls.py +++ b/api/urls.py @@ -17,5 +17,5 @@ urlpatterns = [ # Ceka na autocomplete v3 # path('autocomplete/organizatori/', # org_member_required(views.OrganizatorAutocomplete.as_view()), - # name='seminar_autocomplete_organizator') + # name='autocomplete_organizator') ] diff --git a/data/sitetree.json b/data/sitetree.json index 61868d9e..c19462e6 100644 --- a/data/sitetree.json +++ b/data/sitetree.json @@ -73,7 +73,7 @@ "sort_order": 3, "title": "Aktuální
      ročník", "tree": 1, - "url": "seminar_aktualni_zadani", + "url": "tvorba_aktualni_zadani", "urlaspattern": true }, "model": "sitetree.treeitem", @@ -121,7 +121,7 @@ "sort_order": 5, "title": "Archiv", "tree": 1, - "url": "seminar_archiv_rocniky", + "url": "tvorba_archiv_rocniky", "urlaspattern": true }, "model": "sitetree.treeitem", @@ -289,7 +289,7 @@ "sort_order": 43, "title": "Výsledková listina", "tree": 1, - "url": "seminar_aktualni_vysledky", + "url": "tvorba_aktualni_vysledky", "urlaspattern": true }, "model": "sitetree.treeitem", @@ -361,7 +361,7 @@ "sort_order": 20, "title": "Proběhlo", "tree": 1, - "url": "seminar_seznam_soustredeni", + "url": "soustredeni_probehlo", "urlaspattern": true }, "model": "sitetree.treeitem", @@ -409,7 +409,7 @@ "sort_order": 23, "title": "Osobní údaje", "tree": 1, - "url": "seminar_resitel_edit", + "url": "personalni_resitel_edit", "urlaspattern": true }, "model": "sitetree.treeitem", @@ -439,7 +439,7 @@ "sort_order": 36, "title": "Nahrát řešení", "tree": 1, - "url": "seminar_nahraj_reseni", + "url": "odevzdavatko_nahraj_reseni", "urlaspattern": true }, "model": "sitetree.treeitem", @@ -463,7 +463,7 @@ "sort_order": 35, "title": "Témata", "tree": 1, - "url": "seminar_archiv_temata", + "url": "tvorba_archiv_temata", "urlaspattern": true }, "model": "sitetree.treeitem", @@ -589,7 +589,7 @@ "sort_order": 15, "title": "Aktuální číslo", "tree": 1, - "url": "seminar_aktualni_zadani", + "url": "tvorba_aktualni_zadani", "urlaspattern": true }, "model": "sitetree.treeitem", @@ -613,7 +613,7 @@ "sort_order": 24, "title": "Čísla", "tree": 1, - "url": "seminar_archiv_rocniky", + "url": "tvorba_archiv_rocniky", "urlaspattern": true }, "model": "sitetree.treeitem", @@ -721,7 +721,7 @@ "sort_order": 36, "title": "Vložit řešení", "tree": 1, - "url": "seminar_vloz_reseni", + "url": "odevzdavatko_vloz_reseni", "urlaspattern": true }, "model": "sitetree.treeitem", @@ -804,7 +804,7 @@ "sort_order": 37, "title": "Moje řešení", "tree": 1, - "url": "seminar_resitel_odevzdana_reseni", + "url": "odevzdavatko_resitel_odevzdana_reseni", "urlaspattern": true }, "model": "sitetree.treeitem", @@ -828,7 +828,7 @@ "sort_order": 33, "title": "Aktuální ročník", "tree": 1, - "url": "seminar_aktualni_rocnik", + "url": "tvorba_aktualni_rocnik", "urlaspattern": true }, "model": "sitetree.treeitem", @@ -900,7 +900,7 @@ "sort_order": 46, "title": "Ročník {{rocnik.rocnik}}", "tree": 1, - "url": "seminar_rocnik rocnik.rocnik", + "url": "tvorba_rocnik rocnik.rocnik", "urlaspattern": true }, "model": "sitetree.treeitem", @@ -924,7 +924,7 @@ "sort_order": 47, "title": "Číslo {{ cislo.rocnik.rocnik }}.{{ cislo.poradi }}", "tree": 1, - "url": "seminar_cislo cislo.rocnik.rocnik cislo.poradi", + "url": "tvorba_cislo cislo.rocnik.rocnik cislo.poradi", "urlaspattern": true }, "model": "sitetree.treeitem", @@ -1061,7 +1061,7 @@ "sort_order": 52, "title": "Nahrát řešení k nadproblému {{nadproblem_id}}", "tree": 1, - "url": "seminar_nahraj_reseni nadproblem_id", + "url": "odevzdavatko_nahraj_reseni nadproblem_id", "urlaspattern": true }, "model": "sitetree.treeitem", @@ -1109,10 +1109,10 @@ "sort_order": 54, "title": "Export do abstraktů sousu {{ soustredeni.id }}", "tree": 1, - "url": "seminar_soustredeni_abstrakty soustredeni.id", + "url": "soustredeni_abstrakty soustredeni.id", "urlaspattern": true }, "model": "sitetree.treeitem", "pk": 54 } -] \ No newline at end of file +] diff --git a/odevzdavatko/templates/odevzdavatko/nahraj_reseni.html b/odevzdavatko/templates/odevzdavatko/nahraj_reseni.html index ca326d67..6b8fee05 100644 --- a/odevzdavatko/templates/odevzdavatko/nahraj_reseni.html +++ b/odevzdavatko/templates/odevzdavatko/nahraj_reseni.html @@ -11,7 +11,7 @@ {% endblock %}

-
+ {% csrf_token %} diff --git a/odevzdavatko/templates/odevzdavatko/nahraj_reseni_nadproblem.html b/odevzdavatko/templates/odevzdavatko/nahraj_reseni_nadproblem.html index ccf505fa..0c8e1496 100644 --- a/odevzdavatko/templates/odevzdavatko/nahraj_reseni_nadproblem.html +++ b/odevzdavatko/templates/odevzdavatko/nahraj_reseni_nadproblem.html @@ -12,7 +12,7 @@
    {% for problem in object_list %} -
  • {{ problem }}
  • +
  • {{ problem }}
  • {% empty %}
  • Nelze nic odevzdávat.
  • {% endfor %} diff --git a/odevzdavatko/templates/odevzdavatko/vloz_reseni.html b/odevzdavatko/templates/odevzdavatko/vloz_reseni.html index 27827015..6ae8c13d 100644 --- a/odevzdavatko/templates/odevzdavatko/vloz_reseni.html +++ b/odevzdavatko/templates/odevzdavatko/vloz_reseni.html @@ -12,7 +12,7 @@ Vložit řešení {% endblock %} - + {% csrf_token %} {{form.as_p}} diff --git a/odevzdavatko/urls.py b/odevzdavatko/urls.py index d4c2a092..801709b6 100644 --- a/odevzdavatko/urls.py +++ b/odevzdavatko/urls.py @@ -5,10 +5,10 @@ from various.views.generic import viewMethodSwitch from . import views urlpatterns = [ - path('org/add_solution', org_required(views.VlozReseniView.as_view()), name='seminar_vloz_reseni'), - path('resitel/nahraj_reseni', resitel_required(views.NahrajReseniRozcestnikTematekView.as_view()), name='seminar_nahraj_reseni'), - path('resitel/nahraj_reseni//', resitel_required(views.NahrajReseniView.as_view()), name='seminar_nahraj_reseni'), - path('resitel/odevzdana_reseni/', resitel_or_org_required(views.PrehledOdevzdanychReseni.as_view()), name='seminar_resitel_odevzdana_reseni'), + path('org/add_solution', org_required(views.VlozReseniView.as_view()), name='odevzdavatko_vloz_reseni'), + path('resitel/nahraj_reseni', resitel_required(views.NahrajReseniRozcestnikTematekView.as_view()), name='odevzdavatko_nahraj_reseni'), + path('resitel/nahraj_reseni//', resitel_required(views.NahrajReseniView.as_view()), name='odevzdavatko_nahraj_reseni'), + path('resitel/odevzdana_reseni/', resitel_or_org_required(views.PrehledOdevzdanychReseni.as_view()), name='odevzdavatko_resitel_odevzdana_reseni'), path('org/reseni/', org_required(views.TabulkaOdevzdanychReseniView.as_view()), name='odevzdavatko_tabulka'), path('org/reseni/rocnik//', org_required(views.TabulkaOdevzdanychReseniView.as_view()), name='odevzdavatko_tabulka'), diff --git a/odevzdavatko/views.py b/odevzdavatko/views.py index 1a294c4e..652570e0 100644 --- a/odevzdavatko/views.py +++ b/odevzdavatko/views.py @@ -516,5 +516,5 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): return formularOKView( self.request, text='Řešení úspěšně odevzdáno', - dalsi_odkazy=[("Odevzdat další řešení", reverse("seminar_nahraj_reseni"))], + dalsi_odkazy=[("Odevzdat další řešení", reverse("odevzdavatko_nahraj_reseni"))], ) diff --git a/personalni/templates/personalni/profil/orgorozcestnik.html b/personalni/templates/personalni/profil/orgorozcestnik.html index 9d0bbdba..27071b66 100644 --- a/personalni/templates/personalni/profil/orgorozcestnik.html +++ b/personalni/templates/personalni/profil/orgorozcestnik.html @@ -78,7 +78,7 @@
  • hlasování o přednáškách
-
  • proběhlá soustředění +
  • proběhlá soustředění
    • vytvoření galerie
    • stažení seznamu účastníků
    • diff --git a/personalni/templates/personalni/profil/resitel.html b/personalni/templates/personalni/profil/resitel.html index 0bd92d63..f7abc1ca 100644 --- a/personalni/templates/personalni/profil/resitel.html +++ b/personalni/templates/personalni/profil/resitel.html @@ -10,9 +10,9 @@ Odhlásit se
      -Upravit údaje
      -Nahrát řešení
      -Již odevzdaná řešení
      +Upravit údaje
      +Nahrát řešení
      +Již odevzdaná řešení
      {% endblock %} diff --git a/personalni/templates/personalni/udaje/edit.html b/personalni/templates/personalni/udaje/edit.html index 085e4246..883e8e4c 100644 --- a/personalni/templates/personalni/udaje/edit.html +++ b/personalni/templates/personalni/udaje/edit.html @@ -11,7 +11,7 @@

      Změnit heslo

      - + {% include "personalni/udaje/udaje.html"%} diff --git a/personalni/templates/personalni/udaje/prihlaska.html b/personalni/templates/personalni/udaje/prihlaska.html index f26936de..af0a16c9 100644 --- a/personalni/templates/personalni/udaje/prihlaska.html +++ b/personalni/templates/personalni/udaje/prihlaska.html @@ -14,7 +14,7 @@

      Tučně popsaná pole jsou povinná.

      -
      + {% include "personalni/udaje/udaje.html" %}

      GDPR diff --git a/personalni/urls.py b/personalni/urls.py index 8abbb434..c4820b90 100644 --- a/personalni/urls.py +++ b/personalni/urls.py @@ -7,15 +7,15 @@ urlpatterns = [ path( 'org/rozcestnik/', org_required(views.OrgoRozcestnikView.as_view()), - name='seminar_org_rozcestnik' + name='personalni_org_rozcestnik' ), - path('prihlaska/', views.prihlaskaView, name='seminar_prihlaska'), + path('prihlaska/', views.prihlaskaView, name='personalni_prihlaska'), path( 'resitel/osobni-udaje/', login_required(views.resitelEditView), - name='seminar_resitel_edit' + name='personalni_resitel_edit' ), # Obecný view na profil -- orgům dá rozcestník, řešitelům jejich stránku diff --git a/soustredeni/models.py b/soustredeni/models.py index a86992f5..509c3cc2 100644 --- a/soustredeni/models.py +++ b/soustredeni/models.py @@ -75,7 +75,7 @@ class Soustredeni(SeminarModelBase): def verejne_url(self): #return reverse('seminar_soustredeni', kwargs={'pk': self.id}) - return reverse('seminar_seznam_soustredeni') + return reverse('soustredeni_probehlo') @reversion.register(ignore_duplicates=True) diff --git a/soustredeni/templates/soustredeni/seznam_soustredeni.html b/soustredeni/templates/soustredeni/seznam_soustredeni.html index ac33852a..fb79a7a0 100644 --- a/soustredeni/templates/soustredeni/seznam_soustredeni.html +++ b/soustredeni/templates/soustredeni/seznam_soustredeni.html @@ -40,7 +40,7 @@
      Vytvořit novou fotogalerii
      Vygenerovat obálky pro účastníky
      - Vygenerovat účastníky a vedoucí do abstraktů
      + Vygenerovat účastníky a vedoucí do abstraktů
      Seznam účastníků - HTML tabulka pro tisk, CSV, diff --git a/soustredeni/urls.py b/soustredeni/urls.py index 0daae321..3ef7edae 100644 --- a/soustredeni/urls.py +++ b/soustredeni/urls.py @@ -7,7 +7,7 @@ urlpatterns = [ path( 'probehlo/', views.SoustredeniListView.as_view(), - name='seminar_seznam_soustredeni' + name='soustredeni_probehlo' ), path( '/', @@ -37,12 +37,12 @@ urlpatterns = [ path( 'obalky.pdf', org_required(views.soustredeniObalkyView), - name='seminar_soustredeni_obalky' + name='soustredeni_obalky' ), path( 'abstrakty', org_required(views.SoustredeniAbstraktyView.as_view()), - name='seminar_soustredeni_abstrakty' + name='soustredeni_abstrakty' ), path( 'fotogalerie/', diff --git a/treenode/urls.py b/treenode/urls.py index 60dc88ad..347fd275 100644 --- a/treenode/urls.py +++ b/treenode/urls.py @@ -2,16 +2,16 @@ from django.urls import path, re_path from . import views urlpatterns = [ - #path('treenode//', views.TreeNodeView.as_view(), name='seminar_treenode'), - #path('treenode//json/', views.TreeNodeJSONView.as_view(), name='seminar_treenode_json'), - #path('treenode/text//', views.TextWebView.as_view(), name='seminar_textnode_web'), + #path('treenode//', views.TreeNodeView.as_view(), name='treenode'), + #path('treenode//json/', views.TreeNodeJSONView.as_view(), name='treenode_json'), + #path('treenode/text//', views.TextWebView.as_view(), name='treenode_textnode_web'), #path('treenode/editor/pridat////', views.TreeNodePridatView.as_view(), name='treenode_pridat'), #path('treenode/editor/smazat//', views.TreeNodeSmazatView.as_view(), name='treenode_smazat'), #path('treenode/editor/odvesitpryc//', views.TreeNodeOdvesitPrycView.as_view(), name='treenode_odvesitpryc'), #path('treenode/editor/podvesit///', views.TreeNodePodvesitView.as_view(), name='treenode_podvesit'), #path('treenode/editor/prohodit//', views.TreeNodeProhoditView.as_view(), name='treenode_prohodit'), - #path('treenode/sirotcinec/', views.SirotcinecView.as_view(), name='seminar_treenode_sirotcinec'), - #path('problem/(?P\d+)/(?P\d+)/', views.PrispevekView.as_view(), name='seminar_problem_prispevek'), + #path('treenode/sirotcinec/', views.SirotcinecView.as_view(), name='treenode_sirotcinec'), + #path('problem/(?P\d+)/(?P\d+)/', views.PrispevekView.as_view(), name='treenode_problem_prispevek'), re_path(r'^temp/vue/.*$',views.VueTestView.as_view(),name='vue_test_view'), path('temp/image_upload/', views.NahrajObrazekKTreeNoduView.as_view()), diff --git a/tvorba/models.py b/tvorba/models.py index d7290ae3..c26fc9f0 100644 --- a/tvorba/models.py +++ b/tvorba/models.py @@ -96,7 +96,7 @@ class Rocnik(SeminarModelBase): return self.prvni_rok + 1 def verejne_url(self): - return reverse('seminar_rocnik', kwargs={'rocnik': self.rocnik}) + return reverse('tvorba_rocnik', kwargs={'rocnik': self.rocnik}) @classmethod def cached_rocnik(cls, r_id): @@ -171,7 +171,7 @@ class Cislo(SeminarModelBase): verejne.boolean = True def verejne_url(self): - return reverse('seminar_cislo', kwargs={'rocnik': self.rocnik.rocnik, 'cislo': self.poradi}) + return reverse('tvorba_cislo', kwargs={'rocnik': self.rocnik.rocnik, 'cislo': self.poradi}) def absolute_url(self): return "https://" + str(get_current_site(None)) + self.verejne_url() @@ -508,7 +508,7 @@ class Problem(SeminarModelBase,PolymorphicModel): # verejne.boolean = True def verejne_url(self): - return reverse('seminar_problem', kwargs={'pk': self.id}) + return reverse('tvorba_problem', kwargs={'pk': self.id}) def admin_url(self): return reverse('admin:tvorba_problem_change', args=(self.id, )) diff --git a/tvorba/templates/tvorba/archiv/cislo.html b/tvorba/templates/tvorba/archiv/cislo.html index ca8f4714..f3394e6c 100644 --- a/tvorba/templates/tvorba/archiv/cislo.html +++ b/tvorba/templates/tvorba/archiv/cislo.html @@ -39,8 +39,8 @@
    • Tituly (TeX, 2. deadline předchozího čísla a 1.deadline tohoto)
    • Výsledkovka (TeX, 2. deadline předchozího čísla a 1.deadline tohoto)
    • Odměny
    • -
    • Tituly do závěrečného čísla (TeX, 2. deadline předchozího čísla a oba tohoto)
    • -
    • Výsledkovka závěrečného čísla ročníku (TeX, 2. deadline předchozího čísla a oba tohoto)
    • +
    • Tituly do závěrečného čísla (TeX, 2. deadline předchozího čísla a oba tohoto)
    • +
    • Výsledkovka závěrečného čísla ročníku (TeX, 2. deadline předchozího čísla a oba tohoto)
    {% endif %} diff --git a/tvorba/templates/tvorba/archiv/rocnik.html b/tvorba/templates/tvorba/archiv/rocnik.html index 77141998..6792fb93 100644 --- a/tvorba/templates/tvorba/archiv/rocnik.html +++ b/tvorba/templates/tvorba/archiv/rocnik.html @@ -121,7 +121,7 @@

    Výsledkovka ročníku (LaTeX, včetně neveřejných)

    Tituly (TeX, včetně neveřejných, všechny, nevhodné do mamtexu)

    {# FIXME: Sice to sem asi nepatří sémanticky, ale bylo to nejjednodušší… #} -

    CSV export řešitelů

    +

    CSV export řešitelů

    Výsledková listina včetně neveřejných bodů

    {% include "vysledkovky/vysledkovka_rocnik.html" with vysledkovka=vysledkovka_neverejna %} diff --git a/tvorba/templates/tvorba/zadani/AktualniVysledkovka.html b/tvorba/templates/tvorba/zadani/AktualniVysledkovka.html index 640600f3..9d39b03d 100644 --- a/tvorba/templates/tvorba/zadani/AktualniVysledkovka.html +++ b/tvorba/templates/tvorba/zadani/AktualniVysledkovka.html @@ -17,7 +17,7 @@

    Staré výsledkové listiny najdete - v archivu. + v archivu.

    {% if user.je_org and vysledkovka_s_neverejnymi %} diff --git a/tvorba/urls.py b/tvorba/urls.py index e662491c..950de6cc 100644 --- a/tvorba/urls.py +++ b/tvorba/urls.py @@ -7,19 +7,19 @@ urlpatterns = [ # path('/t/', views.TematkoView), # Archiv - path('archiv/rocniky/', views.ArchivView.as_view(), name="seminar_archiv_rocniky"), - path('archiv/temata/', views.ArchivTemataView.as_view(), name="seminar_archiv_temata"), + path('archiv/rocniky/', views.ArchivView.as_view(), name="tvorba_archiv_rocniky"), + path('archiv/temata/', views.ArchivTemataView.as_view(), name="tvorba_archiv_temata"), - path('rocnik//', views.RocnikView.as_view(), name='seminar_rocnik'), - path('cislo/./', views.CisloView.as_view(), name='seminar_cislo'), - path('problem//', views.problemView, name='seminar_problem'), + path('rocnik//', views.RocnikView.as_view(), name='tvorba_rocnik'), + path('cislo/./', views.CisloView.as_view(), name='tvorba_cislo'), + path('problem//', views.problemView, name='tvorba_problem'), # Zadani -# path('aktualni/zadani/', views.AktualniZadaniView.as_view(), name='seminar_aktualni_zadani'), # Dočasně ad-hoc jednoduchá věc. - path('aktualni/zadani/', views.AktualniZadaniView, name='seminar_aktualni_zadani'), - #path('aktualni/temata/', views.ZadaniTemataView, name='seminar_temata'), - path('aktualni/vysledkova-listina/', views.ZadaniAktualniVysledkovkaView, name='seminar_aktualni_vysledky'), - path('aktualni/rocnik/', views.AktualniRocnikRedirectView.as_view(), name='seminar_aktualni_rocnik'), +# path('aktualni/zadani/', views.AktualniZadaniView.as_view(), name='tvorba_aktualni_zadani'), # Dočasně ad-hoc jednoduchá věc. + path('aktualni/zadani/', views.AktualniZadaniView, name='tvorba_aktualni_zadani'), + #path('aktualni/temata/', views.ZadaniTemataView, name='tvorba_temata'), + path('aktualni/vysledkova-listina/', views.ZadaniAktualniVysledkovkaView, name='tvorba_aktualni_vysledky'), + path('aktualni/rocnik/', views.AktualniRocnikRedirectView.as_view(), name='tvorba_aktualni_rocnik'), # Clanky path('archiv/clanky/', views.ClankyResitelView.as_view(), name='clanky_resitel'), @@ -29,42 +29,42 @@ urlpatterns = [ path( 'rocnik//vysledkovka.tex', org_required(views.RocnikVysledkovkaView.as_view()), - name='seminar_rocnik_vysledkovka' + name='tvorba_rocnik_vysledkovka' ), path( 'rocnik//resitele.csv', org_required(views.resiteleRocnikuCsvExportView), - name='seminar_rocnik_resitele_csv' + name='tvorba_rocnik_resitele_csv' ), path( 'rocnik//tituly.tex', org_required(views.TitulyViewRocnik), - name='seminar_rocnik_titul' + name='tvorba_rocnik_titul' ), path( 'rocnik//posledni_vysledkovka.tex', org_required(views.PosledniCisloVysledkovkaView.as_view()), - name='seminar_rocnik_posledni_vysledkovka' + name='tvorba_rocnik_posledni_vysledkovka' ), path( 'cislo/./vysledkovka.tex', org_required(views.CisloVysledkovkaView.as_view()), - name='seminar_cislo_vysledkovka' + name='tvorba_cislo_vysledkovka' ), path( 'cislo/./obalky.pdf', org_required(views.cisloObalkyView), - name='seminar_cislo_obalky' + name='tvorba_cislo_obalky' ), path( 'cislo/./tituly.tex', org_required(views.TitulyView), - name='seminar_cislo_titul' + name='tvorba_cislo_titul' ), path( 'cislo/./odmeny/./', org_required(views.OdmenyView.as_view()), - name="seminar_archiv_odmeny"), + name="tvorba_archiv_odmeny"), # Dočasné & neodladěné: path( diff --git a/tvorba/views/__init__.py b/tvorba/views/__init__.py index e45eaaa6..2bef3591 100644 --- a/tvorba/views/__init__.py +++ b/tvorba/views/__init__.py @@ -576,7 +576,7 @@ class ClankyResitelView(generic.ListView): class AktualniRocnikRedirectView(RedirectView): permanent=False - pattern_name = 'seminar_rocnik' + pattern_name = 'tvorba_rocnik' def get_redirect_url(self, *args, **kwargs): aktualni_rocnik = Nastaveni.get_solo().aktualni_rocnik.rocnik diff --git a/various/autentizace/templates/autentizace/login.html b/various/autentizace/templates/autentizace/login.html index 2dbabcc4..76a14f9f 100644 --- a/various/autentizace/templates/autentizace/login.html +++ b/various/autentizace/templates/autentizace/login.html @@ -33,7 +33,7 @@ Ješte nejsi zaregistrován? - + diff --git a/various/log_filters.py b/various/log_filters.py index f037dcde..d6d7fc63 100644 --- a/various/log_filters.py +++ b/various/log_filters.py @@ -12,8 +12,8 @@ class StripSensitiveFormDataFilter(Filter): if hasattr(record, 'request') and record.request.path in [ reverse('login'), reverse('logout'), - reverse('seminar_prihlaska'), - reverse('seminar_resitel_edit'), + reverse('personalni_prihlaska'), + reverse('personalni_resitel_edit'), reverse('reset_password'), reverse('reset_password_done'), # FIXME diff --git a/various/templates/various/pracuje_se.html b/various/templates/various/pracuje_se.html index 1a396534..e7f707e4 100644 --- a/various/templates/various/pracuje_se.html +++ b/various/templates/various/pracuje_se.html @@ -11,7 +11,7 @@ Na této stránce velmi intenzivně pracujeme. Za dočasnou nedostupnost se omlouváme. Zkuste přejít na titulní stránku - nebo se podívat na aktuální zadání. + nebo se podívat na aktuální zadání.

    {% endblock %} diff --git a/various/views/pomocne.py b/various/views/pomocne.py index e25ed9df..61f1d630 100644 --- a/various/views/pomocne.py +++ b/various/views/pomocne.py @@ -17,7 +17,7 @@ def formularOKView(request, text='', dalsi_odkazy: Sequence[tuple[str, str]] = ( odkazy = list(dalsi_odkazy) + [ # (Text, odkaz) ('Vrátit se na titulní stránku', reverse('titulni_strana')), - ('Zobrazit aktuální zadání', reverse('seminar_aktualni_zadani')), + ('Zobrazit aktuální zadání', reverse('tvorba_aktualni_zadani')), ] context = { 'odkazy': odkazy, From 28fef9a3938767da98b8031e3e0fde5fac2d123f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 1 Nov 2024 12:58:02 +0100 Subject: [PATCH 25/51] =?UTF-8?q?Pojmenovan=C3=A9=20URL=20m=C3=ADsto=20rel?= =?UTF-8?q?ativn=C3=ADch=20URL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tvorba/templates/tvorba/archiv/cislo.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tvorba/templates/tvorba/archiv/cislo.html b/tvorba/templates/tvorba/archiv/cislo.html index f3394e6c..b33f83de 100644 --- a/tvorba/templates/tvorba/archiv/cislo.html +++ b/tvorba/templates/tvorba/archiv/cislo.html @@ -35,10 +35,10 @@

    Orgovské odkazy

    From eb6eb2d6fbc18bc9c22a4461cc1d58e43ebb5e4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 1 Nov 2024 13:17:39 +0100 Subject: [PATCH 26/51] =?UTF-8?q?P=C5=99ejmenov=C3=A1n=C3=AD=20logger?= =?UTF-8?q?=C5=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mamweb/settings_common.py | 4 ++-- personalni/forms.py | 4 ++-- personalni/views.py | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mamweb/settings_common.py b/mamweb/settings_common.py index 743dbead..19616ac1 100644 --- a/mamweb/settings_common.py +++ b/mamweb/settings_common.py @@ -279,11 +279,11 @@ LOGGING = { 'filters': ['Http404AsInfo'], }, - 'seminar.prihlaska.form':{ + 'personalni.prihlaska.form':{ 'handlers': ['console','registration_logfile'], 'level': 'INFO' }, - 'seminar.prihlaska.problem':{ + 'personalni.prihlaska.problem':{ 'handlers': ['console','mail_registration','registration_error_log'], 'level': 'INFO' }, diff --git a/personalni/forms.py b/personalni/forms.py index 57e1b398..50b9d705 100644 --- a/personalni/forms.py +++ b/personalni/forms.py @@ -27,7 +27,7 @@ class TelInput(forms.TextInput): class UdajeForm(forms.Form): username = None - err_logger = logging.getLogger('seminar.prihlaska.problem') + err_logger = logging.getLogger('personalni.prihlaska.problem') jmeno = forms.CharField(label='Jméno', max_length=256, required=True) prezdivka_resitele = forms.CharField(label='Přezdívka (veřejná)', max_length=256, required=False) @@ -147,7 +147,7 @@ class PrihlaskaForm(PasswordResetForm, UdajeForm): class ProfileEditForm(UdajeForm): - err_logger = logging.getLogger('seminar.edit.problem') + err_logger = logging.getLogger('personalni.edit.problem') username = forms.CharField( label='Přihlašovací jméno', max_length=256, diff --git a/personalni/views.py b/personalni/views.py index 9e29d5b7..7c95325c 100644 --- a/personalni/views.py +++ b/personalni/views.py @@ -161,7 +161,7 @@ def prihlaska_log_gdpr_safe(logger, gdpr_logger, msg, form_data): @sensitive_post_parameters('jmeno', 'prijmeni', 'email', 'telefon', 'datum_narozeni', 'ulice', 'mesto', 'psc', 'skola') def resitelEditView(request): - err_logger = logging.getLogger('seminar.prihlaska.problem') + err_logger = logging.getLogger('personalni.prihlaska.problem') ## Načtení objektů Osoba a Resitel patřících k aktuálně přihlášenému uživateli u = request.user osoba_edit = m.Osoba.objects.get(user=u) @@ -199,7 +199,7 @@ def resitelEditView(request): ## Změny v osobě fcd = form.cleaned_data form_hash = hash(frozenset(fcd.items())) - form_logger = logging.getLogger('seminar.prihlaska.form') + form_logger = logging.getLogger('personalni.prihlaska.form') form_logger.info("EDIT:" + str(fcd) + str(form_hash)) # TODO možná logovat jinak osoba_edit.jmeno = fcd['jmeno'] osoba_edit.prijmeni = fcd['prijmeni'] @@ -248,9 +248,9 @@ def resitelEditView(request): @sensitive_post_parameters('jmeno', 'prijmeni', 'email', 'telefon', 'datum_narozeni', 'ulice', 'mesto', 'psc', 'skola', 'jak_se_dozvedeli') def prihlaskaView(request): - generic_logger = logging.getLogger('seminar.prihlaska') - err_logger = logging.getLogger('seminar.prihlaska.problem') - form_logger = logging.getLogger('seminar.prihlaska.form') + generic_logger = logging.getLogger('personalni.prihlaska') + err_logger = logging.getLogger('personalni.prihlaska.problem') + form_logger = logging.getLogger('personalni.prihlaska.form') if request.method == 'POST': form = PrihlaskaForm(request.POST) # TODO vyresit, co se bude v jakych situacich zobrazovat From 9ca59672618cfea69e30ca1977739e1c0381e69d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 1 Nov 2024 13:20:28 +0100 Subject: [PATCH 27/51] =?UTF-8?q?P=C5=99esun=20vue=20(zat=C3=ADm)=20do=20t?= =?UTF-8?q?reenod=C5=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- make/push_compiled_vue_to_test | 2 +- vue_frontend/vue.config.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/make/push_compiled_vue_to_test b/make/push_compiled_vue_to_test index 99495a7b..0241074c 100755 --- a/make/push_compiled_vue_to_test +++ b/make/push_compiled_vue_to_test @@ -4,7 +4,7 @@ set -exuo pipefail . make/lib.sh scp vue_frontend/webpack-stats.json "$GIMLI_LOGIN:$TESTWEB/vue_frontend/" -rsync -ave ssh seminar/static/seminar/vue "$GIMLI_LOGIN:$TESTWEB/seminar/static/seminar/" +rsync -ave ssh treenode/static/treenode/vue "$GIMLI_LOGIN:$TESTWEB/treenode/static/treenode/" ssh "$GIMLI_LOGIN" " set -euxo pipefail cd $TESTWEB diff --git a/vue_frontend/vue.config.js b/vue_frontend/vue.config.js index e4fc61ce..afe78eee 100644 --- a/vue_frontend/vue.config.js +++ b/vue_frontend/vue.config.js @@ -17,9 +17,9 @@ module.exports = { filenameHashing: false, productionSourceMap: true, publicPath: process.env.NODE_ENV === 'production' - ? '/static/seminar/vue/' + ? '/static/treenode/vue/' : 'http://localhost:8080/', - outputDir: '../seminar/static/seminar/vue/', + outputDir: '../treenode/static/treenode/vue/', chainWebpack: config => { From 07d1505e2a62b654da3da2e9ae91ad6c405f7588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 1 Nov 2024 13:50:48 +0100 Subject: [PATCH 28/51] =?UTF-8?q?Odst=C5=99el=20(import=C5=AF)=20treenod?= =?UTF-8?q?=C5=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/models/odevzdavatko.py | 2 +- treenode/admin.py | 73 +++++++++--------- treenode/forms.py | 4 +- treenode/models/__init__.py | 2 + treenode/models/pomocne.py | 1 + treenode/serializers.py | 132 +++++++++++++++++---------------- treenode/templatetags.py | 34 +++++---- treenode/tests.py | 12 +-- treenode/treelib.py | 6 +- treenode/views.py | 82 ++++++++++---------- treenode/viewsets.py | 25 ++++--- tvorba/models.py | 6 +- tvorba/testutils.py | 80 ++++++++++---------- tvorba/views/__init__.py | 8 +- various/views/final.py | 5 +- 15 files changed, 246 insertions(+), 226 deletions(-) create mode 100644 treenode/models/__init__.py create mode 100644 treenode/models/pomocne.py diff --git a/seminar/models/odevzdavatko.py b/seminar/models/odevzdavatko.py index efc88e74..6079a708 100644 --- a/seminar/models/odevzdavatko.py +++ b/seminar/models/odevzdavatko.py @@ -1,6 +1,6 @@ from django.db import models -from seminar.models import treenode as tm +from treenode import models as tm from odevzdavatko.models import Reseni class ReseniNode(tm.TreeNode): diff --git a/treenode/admin.py b/treenode/admin.py index 8ffe4fc8..6f3bfadb 100644 --- a/treenode/admin.py +++ b/treenode/admin.py @@ -4,25 +4,26 @@ from django.forms import widgets from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter -import seminar.models as m +from .models import TreeNode, RocnikNode, CisloNode, MezicisloNode, TemaVCisleNode, UlohaZadaniNode, PohadkaNode, UlohaVzorakNode, TextNode, CastNode, OrgTextNode +from .models.pomocne import Text, Obrazek # Polymorfismus pro stromy # TODO: Inlines podle https://django-polymorphic.readthedocs.io/en/stable/admin.html -@admin.register(m.TreeNode) +@admin.register(TreeNode) class TreeNodeAdmin(PolymorphicParentModelAdmin): - base_model = m.TreeNode + base_model = TreeNode child_models = [ - m.RocnikNode, - m.CisloNode, - m.MezicisloNode, - m.TemaVCisleNode, - m.UlohaZadaniNode, - m.PohadkaNode, - m.UlohaVzorakNode, - m.TextNode, - m.CastNode, - m.OrgTextNode, + RocnikNode, + CisloNode, + MezicisloNode, + TemaVCisleNode, + UlohaZadaniNode, + PohadkaNode, + UlohaVzorakNode, + TextNode, + CastNode, + OrgTextNode, ] actions = ['aktualizuj_nazvy'] @@ -36,64 +37,64 @@ class TreeNodeAdmin(PolymorphicParentModelAdmin): self.message_user(request, "Názvy aktualizovány.") aktualizuj_nazvy.short_description = "Aktualizuj vybraným TreeNodům názvy" -@admin.register(m.RocnikNode) +@admin.register(RocnikNode) class RocnikNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.RocnikNode + base_model = RocnikNode show_in_index = True -@admin.register(m.CisloNode) +@admin.register(CisloNode) class CisloNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.CisloNode + base_model = CisloNode show_in_index = True -@admin.register(m.MezicisloNode) +@admin.register(MezicisloNode) class MezicisloNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.MezicisloNode + base_model = MezicisloNode show_in_index = True -@admin.register(m.TemaVCisleNode) +@admin.register(TemaVCisleNode) class TemaVCisleNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.TemaVCisleNode + base_model = TemaVCisleNode show_in_index = True -@admin.register(m.UlohaZadaniNode) +@admin.register(UlohaZadaniNode) class UlohaZadaniNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.UlohaZadaniNode + base_model = UlohaZadaniNode show_in_index = True -@admin.register(m.PohadkaNode) +@admin.register(PohadkaNode) class PohadkaNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.PohadkaNode + base_model = PohadkaNode show_in_index = True -@admin.register(m.UlohaVzorakNode) +@admin.register(UlohaVzorakNode) class UlohaVzorakNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.UlohaVzorakNode + base_model = UlohaVzorakNode show_in_index = True -@admin.register(m.TextNode) +@admin.register(TextNode) class TextNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.TextNode + base_model = TextNode show_in_index = True -@admin.register(m.CastNode) +@admin.register(CastNode) class TextNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.CastNode + base_model = CastNode show_in_index = True fields = ('nadpis',) -@admin.register(m.OrgTextNode) +@admin.register(OrgTextNode) class TextNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.OrgTextNode + base_model = OrgTextNode show_in_index = True class TextAdminInline(admin.TabularInline): - model = m.Text + model = Text formfield_overrides = { models.TextField: {'widget': widgets.TextInput} } exclude = ['text_zkraceny_set', 'text_zkraceny'] -admin.site.register(m.Text) -admin.site.register(m.Obrazek) +admin.site.register(Text) +admin.site.register(Obrazek) diff --git a/treenode/forms.py b/treenode/forms.py index 704084f7..61243720 100644 --- a/treenode/forms.py +++ b/treenode/forms.py @@ -1,5 +1,5 @@ from django import forms -import seminar.models as m +from .models.pomocne import Obrazek # pro přidání políčka do formuláře je potřeba # - mít v modelu tu položku, kterou chci upravovat @@ -10,5 +10,5 @@ import seminar.models as m class NahrajObrazekKTreeNoduForm(forms.ModelForm): class Meta: - model = m.Obrazek + model = Obrazek fields = ('na_web',) diff --git a/treenode/models/__init__.py b/treenode/models/__init__.py new file mode 100644 index 00000000..f068a819 --- /dev/null +++ b/treenode/models/__init__.py @@ -0,0 +1,2 @@ +from seminar.models.odevzdavatko import * +from seminar.models.treenode import * diff --git a/treenode/models/pomocne.py b/treenode/models/pomocne.py new file mode 100644 index 00000000..6592da81 --- /dev/null +++ b/treenode/models/pomocne.py @@ -0,0 +1 @@ +from seminar.models.pomocne import * diff --git a/treenode/serializers.py b/treenode/serializers.py index eedb03b1..d26964d1 100644 --- a/treenode/serializers.py +++ b/treenode/serializers.py @@ -1,7 +1,11 @@ from rest_framework import serializers from rest_polymorphic.serializers import PolymorphicSerializer -import seminar.models as m +from odevzdavatko.models import Reseni +from tvorba.models import Problem, Uloha + +from .models import RocnikNode, CisloNode, MezicisloNode, TemaVCisleNode, OrgTextNode, PohadkaNode, TextNode, TreeNode, CastNode, UlohaZadaniNode, UlohaVzorakNode, ReseniNode +from .models.pomocne import Text from treenode import treelib DEFAULT_NODE_DEPTH = 2 @@ -9,57 +13,57 @@ DEFAULT_NODE_DEPTH = 2 class TextSerializer(serializers.ModelSerializer): class Meta: - model = m.Text + model = Text fields = '__all__' class ProblemSerializer(serializers.ModelSerializer): class Meta: - model = m.Problem + model = Problem fields = '__all__' class UlohaSerializer(serializers.ModelSerializer): class Meta: - model = m.Uloha + model = Uloha fields = '__all__' class ReseniSerializer(serializers.ModelSerializer): class Meta: - model = m.Reseni + model = Reseni fields = '__all__' class RocnikNodeSerializer(serializers.ModelSerializer): class Meta: - model = m.RocnikNode + model = RocnikNode fields = '__all__' depth = DEFAULT_NODE_DEPTH class CisloNodeSerializer(serializers.ModelSerializer): class Meta: - model = m.CisloNode + model = CisloNode fields = '__all__' depth = DEFAULT_NODE_DEPTH class MezicisloNodeSerializer(serializers.ModelSerializer): class Meta: - model = m.MezicisloNode + model = MezicisloNode fields = '__all__' depth = DEFAULT_NODE_DEPTH class TemaVCisleNodeSerializer(serializers.ModelSerializer): class Meta: - model = m.TemaVCisleNode + model = TemaVCisleNode fields = '__all__' depth = DEFAULT_NODE_DEPTH class OrgTextNodeSerializer(serializers.ModelSerializer): class Meta: - model = m.OrgTextNode + model = OrgTextNode fields = '__all__' depth = DEFAULT_NODE_DEPTH class PohadkaNodeSerializer(serializers.ModelSerializer): class Meta: - model = m.PohadkaNode + model = PohadkaNode fields = '__all__' depth = DEFAULT_NODE_DEPTH @@ -67,7 +71,7 @@ class TextNodeSerializer(serializers.ModelSerializer): text = TextSerializer() class Meta: - model = m.TextNode + model = TextNode fields = ('id','text','polymorphic_ctype') depth = DEFAULT_NODE_DEPTH @@ -80,7 +84,7 @@ class TextNodeWriteSerializer(serializers.ModelSerializer): return node class Meta: - model = m.TextNode + model = TextNode fields = ('id','text') depth = DEFAULT_NODE_DEPTH @@ -93,26 +97,26 @@ class TextNodeCreateSerializer(serializers.ModelSerializer): temp_text = validated_data.pop('text') where = validated_data.pop('where') refnode_id = validated_data.pop('refnode') - refnode = m.TreeNode.objects.get(pk=refnode_id) - text = m.Text.objects.create(**temp_text) + refnode = TreeNode.objects.get(pk=refnode_id) + text = Text.objects.create(**temp_text) if where == 'syn': - node = treelib.create_child(refnode,m.TextNode,text=text) + node = treelib.create_child(refnode,TextNode,text=text) elif where == 'za': - node = treelib.create_node_after(refnode,m.TextNode,text=text) + node = treelib.create_node_after(refnode,TextNode,text=text) elif where == 'pred': - node = treelib.create_node_before(refnode,m.TextNode,text=text) + node = treelib.create_node_before(refnode,TextNode,text=text) node.where = None node.refnode = None return node class Meta: - model = m.TextNode + model = TextNode fields = ('text','where','refnode') depth = DEFAULT_NODE_DEPTH class CastNodeSerializer(serializers.ModelSerializer): class Meta: - model = m.CastNode + model = CastNode fields = '__all__' depth = DEFAULT_NODE_DEPTH @@ -124,25 +128,25 @@ class CastNodeCreateSerializer(serializers.ModelSerializer): temp_nadpis = validated_data.pop('nadpis') where = validated_data.pop('where') refnode_id = validated_data.pop('refnode') - refnode = m.TreeNode.objects.get(pk=refnode_id) + refnode = TreeNode.objects.get(pk=refnode_id) if where == 'syn': - node = treelib.create_child(refnode,m.CastNode,nadpis=temp_nadpis) + node = treelib.create_child(refnode,CastNode,nadpis=temp_nadpis) elif where == 'za': - node = treelib.create_node_after(refnode,m.CastNode,nadpis=temp_nadpis) + node = treelib.create_node_after(refnode,CastNode,nadpis=temp_nadpis) elif where == 'pred': - node = treelib.create_node_before(refnode,m.CastNode,nadpis=temp_nadpis) + node = treelib.create_node_before(refnode,CastNode,nadpis=temp_nadpis) node.where = None node.refnode = None return node class Meta: - model = m.CastNode + model = CastNode fields = ('nadpis','where','refnode') depth = DEFAULT_NODE_DEPTH class UlohaZadaniNodeSerializer(serializers.ModelSerializer): class Meta: - model = m.UlohaZadaniNode + model = UlohaZadaniNode fields = '__all__' depth = DEFAULT_NODE_DEPTH @@ -157,7 +161,7 @@ class UlohaZadaniNodeWriteSerializer(serializers.ModelSerializer): return node class Meta: - model = m.TextNode + model = TextNode fields = ('id','uloha') depth = DEFAULT_NODE_DEPTH @@ -171,28 +175,28 @@ class UlohaZadaniNodeCreateSerializer(serializers.ModelSerializer): temp_uloha = validated_data.pop('uloha') where = validated_data.pop('where') refnode_id = validated_data.pop('refnode') - refnode = m.TreeNode.objects.get(pk=refnode_id) + refnode = TreeNode.objects.get(pk=refnode_id) # Z cesty ke koreni stromu zjistime, v jakem jsme tematu a v jakem cisle cislo = None tema = None travelnode = refnode while travelnode is not None: - if isinstance(travelnode, m.TemaVCisleNode): + if isinstance(travelnode, TemaVCisleNode): tema = travelnode.tema - if isinstance(travelnode, m.CisloNode): + if isinstance(travelnode, CisloNode): cislo = travelnode.cislo travelnode = treelib.get_parent(travelnode) # Vyrobime ulohu - uloha = m.Uloha.objects.create(cislo_zadani=cislo, nadproblem = tema, **temp_uloha) + uloha = Uloha.objects.create(cislo_zadani=cislo, nadproblem = tema, **temp_uloha) # A vyrobime UlohaZadaniNode if where == 'syn': - node = treelib.create_child(refnode,m.UlohaZadaniNode,uloha = uloha) + node = treelib.create_child(refnode,UlohaZadaniNode,uloha = uloha) elif where == 'za': - node = treelib.create_node_after(refnode,m.UlohaZadaniNode,uloha = uloha) + node = treelib.create_node_after(refnode,UlohaZadaniNode,uloha = uloha) elif where == 'pred': - node = treelib.create_node_before(refnode,m.UlohaZadaniNode,uloha = uloha) + node = treelib.create_node_before(refnode,UlohaZadaniNode,uloha = uloha) node.where = None node.refnode = None node.max_body = None @@ -200,21 +204,21 @@ class UlohaZadaniNodeCreateSerializer(serializers.ModelSerializer): return node class Meta: - model = m.UlohaZadaniNode + model = UlohaZadaniNode fields = ('uloha','where','refnode') depth = DEFAULT_NODE_DEPTH class UlohaVzorakNodeSerializer(serializers.ModelSerializer): class Meta: - model = m.UlohaVzorakNode + model = UlohaVzorakNode fields = '__all__' depth = DEFAULT_NODE_DEPTH class UlohaVzorakNodeWriteSerializer(serializers.ModelSerializer): - uloha = serializers.PrimaryKeyRelatedField(queryset=m.Uloha.objects.all(), many=False, read_only=False) + uloha = serializers.PrimaryKeyRelatedField(queryset=Uloha.objects.all(), many=False, read_only=False) class Meta: - model = m.UlohaVzorakNode + model = UlohaVzorakNode fields = ('id','uloha') depth = DEFAULT_NODE_DEPTH @@ -226,17 +230,17 @@ class UlohaVzorakNodeCreateSerializer(serializers.ModelSerializer): def create(self, validated_data): uloha_id = validated_data.pop('uloha_id') - uloha = m.Uloha.objects.get(pk=uloha_id) + uloha = Uloha.objects.get(pk=uloha_id) where = validated_data.pop('where') refnode_id = validated_data.pop('refnode') - refnode = m.TreeNode.objects.get(pk=refnode_id) + refnode = TreeNode.objects.get(pk=refnode_id) if where == 'syn': - node = treelib.create_child(refnode,m.UlohaVzorakNode,uloha = uloha) + node = treelib.create_child(refnode,UlohaVzorakNode,uloha = uloha) elif where == 'za': - node = treelib.create_node_after(refnode,m.UlohaVzorakNode,uloha = uloha) + node = treelib.create_node_after(refnode,UlohaVzorakNode,uloha = uloha) elif where == 'pred': - node = treelib.create_node_before(refnode,m.UlohaVzorakNode,uloha = uloha) + node = treelib.create_node_before(refnode,UlohaVzorakNode,uloha = uloha) node.refnode = None node.where = None node.uloha_id = None @@ -244,7 +248,7 @@ class UlohaVzorakNodeCreateSerializer(serializers.ModelSerializer): return node class Meta: - model = m.UlohaVzorakNode + model = UlohaVzorakNode fields = ('refnode', 'uloha_id', 'where') depth = DEFAULT_NODE_DEPTH @@ -253,15 +257,15 @@ class UlohaVzorakNodeCreateSerializer(serializers.ModelSerializer): class ReseniNodeSerializer(serializers.ModelSerializer): class Meta: - model = m.ReseniNode + model = ReseniNode fields = '__all__' depth = DEFAULT_NODE_DEPTH class ReseniNodeWriteSerializer(serializers.ModelSerializer): - reseni = serializers.PrimaryKeyRelatedField(queryset=m.Reseni.objects.all(), many=False, read_only=False) + reseni = serializers.PrimaryKeyRelatedField(queryset=Reseni.objects.all(), many=False, read_only=False) class Meta: - model = m.ReseniNode + model = ReseniNode fields = ('id','reseni') depth = DEFAULT_NODE_DEPTH @@ -273,41 +277,41 @@ class ReseniNodeCreateSerializer(serializers.ModelSerializer): def create(self,validated_data): # text_zadani = validated_data.pop('text_zadani') reseni_id = validated_data.pop('reseni_id') - reseni = m.Reseni.objects.get(pk=reseni_id) + reseni = Reseni.objects.get(pk=reseni_id) where = validated_data.pop('where') refnode_id = validated_data.pop('refnode') - refnode = m.TreeNode.objects.get(pk=refnode_id) + refnode = TreeNode.objects.get(pk=refnode_id) # A vyrobime UlohaZadaniNode if where == 'syn': - node = treelib.create_child(refnode,m.ReseniNode,reseni = reseni) + node = treelib.create_child(refnode,ReseniNode,reseni = reseni) elif where == 'za': - node = treelib.create_node_after(refnode,m.ReseniNode,reseni = reseni) + node = treelib.create_node_after(refnode,ReseniNode,reseni = reseni) elif where == 'pred': - node = treelib.create_node_before(refnode,m.ReseniNode,reseni = reseni) + node = treelib.create_node_before(refnode,ReseniNode,reseni = reseni) node.where = None node.refnode = None node.reseni_id = None return node class Meta: - model = m.ReseniNode + model = ReseniNode fields = ('reseni_id','where','refnode') depth = DEFAULT_NODE_DEPTH class TreeNodeSerializer(PolymorphicSerializer): model_serializer_mapping = { - m.RocnikNode: RocnikNodeSerializer, - m.CisloNode: CisloNodeSerializer, - m.MezicisloNode: MezicisloNodeSerializer, - m.TemaVCisleNode: TemaVCisleNodeSerializer, - m.OrgTextNode: OrgTextNodeSerializer, - m.UlohaZadaniNode: UlohaZadaniNodeSerializer, - m.UlohaVzorakNode: UlohaVzorakNodeSerializer, - m.PohadkaNode: PohadkaNodeSerializer, - m.TextNode: TextNodeSerializer, - m.CastNode: CastNodeSerializer, - m.ReseniNode: ReseniNodeSerializer, + RocnikNode: RocnikNodeSerializer, + CisloNode: CisloNodeSerializer, + MezicisloNode: MezicisloNodeSerializer, + TemaVCisleNode: TemaVCisleNodeSerializer, + OrgTextNode: OrgTextNodeSerializer, + UlohaZadaniNode: UlohaZadaniNodeSerializer, + UlohaVzorakNode: UlohaVzorakNodeSerializer, + PohadkaNode: PohadkaNodeSerializer, + TextNode: TextNodeSerializer, + CastNode: CastNodeSerializer, + ReseniNode: ReseniNodeSerializer, } diff --git a/treenode/templatetags.py b/treenode/templatetags.py index e5efe701..701965c5 100644 --- a/treenode/templatetags.py +++ b/treenode/templatetags.py @@ -1,6 +1,6 @@ from django import template from enum import Enum -import seminar.models as m +from .models import RocnikNode, CisloNode, CastNode, TextNode, TemaVCisleNode, UlohaVzorakNode, UlohaZadaniNode, PohadkaNode register = template.Library() @@ -11,8 +11,8 @@ def nodeType(value): if isinstance(value,CastNode): return "Část" if isinstance(value,TextNode): return "Text" if isinstance(value,TemaVCisleNode): return "Téma v čísle" - if isinstance(value,KonferaNode): return "Konfera" - if isinstance(value,ClanekNode): return "Článek" + # if isinstance(value,KonferaNode): return "Konfera" # FIXME neexistuje + # if isinstance(value,ClanekNode): return "Článek" # FIXME neexistuje if isinstance(value,UlohaVzorakNode): return "Vzorák" if isinstance(value,UlohaZadaniNode): return "Zadání úlohy" if isinstance(value,PohadkaNode): return "Pohádka" @@ -22,53 +22,57 @@ def nodeType(value): @register.filter def isRocnik(value): - return isinstance(value, m.RocnikNode) + return isinstance(value, RocnikNode) @register.filter def isCislo(value): - return isinstance(value, m.CisloNode) + return isinstance(value, CisloNode) @register.filter def isCast(value): - return isinstance(value, m.CastNode) + return isinstance(value, CastNode) @register.filter def isText(value): - return isinstance(value, m.TextNode) + return isinstance(value, TextNode) @register.filter def isTemaVCisle(value): - return isinstance(value, m.TemaVCisleNode) + return isinstance(value, TemaVCisleNode) @register.filter def isKonfera(value): - return isinstance(value, m.KonferaNode) + # FIXME neexistuje + # return isinstance(value, KonferaNode) + return False @register.filter def isClanek(value): - return isinstance(value, m.ClanekNode) + # FIXME neexistuje + # return isinstance(value, seminar.models.ClanekNode) + return False @register.filter def isUlohaVzorak(value): - return isinstance(value, m.UlohaVzorakNode) + return isinstance(value, UlohaVzorakNode) @register.filter def isUlohaZadani(value): - return isinstance(value, m.UlohaZadaniNode) + return isinstance(value, UlohaZadaniNode) @register.filter def isPohadka(value): - return isinstance(value, m.PohadkaNode) + return isinstance(value, PohadkaNode) @register.filter def isReseni(value): return False -# return isinstance(value, m.OtisteneReseniNode) +# return isinstance(value, OtisteneReseniNode) @register.filter def isOrgText(value): return False -# return isinstance(value, m.OrgTextNode) +# return isinstance(value, OrgTextNode) ### diff --git a/treenode/tests.py b/treenode/tests.py index 32a77196..e9617b4b 100644 --- a/treenode/tests.py +++ b/treenode/tests.py @@ -1,16 +1,16 @@ from django.test import TestCase import treenode.treelib as tl -import seminar.models as m +from .models import CastNode class SimpleTreeLibTests(TestCase): def setUp(self): # Vyrobíme pár nějakých Nodů - self.root = m.CastNode(root=None, first_child=None, succ=None, nadpis="Root") + self.root = CastNode(root=None, first_child=None, succ=None, nadpis="Root") self.root.save() - self.some_node = m.CastNode(root=self.root, first_child=None, succ=None, nadpis="Přetržené") - self.other_node = m.CastNode(root=self.root, first_child=None, succ=None, nadpis="Dítě") - self.some_orphan = m.CastNode(root=None, first_child=None, succ=None, nadpis="Ošklivé") - self.other_orphan = m.CastNode(root=None, first_child=None, succ=None, nadpis="Káčátko") + self.some_node = CastNode(root=self.root, first_child=None, succ=None, nadpis="Přetržené") + self.other_node = CastNode(root=self.root, first_child=None, succ=None, nadpis="Dítě") + self.some_orphan = CastNode(root=None, first_child=None, succ=None, nadpis="Ošklivé") + self.other_orphan = CastNode(root=None, first_child=None, succ=None, nadpis="Káčátko") # Trochu je pospojujeme self.root.first_child = self.some_node diff --git a/treenode/treelib.py b/treenode/treelib.py index 1835bf1c..5b05a86e 100644 --- a/treenode/treelib.py +++ b/treenode/treelib.py @@ -238,7 +238,7 @@ class TreeLibError(RuntimeError): # Editace stromu: def create_node_after(predecessor, type, **kwargs): - from seminar.models import TreeNode + from .models import TreeNode if predecessor is None: raise TreeLibError("Nelze vyrábět sirotky! (predecessor=None)") if not issubclass(type, TreeNode): @@ -255,7 +255,7 @@ def create_node_after(predecessor, type, **kwargs): # Vyrábí prvního syna, ostatní nalepí za (existují-li) def create_child(parent, type, **kwargs): - from seminar.models import TreeNode + from .models import TreeNode if parent is None: raise TreeLibError("Nelze vyrábět sirotky! (parent=None)") if not issubclass(type, TreeNode): @@ -293,7 +293,7 @@ def insert_last_child(parent, node): last.save() def create_node_before(successor, type, **kwargs): - from seminar.models import TreeNode + from .models import TreeNode if successor is None: raise TreeLibError("Nelze vyrábět sirotky! (successor=None)") if not issubclass(type, TreeNode): diff --git a/treenode/views.py b/treenode/views.py index 2c300263..ee8cd897 100644 --- a/treenode/views.py +++ b/treenode/views.py @@ -6,8 +6,8 @@ from django.views.generic.edit import CreateView from django.contrib.auth.mixins import LoginRequiredMixin from django.core.exceptions import PermissionDenied -import seminar.models as s -import seminar.models as m +from .models import TemaVCisleNode, RocnikNode, CisloNode, UlohaVzorakNode, UlohaZadaniNode, TreeNode, CastNode, TextNode, ReseniNode, PohadkaNode, OrgTextNode +from .models.pomocne import Text, Obrazek from treenode import treelib import treenode.forms as f import treenode.templatetags as tnltt @@ -29,7 +29,7 @@ class TNLData(object): if parent: self.tema_in_path = parent.tema_in_path - if isinstance(anode, m.TemaVCisleNode): + if isinstance(anode, TemaVCisleNode): self.tema_in_path = True def add_edit_options(self): @@ -51,11 +51,11 @@ class TNLData(object): (All of them have method verejne.)""" parent = anode # chceme začít už od konkrétního node včetně while True: - rocnik = isinstance(parent, s.RocnikNode) - cislo = isinstance(parent, s.CisloNode) - uloha = (isinstance(parent, s.UlohaVzorakNode) or - isinstance(parent, s.UlohaZadaniNode)) - tema = isinstance(parent, s.TemaVCisleNode) + rocnik = isinstance(parent, RocnikNode) + cislo = isinstance(parent, CisloNode) + uloha = (isinstance(parent, UlohaVzorakNode) or + isinstance(parent, UlohaZadaniNode)) + tema = isinstance(parent, TemaVCisleNode) if (rocnik or cislo or uloha or tema) or parent==None: break @@ -158,7 +158,7 @@ class TNLData(object): class TreeNodeView(generic.DetailView): - model = s.TreeNode + model = TreeNode template_name = 'treenode/treenode.html' def get_context_data(self,**kwargs): @@ -168,7 +168,7 @@ class TreeNodeView(generic.DetailView): class TreeNodeJSONView(generic.DetailView): - model = s.TreeNode + model = TreeNode def get(self,request,*args, **kwargs): self.object = self.get_object() @@ -178,21 +178,21 @@ class TreeNodeJSONView(generic.DetailView): class TreeNodePridatView(generic.View): type_from_str = { - 'rocnikNode': m.RocnikNode, - 'cisloNode': m.CisloNode, - 'castNode': m.CastNode, - 'textNode': m.TextNode, - 'temaVCisleNode': m.TemaVCisleNode, - 'reseniNode': m.ReseniNode, - 'ulohaZadaniNode': m.UlohaZadaniNode, - 'ulohaVzorakNode': m.UlohaVzorakNode, - 'pohadkaNode': m.PohadkaNode, - 'orgText': m.OrgTextNode, + 'rocnikNode': RocnikNode, + 'cisloNode': CisloNode, + 'castNode': CastNode, + 'textNode': TextNode, + 'temaVCisleNode': TemaVCisleNode, + 'reseniNode': ReseniNode, + 'ulohaZadaniNode': UlohaZadaniNode, + 'ulohaVzorakNode': UlohaVzorakNode, + 'pohadkaNode': PohadkaNode, + 'orgText': OrgTextNode, } def post(self, request, *args, **kwargs): ######## FIXME: ROZEPSANE, NEFUNGUJE, DOPSAT !!!!!! ########### - node = s.TreeNode.objects.get(pk=self.kwargs['pk']) + node = TreeNode.objects.get(pk=self.kwargs['pk']) kam = self.kwargs['kam'] co = self.kwargs['co'] typ = self.type_from_str[co] @@ -202,19 +202,19 @@ class TreeNodePridatView(generic.View): if kam not in ('pred','syn','za'): raise ValidationError('Přidat lze pouze před nebo za node nebo jako syna') - if co == m.TextNode: - new_obj = m.Text() + if co == TextNode: + new_obj = Text() new_obj.save() - elif co == m.CastNode: - new_obj = m.CastNode() + elif co == CastNode: + new_obj = CastNode() new_obj.nadpis = request.POST.get('pridat-castNode-{}-{}'.format(node.id,kam)) new_obj.save() - elif co == m.ReseniNode: + elif co == ReseniNode: new_obj = m pass - elif co == m.UlohaZadaniNode: + elif co == UlohaZadaniNode: pass - elif co == m.UlohaReseniNode: + elif co == UlohaReseniNode: pass else: new_obj = None @@ -225,15 +225,15 @@ class TreeNodePridatView(generic.View): if kam == 'syn': - if typ == m.TextNode: - text_obj = m.Text() + if typ == TextNode: + text_obj = Text() text_obj.save() node = treelib.create_child(node, typ, text=text_obj) else: node = treelib.create_child(node, typ) if kam == 'za': - if typ == m.TextNode: - text_obj = m.Text() + if typ == TextNode: + text_obj = Text() text_obj.save() node = treelib.create_node_after(node, typ, text=text_obj) else: @@ -244,7 +244,7 @@ class TreeNodePridatView(generic.View): class TreeNodeSmazatView(generic.base.View): def post(self, request, *args, **kwargs): - node = s.TreeNode.objects.get(pk=self.kwargs['pk']) + node = TreeNode.objects.get(pk=self.kwargs['pk']) if node.first_child: raise NotImplementedError('Mazání TreeNode se syny není zatím podporováno!') treelib.disconnect_node(node) @@ -254,7 +254,7 @@ class TreeNodeSmazatView(generic.base.View): class TreeNodeOdvesitPrycView(generic.base.View): def post(self, request, *args, **kwargs): - node = s.TreeNode.objects.get(pk=self.kwargs['pk']) + node = TreeNode.objects.get(pk=self.kwargs['pk']) treelib.disconnect_node(node) node.root = None node.save() @@ -263,7 +263,7 @@ class TreeNodeOdvesitPrycView(generic.base.View): class TreeNodePodvesitView(generic.base.View): def post(self, request, *args, **kwargs): - node = s.TreeNode.objects.get(pk=self.kwargs['pk']) + node = TreeNode.objects.get(pk=self.kwargs['pk']) kam = self.kwargs['kam'] if kam == 'pred': treelib.lower_node(node) @@ -274,21 +274,21 @@ class TreeNodePodvesitView(generic.base.View): class TreeNodeProhoditView(generic.base.View): def post(self, request, *args, **kwargs): - node = s.TreeNode.objects.get(pk=self.kwargs['pk']) + node = TreeNode.objects.get(pk=self.kwargs['pk']) treelib.swap_succ(node) return redirect(request.headers.get('referer')) #FIXME ve formulari predat puvodni url a vratit redirect na ni class SirotcinecView(generic.ListView): - model = s.TreeNode + model = TreeNode template_name = 'treenode/orphanage.html' def get_queryset(self): - return s.TreeNode.objects.not_instance_of(s.RocnikNode).filter(root=None,prev=None,succ=None,father_of_first=None) + return TreeNode.objects.not_instance_of(RocnikNode).filter(root=None,prev=None,succ=None,father_of_first=None) # FIXME pouzit Django REST Framework class TextWebView(generic.DetailView): - model = s.Text + model = Text def get(self,request,*args, **kwargs): self.object = self.get_object() @@ -300,7 +300,7 @@ class VueTestView(generic.TemplateView): class NahrajObrazekKTreeNoduView(LoginRequiredMixin, CreateView): - model = s.Obrazek + model = Obrazek form_class = f.NahrajObrazekKTreeNoduForm def get_initial(self): @@ -316,7 +316,7 @@ class NahrajObrazekKTreeNoduView(LoginRequiredMixin, CreateView): print(form) self.object = form.save(commit=False) print(self.object.na_web) - self.object.text = m.Text.objects.get(pk=int(self.request.headers['Textid'])) + self.object.text = Text.objects.get(pk=int(self.request.headers['Textid'])) self.object.save() return JsonResponse({"url":self.object.na_web.url}) diff --git a/treenode/viewsets.py b/treenode/viewsets.py index 16dce6d6..5280b5e4 100644 --- a/treenode/viewsets.py +++ b/treenode/viewsets.py @@ -3,7 +3,12 @@ from rest_framework import status from rest_framework.response import Response from django.core.exceptions import PermissionDenied from rest_framework.permissions import BasePermission, AllowAny -from seminar import models as m + +from odevzdavatko.models import Reseni +from tvorba.models import Problem, Uloha + +from .models import TextNode, CastNode, UlohaVzorakNode, UlohaZadaniNode, ReseniNode +from .models.pomocne import Text import treenode.serializers as views from treenode.permissions import AllowWrite @@ -66,17 +71,17 @@ class ReadWriteSerializerMixin(object): return self.create_serializer_class class TextViewSet(PermissionMixin, viewsets.ModelViewSet): - queryset = m.Text.objects.all() + queryset = Text.objects.all() serializer_class = views.TextSerializer class TextNodeViewSet(PermissionMixin, ReadWriteSerializerMixin,viewsets.ModelViewSet): - queryset = m.TextNode.objects.all() + queryset = TextNode.objects.all() read_serializer_class = views.TextNodeSerializer write_serializer_class = views.TextNodeWriteSerializer create_serializer_class = views.TextNodeCreateSerializer class CastNodeViewSet(PermissionMixin, ReadWriteSerializerMixin,viewsets.ModelViewSet): - queryset = m.CastNode.objects.all() + queryset = CastNode.objects.all() read_serializer_class = views.CastNodeSerializer write_serializer_class = views.CastNodeSerializer create_serializer_class = views.CastNodeCreateSerializer @@ -95,7 +100,7 @@ class UlohaVzorakNodeViewSet(PermissionMixin, ReadWriteSerializerMixin, viewsets create_serializer_class = views.UlohaVzorakNodeCreateSerializer def get_queryset(self): - queryset = m.UlohaVzorakNode.objects.all() + queryset = UlohaVzorakNode.objects.all() nazev = self.request.query_params.get('nazev',None) if nazev is not None: queryset = queryset.filter(nazev__contains=nazev) @@ -114,7 +119,7 @@ class ReseniViewSet(viewsets.ModelViewSet): serializer_class = views.ReseniSerializer def get_queryset(self): - queryset = m.Reseni.objects.all() + queryset = Reseni.objects.all() #FIXME upravit nazvy dle skutecnych polozek reseni nazev = self.request.query_params.get('nazev',None) if nazev is not None: @@ -128,7 +133,7 @@ class UlohaViewSet(viewsets.ModelViewSet): serializer_class = views.UlohaSerializer def get_queryset(self): - queryset = m.Uloha.objects.all() + queryset = Uloha.objects.all() nazev = self.request.query_params.get('nazev',None) if nazev is not None: queryset = queryset.filter(nazev__contains=nazev) @@ -138,13 +143,13 @@ class UlohaViewSet(viewsets.ModelViewSet): return queryset class UlohaZadaniNodeViewSet(ReadWriteSerializerMixin, viewsets.ModelViewSet): - queryset = m.UlohaZadaniNode.objects.all() + queryset = UlohaZadaniNode.objects.all() read_serializer_class = views.UlohaZadaniNodeSerializer write_serializer_class = views.UlohaZadaniNodeWriteSerializer create_serializer_class = views.UlohaZadaniNodeCreateSerializer class ReseniNodeViewSet(ReadWriteSerializerMixin, viewsets.ModelViewSet): - queryset = m.ReseniNode.objects.all() + queryset = ReseniNode.objects.all() read_serializer_class = views.ReseniNodeSerializer write_serializer_class = views.ReseniNodeWriteSerializer create_serializer_class = views.ReseniNodeCreateSerializer @@ -155,7 +160,7 @@ class ProblemViewSet(viewsets.ModelViewSet): serializer_class = views.ProblemSerializer def get_queryset(self): - queryset = m.Problem.objects.all() + queryset = Problem.objects.all() ucel = self.request.query_params.get('ucel',None) rocnik = self.request.query_params.get('rocnik',None) tema = self.request.query_params.get('tema',None) diff --git a/tvorba/models.py b/tvorba/models.py index c26fc9f0..c00d0935 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) diff --git a/tvorba/testutils.py b/tvorba/testutils.py index 323acb38..4bbed8e4 100644 --- a/tvorba/testutils.py +++ b/tvorba/testutils.py @@ -6,10 +6,12 @@ import lorem import django.contrib.auth import logging -from .models import Rocnik, Cislo, Deadline, Problem, Tema, Uloha +from .models import Rocnik, Cislo, Deadline, Problem, Tema, Uloha, Clanek from odevzdavatko.models import Reseni, Hodnoceni -import seminar.models as m +# TODO zbavit se treenodů do treenode.testutils (ty pak klidně volat odtud) +from treenode.models import TextNode, UlohaZadaniNode, UlohaVzorakNode, RocnikNode, CisloNode, TemaVCisleNode, CastNode, MezicisloNode, ReseniNode +from treenode.models.pomocne import Text from treenode.treelib import all_children, insert_last_child, all_children_of_type, create_node_after @@ -56,12 +58,12 @@ def gen_zadani_ulohy(rnd, cisla, organizatori, pocet_oboru, poradi_cisla, poradi rnd.choice(jmeno), rnd.choice(kde)] ) - text_zadani = m.Text.objects.create( + text_zadani = Text.objects.create( na_web = text, do_cisla = text, ) - zad = m.TextNode.objects.create(text = text_zadani, root = p.cislo_zadani.rocnik.rocniknode) - uloha_zadani = m.UlohaZadaniNode.objects.create(uloha = p, first_child = zad, root = p.cislo_zadani.rocnik.rocniknode) + zad = TextNode.objects.create(text = text_zadani, root = p.cislo_zadani.rocnik.rocniknode) + uloha_zadani = UlohaZadaniNode.objects.create(uloha = p, first_child = zad, root = p.cislo_zadani.rocnik.rocniknode) p.ulohazadaninode = uloha_zadani otec_syn(cisla[poradi_cisla-2-1].cislonode, uloha_zadani) @@ -78,12 +80,12 @@ def gen_vzoroveho_reseni_ulohy(rnd, organizatori, uloha, pocet_opravovatelu): # Generování vzorového řešení. obsah = rnd.choice(reseni) - text_vzoraku = m.Text.objects.create( + text_vzoraku = Text.objects.create( na_web = obsah, do_cisla = obsah ) - vzorak = m.TextNode.objects.create(text = text_vzoraku, root = uloha.cislo_zadani.rocnik.rocniknode) - uloha_vzorak = m.UlohaVzorakNode.objects.create(uloha = uloha, first_child = vzorak, root = uloha.cislo_zadani.rocnik.rocniknode) + vzorak = TextNode.objects.create(text = text_vzoraku, root = uloha.cislo_zadani.rocnik.rocniknode) + uloha_vzorak = UlohaVzorakNode.objects.create(uloha = uloha, first_child = vzorak, root = uloha.cislo_zadani.rocnik.rocniknode) uloha.ulohavzoraknode = uloha_vzorak uloha.opravovatele.set(rnd.sample(organizatori, pocet_opravovatelu)) @@ -134,7 +136,7 @@ def gen_rocniky(last_rocnik, size): node = None for ri in range(min(last_rocnik - size, 1), last_rocnik + 1): rocnik = Rocnik.objects.create(prvni_rok = 1993 + ri, rocnik = ri) - node2 = m.RocnikNode.objects.create(rocnik = rocnik, succ = node) + node2 = RocnikNode.objects.create(rocnik = rocnik, succ = node) rocnik.save() node = node2 rocniky.append(rocnik) @@ -169,7 +171,7 @@ def gen_cisla(rnd, rocniky): datum_vydani=vydano, verejne_db=True, ) - node2 = m.CisloNode.objects.get(cislo = cislo) + node2 = CisloNode.objects.get(cislo = cislo) node2.succ = node node2.root = rocnik.rocniknode cislo.save() @@ -197,7 +199,7 @@ def add_first_child(node, child): def get_text(): odstavec = lorem.paragraph() - return m.Text.objects.create(na_web = odstavec, do_cisla = odstavec) + return Text.objects.create(na_web = odstavec, do_cisla = odstavec) def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod): tema = Tema.objects.create( @@ -217,54 +219,54 @@ def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod): for cislo in cisla: # Přidáme TemaVCisleNode do daného čísla cislo_node = cislo.cislonode - tema_cislo_node = m.TemaVCisleNode.objects.create(tema = tema, root = cislo_node.root) + tema_cislo_node = TemaVCisleNode.objects.create(tema = tema, root = cislo_node.root) insert_last_child(cislo_node, tema_cislo_node) # Přidávání obsahu do čísla - cast_node = m.CastNode.objects.create(nadpis = "Příspěvek k číslu {}".format(cislo.kod), root=cislo_node.root) + cast_node = CastNode.objects.create(nadpis = "Příspěvek k číslu {}".format(cislo.kod), root=cislo_node.root) add_first_child(tema_cislo_node, cast_node) - text_node = m.TextNode.objects.create(text = get_text(), root=cislo_node.root) + text_node = TextNode.objects.create(text = get_text(), root=cislo_node.root) add_first_child(cast_node, text_node) - cast_node2 = m.CastNode.objects.create(nadpis = "První podproblém", root=cislo_node.root) + cast_node2 = CastNode.objects.create(nadpis = "První podproblém", root=cislo_node.root) add_first_child(text_node, cast_node2) - text_node2 = m.TextNode.objects.create(text = get_text(), root=cislo_node.root) + text_node2 = TextNode.objects.create(text = get_text(), root=cislo_node.root) add_first_child(cast_node2, text_node2) - cast_node3 = m.CastNode.objects.create(nadpis = "Druhý podproblém", root=cislo_node.root) + cast_node3 = CastNode.objects.create(nadpis = "Druhý podproblém", root=cislo_node.root) add_first_child(text_node2, cast_node3) - text_node3 = m.TextNode.objects.create(text = get_text(), root=cislo_node.root) + text_node3 = TextNode.objects.create(text = get_text(), root=cislo_node.root) add_first_child(cast_node3, text_node3) - cast_node4 = m.CastNode.objects.create(nadpis = "Třetí podproblém", root=cislo_node.root) + cast_node4 = CastNode.objects.create(nadpis = "Třetí podproblém", root=cislo_node.root) add_first_child(text_node3, cast_node4) - text_node4 = m.TextNode.objects.create(text = get_text(), root=cislo_node.root) + text_node4 = TextNode.objects.create(text = get_text(), root=cislo_node.root) add_first_child(cast_node3, text_node4) - cast_node3a = m.CastNode.objects.create(nadpis = "Podproblém paralelní s " + cast_node3a = CastNode.objects.create(nadpis = "Podproblém paralelní s " "druhým podproblémem", root=cislo_node.root) cast_node3.succ = cast_node3a cast_node3.save() - text_node3a = m.TextNode.objects.create(text = get_text(), root=cislo_node.root) + text_node3a = TextNode.objects.create(text = get_text(), root=cislo_node.root) add_first_child(cast_node3a, text_node3a) # Občas přidáme mezičíslo if rnd.randint(1, 3) == 1: - create_node_after(cislo_node, m.MezicisloNode, root=cislo_node.root) + create_node_after(cislo_node, MezicisloNode, root=cislo_node.root) mezicislo_node = cislo_node.succ - cast_node_mezicislo = m.CastNode.objects.create( + cast_node_mezicislo = CastNode.objects.create( nadpis = "Příspěvek k mezičíslu".format(cislo.kod), root=cislo_node.root) add_first_child(mezicislo_node, cast_node_mezicislo) odstavec = lorem.paragraph() - text_mezicislo = m.Text.objects.create(na_web = odstavec, do_cisla = odstavec) - text_node_mezicislo = m.TextNode.objects.create(text = text_mezicislo, root=cislo_node.root) + text_mezicislo = Text.objects.create(na_web = odstavec, do_cisla = odstavec) + text_node_mezicislo = TextNode.objects.create(text = text_mezicislo, root=cislo_node.root) add_first_child(cast_node_mezicislo, text_node_mezicislo) return tema @@ -308,7 +310,7 @@ def gen_temata(rnd, rocniky, rocnik_cisla, organizatori): # Vyrobíme TemaVCisleNody pro obsah for i in range(zacatek_tematu, konec_tematu+1): - node = m.TemaVCisleNode.objects.create(tema = t,root=rocnik.rocniknode) + node = TemaVCisleNode.objects.create(tema = t,root=rocnik.rocniknode) # FIXME: Není to off-by-one? otec = cisla[i-1].cislonode otec_syn(otec, node) @@ -361,12 +363,12 @@ def gen_ulohy_tematu(rnd, organizatori, resitele, tema, kod, cislo, cislo_se_vzo rnd.choice(jmeno), rnd.choice(kde)] ) - text_zadani = m.Text.objects.create( + text_zadani = Text.objects.create( na_web = obsah, do_cisla = obsah, ) - zad = m.TextNode.objects.create(text = text_zadani, root=tema.temavcislenode_set.first().root) - uloha_zadani = m.UlohaZadaniNode.objects.create(uloha=uloha, first_child = zad, root=tema.temavcislenode_set.first().root) + zad = TextNode.objects.create(text = text_zadani, root=tema.temavcislenode_set.first().root) + uloha_zadani = UlohaZadaniNode.objects.create(uloha=uloha, first_child = zad, root=tema.temavcislenode_set.first().root) uloha.ulohazadaninode = uloha_zadani # Generování řešení a hodnocení k úloze @@ -398,7 +400,7 @@ def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori, else: cislo_se_vzorakem = cislo_se_vzorakem.first() - for tema_node in all_children_of_type(cislo.cislonode, m.TemaVCisleNode): + for tema_node in all_children_of_type(cislo.cislonode, TemaVCisleNode): tema = tema_node.tema # Pokud už témátko skončilo, žádné úložky negenerujeme @@ -421,7 +423,7 @@ def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori, # Najdeme správný TemaVCisleNode pro vložení vzoráku res_tema_node = None; for node in all_children(cislo_se_vzorakem.cislonode): - if isinstance(node, m.TemaVCisleNode): + if isinstance(node, TemaVCisleNode): if node.tema == tema: res_tema_node = node if res_tema_node is None: @@ -440,7 +442,7 @@ def otec_syn(otec, syn): def gen_clanek(rnd, organizatori, resitele): logger.info("Generuji článek do čísla 22.2") - clanek = m.Clanek.objects.create( + clanek = Clanek.objects.create( nazev="Článek o Lorem ipsum", nadproblem=None, stav='vyreseny', @@ -467,7 +469,7 @@ def gen_clanek(rnd, organizatori, resitele): ) hodnoceni.save() - reseninode = m.ReseniNode.objects.create( + reseninode = ReseniNode.objects.create( reseni=reseni ) reseninode.save() @@ -483,26 +485,26 @@ def gen_clanek(rnd, organizatori, resitele): # FIXME: Ten, kdo vymyslel TreeLib (mj. týž, kdo psal tenhle kód), # nevyrobil vhodnou funkci, takže to postavíme pozpátku pomocí create_child # (které vyrábí _prvního_ syna) - create_child(reseninode, m.CastNode, nadpis="Lorem ipsum") + create_child(reseninode, CastNode, nadpis="Lorem ipsum") # Taky ten člověk nevyrobil vracení nových věcí... castnode = reseninode.first_child # Úvodní odstaveček obsah = "Tohle je zamyšlení o textu lorem ipsum. Začneme a skončíme ukázkou." - text = m.Text.objects.create( + text = Text.objects.create( na_web=obsah, do_cisla=obsah, ) text.save() - create_child(reseninode, m.TextNode, text=text) + create_child(reseninode, TextNode, text=text) # Několik odstavců lorem ipsum for _ in range(rnd.randint(3, 7)): lipsum = lorem.paragraph() - text = m.Text.objects.create( + text = Text.objects.create( na_web=lipsum, do_cisla=lipsum, ) text.save() - create_child(castnode, m.TextNode, text=text) + create_child(castnode, TextNode, text=text) logger.info(f"Článek vygenerován (reseni={reseni.id}, treenode={reseninode.id})") diff --git a/tvorba/views/__init__.py b/tvorba/views/__init__.py index 2bef3591..2e9836e9 100644 --- a/tvorba/views/__init__.py +++ b/tvorba/views/__init__.py @@ -14,10 +14,10 @@ from django.db.models import Q, Sum, Count from django.views.generic.base import RedirectView from django.core.exceptions import PermissionDenied -import seminar.models as m from personalni.models import Resitel from soustredeni.models import Konfera from tvorba.models import Problem, Cislo, Rocnik, Tema, Clanek, Deadline, Uloha +from treenode.models import TemaVCisleNode, PohadkaNode from various.models import Nastaveni from treenode import treelib import treenode.templatetags as tnltt @@ -79,7 +79,7 @@ def get_problemy_k_tematu(tema): # context['tnldata'] = TNLData.from_tnldata_list([tnl_zadani, tnl_vzorak]) # elif isinstance(self.object, Tema): # rocniknode = self.object.rocnik.rocniknode -# context['tnldata'] = TNLData.filter_treenode(rocniknode, lambda x: isinstance(x, m.TemaVCisleNode)) +# context['tnldata'] = TNLData.filter_treenode(rocniknode, lambda x: isinstance(x, TemaVCisleNode)) # else: # raise ValueError("Obecný problém nejde zobrazit.") # return context @@ -144,9 +144,9 @@ def ZadaniTemataView(request): # tematko_object = Tema.objects.filter(rocnik=rocnik_object[0], kod=tematko) # seznam = vytahniZLesaSeznam(tematko_object[0], nastaveni.aktualni_rocnik().rocniknode) # for node, depth in seznam: -# if node.isinstance(node, m.KonferaNode): +# if node.isinstance(node, KonferaNode): # FIXME neexistuje # raise Exception("Not implemented yet") -# if node.isinstance(node, m.PohadkaNode): # Mohu ignorovat, má pod sebou +# if node.isinstance(node, PohadkaNode): # Mohu ignorovat, má pod sebou # pass # # return render(request, 'tvorba/tematka/toaletak.html', {}) diff --git a/various/views/final.py b/various/views/final.py index d21c00be..a1df0cfa 100644 --- a/various/views/final.py +++ b/various/views/final.py @@ -14,7 +14,8 @@ from django.views import generic import novinky.views import treenode.treelib as t import tvorba.views -import seminar.models as m + +from treenode.models import CisloNode from personalni.models import Resitel, Osoba from tvorba.models import Clanek, Deadline @@ -128,7 +129,7 @@ def seznam_problemu(): # content type je věc pomáhající rozeznávat různé typy objektů v django-polymorphic # protože isinstance vrátí vždy jen TreeNode # https://django-polymorphic.readthedocs.io/en/stable/migrating.html - cislonode_ct = ContentType.objects.get_for_model(m.CisloNode) + cislonode_ct = ContentType.objects.get_for_model(CisloNode) node = clanek_node while node is not None: node_ct = node.polymorphic_ctype From dc0ff80632248bb8cce52aae1ecf7d9f5ef29d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 1 Nov 2024 13:51:43 +0100 Subject: [PATCH 29/51] =?UTF-8?q?P=C5=99ehl=C3=ADdnut=C3=BD=20import=20sem?= =?UTF-8?q?inar.models?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aesop/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aesop/views.py b/aesop/views.py index 56a6d266..c38c503b 100644 --- a/aesop/views.py +++ b/aesop/views.py @@ -6,7 +6,8 @@ from django.views import generic from django.utils.encoding import force_str from .utils import default_ovvpfile -from seminar.models import Rocnik, Soustredeni +from soustredeni.models import Soustredeni +from tvorba.models import Rocnik from vysledkovky import utils from tvorba.utils import aktivniResitele From 7f21d10c26d6a983f7bb42f739456d225cab5bda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 1 Nov 2024 14:02:07 +0100 Subject: [PATCH 30/51] =?UTF-8?q?Ha,=20tak=20jsem=20se=20n=C4=9Bkde=20zamo?= =?UTF-8?q?tal=20do=20import=20cyklu=20a=20musel=20jsem=20ReseniNode=20d?= =?UTF-8?q?=C3=A1t=20tam,=20kam=20pat=C5=99=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/models/__init__.py | 1 - seminar/models/odevzdavatko.py | 20 -------------------- seminar/models/treenode.py | 17 +++++++++++++++++ treenode/models/__init__.py | 1 - 4 files changed, 17 insertions(+), 22 deletions(-) delete mode 100644 seminar/models/odevzdavatko.py diff --git a/seminar/models/__init__.py b/seminar/models/__init__.py index 95e449ab..4b57e025 100644 --- a/seminar/models/__init__.py +++ b/seminar/models/__init__.py @@ -1,5 +1,4 @@ from .tvorba import * -from .odevzdavatko import * from .base import * from .pomocne import * from .treenode import * diff --git a/seminar/models/odevzdavatko.py b/seminar/models/odevzdavatko.py deleted file mode 100644 index 6079a708..00000000 --- a/seminar/models/odevzdavatko.py +++ /dev/null @@ -1,20 +0,0 @@ -from django.db import models - -from treenode import models 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/treenode.py b/seminar/models/treenode.py index eee40281..8600c4b4 100644 --- a/seminar/models/treenode.py +++ b/seminar/models/treenode.py @@ -9,6 +9,7 @@ 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 odevzdavatko.models import Reseni from .pomocne import Text @@ -263,3 +264,19 @@ class CastNode(TreeNode): def getOdkazStr(self): return str(self.nadpis) + + +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) diff --git a/treenode/models/__init__.py b/treenode/models/__init__.py index f068a819..36b2521c 100644 --- a/treenode/models/__init__.py +++ b/treenode/models/__init__.py @@ -1,2 +1 @@ -from seminar.models.odevzdavatko import * from seminar.models.treenode import * From 007332804e7d26838434a352bff0cf84273df656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 1 Nov 2024 16:40:21 +0100 Subject: [PATCH 31/51] =?UTF-8?q?Fix:=20A=C5=A5=20to=20alespo=C5=88=20neh?= =?UTF-8?q?=C3=A1z=C3=AD=20chybu.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tvorba/templates/tvorba/archiv/cislo.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvorba/templates/tvorba/archiv/cislo.html b/tvorba/templates/tvorba/archiv/cislo.html index b33f83de..fdd0db43 100644 --- a/tvorba/templates/tvorba/archiv/cislo.html +++ b/tvorba/templates/tvorba/archiv/cislo.html @@ -38,7 +38,7 @@
  • Obálky (PDF)
  • Tituly (TeX, 2. deadline předchozího čísla a 1.deadline tohoto)
  • Výsledkovka (TeX, 2. deadline předchozího čísla a 1.deadline tohoto)
  • -
  • Odměny
  • +
  • {% if prevcislo %}Odměny{% else %}Pro toto číslo neumíme spočítat odměny.{% endif %}{# FIXME (Jediné číslo, kde toto neumíme je to úplně první.) #}
  • Tituly do závěrečného čísla (TeX, 2. deadline předchozího čísla a oba tohoto)
  • Výsledkovka závěrečného čísla ročníku (TeX, 2. deadline předchozího čísla a oba tohoto)
  • From 2659d72d2e724cdb317988aab7212ce2db1c76fa Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sat, 2 Nov 2024 22:00:08 +0100 Subject: [PATCH 32/51] =?UTF-8?q?odst=C5=99el=20TreeNode:=20=F0=9F=94=AB?= =?UTF-8?q?=F0=9F=92=A5=F0=9F=92=A5=F0=9F=92=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0007_odstrel_treenode_pre.py | 13 + .../0008_odstrel_treenode_relink.py | 20 ++ .../migrations/0009_odstrel_treenode_post.py | 14 ++ odevzdavatko/models.py | 2 +- .../migrations/0016_odstrel_treenode_pre.py | 13 + .../migrations/0017_odstrel_treenode_post.py | 14 ++ .../migrations/0140_odstrel_treenode_pre.py | 16 ++ .../0141_odstrel_treenode_unmanage.py | 69 ++++++ .../0142_odstrel_treenode_delete.py | 153 ++++++++++++ .../migrations/0143_odstrel_treenode_post.py | 14 ++ seminar/models/__init__.py | 4 +- seminar/models/odevzdavatko.py | 20 -- seminar/models/pomocne.py | 69 ------ .../0001_odstrel_treenode_create.py | 231 ++++++++++++++++++ .../0002_odstrel_treenode_manage.py | 70 ++++++ .../migrations/0003_odstrel_treenode_post.py | 13 + .../models/treenode.py => treenode/models.py | 81 +++++- .../migrations/0004_odstrel_treenode_pre.py | 13 + .../migrations/0005_odstrel_treenode_post.py | 14 ++ tvorba/models.py | 6 +- 20 files changed, 750 insertions(+), 99 deletions(-) create mode 100644 odevzdavatko/migrations/0007_odstrel_treenode_pre.py create mode 100644 odevzdavatko/migrations/0008_odstrel_treenode_relink.py create mode 100644 odevzdavatko/migrations/0009_odstrel_treenode_post.py create mode 100644 personalni/migrations/0016_odstrel_treenode_pre.py create mode 100644 personalni/migrations/0017_odstrel_treenode_post.py create mode 100644 seminar/migrations/0140_odstrel_treenode_pre.py create mode 100644 seminar/migrations/0141_odstrel_treenode_unmanage.py create mode 100644 seminar/migrations/0142_odstrel_treenode_delete.py create mode 100644 seminar/migrations/0143_odstrel_treenode_post.py delete mode 100644 seminar/models/odevzdavatko.py delete mode 100644 seminar/models/pomocne.py create mode 100644 treenode/migrations/0001_odstrel_treenode_create.py create mode 100644 treenode/migrations/0002_odstrel_treenode_manage.py create mode 100644 treenode/migrations/0003_odstrel_treenode_post.py rename seminar/models/treenode.py => treenode/models.py (79%) create mode 100644 tvorba/migrations/0004_odstrel_treenode_pre.py create mode 100644 tvorba/migrations/0005_odstrel_treenode_post.py 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..2e1287e4 --- /dev/null +++ b/seminar/migrations/0140_odstrel_treenode_pre.py @@ -0,0 +1,16 @@ +# 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'), + ] + + 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/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 79% rename from seminar/models/treenode.py rename to treenode/models.py index eee40281..414e9e40 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,48 @@ 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) + +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 +292,6 @@ class TextNode(TreeNode): def getOdkazStr(self): return str(self.text) - class CastNode(TreeNode): class Meta: db_table = 'seminar_nodes_cast' @@ -263,3 +305,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) From 8ae19988b18a1671da75eeb0077a19d28a05505d Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sat, 2 Nov 2024 22:21:47 +0100 Subject: [PATCH 33/51] =?UTF-8?q?Vyr=C3=A1b=C4=9Bt=20sch=C3=A9ma=20semin?= =?UTF-8?q?=C3=A1=C5=99e=20u=C5=BE=20nem=C3=A1=20moc=20smysl=20:-)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- make/schema | 1 - 1 file changed, 1 deletion(-) 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 From e10747aa9f4d25068f9d3639d668452fe954339d Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sat, 2 Nov 2024 22:46:14 +0100 Subject: [PATCH 34/51] =?UTF-8?q?odst=C5=99el=20Treenode:=20zapomenut?= =?UTF-8?q?=C3=A1=20z=C3=A1vislost=20na=20contenttypes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/migrations/0140_odstrel_treenode_pre.py | 1 + 1 file changed, 1 insertion(+) diff --git a/seminar/migrations/0140_odstrel_treenode_pre.py b/seminar/migrations/0140_odstrel_treenode_pre.py index 2e1287e4..b74a1215 100644 --- a/seminar/migrations/0140_odstrel_treenode_pre.py +++ b/seminar/migrations/0140_odstrel_treenode_pre.py @@ -10,6 +10,7 @@ class Migration(migrations.Migration): ('odevzdavatko', '0007_odstrel_treenode_pre'), ('personalni', '0016_odstrel_treenode_pre'), ('tvorba', '0004_odstrel_treenode_pre'), + ('contenttypes', '0002_remove_content_type_name'), ] operations = [ From 9b14e4a333380a6a7d8550a05f858b490c55a95a Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 3 Nov 2024 01:46:18 +0100 Subject: [PATCH 35/51] =?UTF-8?q?Treenode:=20=C5=BE=C3=A1dn=C3=A9=20pomocn?= =?UTF-8?q?=C3=A9=20treenody=20nebudou.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- treenode/admin.py | 2 +- treenode/forms.py | 2 +- treenode/models/__init__.py | 1 - treenode/models/pomocne.py | 1 - treenode/serializers.py | 2 +- treenode/views.py | 2 +- treenode/viewsets.py | 2 +- tvorba/testutils.py | 2 +- 8 files changed, 6 insertions(+), 8 deletions(-) delete mode 100644 treenode/models/__init__.py delete mode 100644 treenode/models/pomocne.py diff --git a/treenode/admin.py b/treenode/admin.py index 6f3bfadb..fb8a8781 100644 --- a/treenode/admin.py +++ b/treenode/admin.py @@ -5,7 +5,7 @@ from django.forms import widgets from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter from .models import TreeNode, RocnikNode, CisloNode, MezicisloNode, TemaVCisleNode, UlohaZadaniNode, PohadkaNode, UlohaVzorakNode, TextNode, CastNode, OrgTextNode -from .models.pomocne import Text, Obrazek +from .models import Text, Obrazek # Polymorfismus pro stromy # TODO: Inlines podle https://django-polymorphic.readthedocs.io/en/stable/admin.html diff --git a/treenode/forms.py b/treenode/forms.py index 61243720..12ef293b 100644 --- a/treenode/forms.py +++ b/treenode/forms.py @@ -1,5 +1,5 @@ from django import forms -from .models.pomocne import Obrazek +from .models import Obrazek # pro přidání políčka do formuláře je potřeba # - mít v modelu tu položku, kterou chci upravovat diff --git a/treenode/models/__init__.py b/treenode/models/__init__.py deleted file mode 100644 index 36b2521c..00000000 --- a/treenode/models/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from seminar.models.treenode import * diff --git a/treenode/models/pomocne.py b/treenode/models/pomocne.py deleted file mode 100644 index 6592da81..00000000 --- a/treenode/models/pomocne.py +++ /dev/null @@ -1 +0,0 @@ -from seminar.models.pomocne import * diff --git a/treenode/serializers.py b/treenode/serializers.py index d26964d1..435a14d4 100644 --- a/treenode/serializers.py +++ b/treenode/serializers.py @@ -5,7 +5,7 @@ from odevzdavatko.models import Reseni from tvorba.models import Problem, Uloha from .models import RocnikNode, CisloNode, MezicisloNode, TemaVCisleNode, OrgTextNode, PohadkaNode, TextNode, TreeNode, CastNode, UlohaZadaniNode, UlohaVzorakNode, ReseniNode -from .models.pomocne import Text +from .models import Text from treenode import treelib DEFAULT_NODE_DEPTH = 2 diff --git a/treenode/views.py b/treenode/views.py index ee8cd897..bc817faf 100644 --- a/treenode/views.py +++ b/treenode/views.py @@ -7,7 +7,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin from django.core.exceptions import PermissionDenied from .models import TemaVCisleNode, RocnikNode, CisloNode, UlohaVzorakNode, UlohaZadaniNode, TreeNode, CastNode, TextNode, ReseniNode, PohadkaNode, OrgTextNode -from .models.pomocne import Text, Obrazek +from .models import Text, Obrazek from treenode import treelib import treenode.forms as f import treenode.templatetags as tnltt diff --git a/treenode/viewsets.py b/treenode/viewsets.py index 5280b5e4..e668436e 100644 --- a/treenode/viewsets.py +++ b/treenode/viewsets.py @@ -8,7 +8,7 @@ from odevzdavatko.models import Reseni from tvorba.models import Problem, Uloha from .models import TextNode, CastNode, UlohaVzorakNode, UlohaZadaniNode, ReseniNode -from .models.pomocne import Text +from .models import Text import treenode.serializers as views from treenode.permissions import AllowWrite diff --git a/tvorba/testutils.py b/tvorba/testutils.py index 4bbed8e4..53229d18 100644 --- a/tvorba/testutils.py +++ b/tvorba/testutils.py @@ -11,7 +11,7 @@ from .models import Rocnik, Cislo, Deadline, Problem, Tema, Uloha, Clanek from odevzdavatko.models import Reseni, Hodnoceni # TODO zbavit se treenodů do treenode.testutils (ty pak klidně volat odtud) from treenode.models import TextNode, UlohaZadaniNode, UlohaVzorakNode, RocnikNode, CisloNode, TemaVCisleNode, CastNode, MezicisloNode, ReseniNode -from treenode.models.pomocne import Text +from treenode.models import Text from treenode.treelib import all_children, insert_last_child, all_children_of_type, create_node_after From 51618e0d8971943a01c1d59c9162de9b6cff18ef Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 3 Nov 2024 02:51:27 +0100 Subject: [PATCH 36/51] =?UTF-8?q?Oprava=20testu=20d=C4=9Bl=C3=A1n=C3=AD=20?= =?UTF-8?q?orgem?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- personalni/tests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/personalni/tests.py b/personalni/tests.py index 31aac8e8..4c042ba0 100644 --- a/personalni/tests.py +++ b/personalni/tests.py @@ -2,7 +2,7 @@ from django.test import TestCase, RequestFactory from django.contrib.auth.models import User, Group from django.contrib.admin.sites import AdminSite -from personalni.admin import OsobaAdmin +from personalni.admin import OsobaAdmin, udelej_orgem # Tohle bude peklo, až jednou ty modely fakt rozstřelíme… Možná vyrobit various.all_models, které půjdou importovat jako m? :-) import seminar.models as m @@ -42,7 +42,7 @@ class DelaniOrguTest(TestCase): # Pak orga uděláme… qs = m.Osoba.objects.filter(id=self.nova_osoba.id) - self.admin.udelej_orgem(self.request, qs) + udelej_orgem(self.admin, self.request, qs) # A pak už to org má být. self.nova_osoba.refresh_from_db() @@ -56,7 +56,7 @@ class DelaniOrguTest(TestCase): self.assertIsNotNone(novy_org.organizuje_od) def test_pridani_stareho_orga(self): - self.admin.udelej_orgem(self.request, m.Osoba.objects.filter(id=self.stary_org.osoba.id)) # Ugly + udelej_orgem(self.admin, self.request, m.Osoba.objects.filter(id=self.stary_org.osoba.id)) # Ugly # Když to spadne, tak jsem se to dozvěděl, takže už nepotřebuju nic kontrolovat. # Jestli to funguje správně má řešit jiný test. From a2517abade88ab45fd1fb9fa71c7af35392421bc Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 3 Nov 2024 03:12:16 +0100 Subject: [PATCH 37/51] =?UTF-8?q?Odst=C5=99el:=20z=C3=A1v=C4=9Bre=C4=8Dn?= =?UTF-8?q?=C3=A1=20migrace?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/migrations/0144_post_odstrel_vseho.py | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 seminar/migrations/0144_post_odstrel_vseho.py 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 = [ + ] From 461cfa4253ac5d6743f9f309ab9f24fc0fa95137 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Sun, 3 Nov 2024 03:20:52 +0100 Subject: [PATCH 38/51] =?UTF-8?q?Treenode:=20koment=C3=A1=C5=99=20o=20tom,?= =?UTF-8?q?=20pro=C4=8D=20tam=20je=20i=20Text=20a=20Obrazek?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- treenode/models.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/treenode/models.py b/treenode/models.py index 414e9e40..8196d647 100644 --- a/treenode/models.py +++ b/treenode/models.py @@ -249,6 +249,16 @@ class ReseniNode(TreeNode): 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' From 527a7b3ea615bb058e0a32da395661b55ed17b6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 5 Nov 2024 14:58:25 +0100 Subject: [PATCH 39/51] =?UTF-8?q?Pojmenovan=C3=A9=20URL=20m=C3=ADsto=20?= =?UTF-8?q?=C4=8Dist=C3=A9ho=20URL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- novinky/templates/novinky/novinky.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/novinky/templates/novinky/novinky.html b/novinky/templates/novinky/novinky.html index 59f9377b..a1acbeaa 100644 --- a/novinky/templates/novinky/novinky.html +++ b/novinky/templates/novinky/novinky.html @@ -4,7 +4,7 @@ {% if not novinka.zverejneno and user.je_org %}
    {% endif %} {% if novinka.zverejneno or user.je_org %} From 1c146ac5c35d84a284e39ba216e25004327314de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 5 Nov 2024 15:07:51 +0100 Subject: [PATCH 40/51] Admin ReseniNode --- treenode/admin.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/treenode/admin.py b/treenode/admin.py index fb8a8781..56beb97a 100644 --- a/treenode/admin.py +++ b/treenode/admin.py @@ -4,7 +4,7 @@ from django.forms import widgets from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter -from .models import TreeNode, RocnikNode, CisloNode, MezicisloNode, TemaVCisleNode, UlohaZadaniNode, PohadkaNode, UlohaVzorakNode, TextNode, CastNode, OrgTextNode +from .models import TreeNode, RocnikNode, CisloNode, MezicisloNode, TemaVCisleNode, UlohaZadaniNode, PohadkaNode, UlohaVzorakNode, TextNode, CastNode, OrgTextNode, ReseniNode from .models import Text, Obrazek # Polymorfismus pro stromy @@ -24,6 +24,7 @@ class TreeNodeAdmin(PolymorphicParentModelAdmin): TextNode, CastNode, OrgTextNode, + ReseniNode, ] actions = ['aktualizuj_nazvy'] @@ -88,6 +89,10 @@ class TextNodeAdmin(PolymorphicChildModelAdmin): base_model = OrgTextNode show_in_index = True +@admin.register(ReseniNode) +class ReseniNodeAdmin(PolymorphicChildModelAdmin): + base_model = ReseniNode + show_in_index = True class TextAdminInline(admin.TabularInline): model = Text From ac6c41cc8844b8a89ed19782f23f9f1f99b91050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 5 Nov 2024 15:10:36 +0100 Subject: [PATCH 41/51] =?UTF-8?q?N=C3=A1zev=20url=20seznamu=20v=C5=A1ech?= =?UTF-8?q?=20soust=C5=99ed=C4=9Bn=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- soustredeni/models.py | 2 +- soustredeni/urls.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/soustredeni/models.py b/soustredeni/models.py index 509c3cc2..8a6f6853 100644 --- a/soustredeni/models.py +++ b/soustredeni/models.py @@ -75,7 +75,7 @@ class Soustredeni(SeminarModelBase): def verejne_url(self): #return reverse('seminar_soustredeni', kwargs={'pk': self.id}) - return reverse('soustredeni_probehlo') + return reverse('soustredeni_seznam') @reversion.register(ignore_duplicates=True) diff --git a/soustredeni/urls.py b/soustredeni/urls.py index 3ef7edae..181700f5 100644 --- a/soustredeni/urls.py +++ b/soustredeni/urls.py @@ -7,7 +7,7 @@ urlpatterns = [ path( 'probehlo/', views.SoustredeniListView.as_view(), - name='soustredeni_probehlo' + name='soustredeni_seznam' ), path( '/', From d0cb46b658a4b8776d7307229457ee6d97bc8f3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 5 Nov 2024 17:40:36 +0100 Subject: [PATCH 42/51] =?UTF-8?q?N=C3=A1zev=20url=20seznamu=20v=C5=A1ech?= =?UTF-8?q?=20soust=C5=99ed=C4=9Bn=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/sitetree.json | 2 +- personalni/templates/personalni/profil/orgorozcestnik.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/sitetree.json b/data/sitetree.json index c19462e6..4505941c 100644 --- a/data/sitetree.json +++ b/data/sitetree.json @@ -361,7 +361,7 @@ "sort_order": 20, "title": "Proběhlo", "tree": 1, - "url": "soustredeni_probehlo", + "url": "soustredeni_seznam", "urlaspattern": true }, "model": "sitetree.treeitem", diff --git a/personalni/templates/personalni/profil/orgorozcestnik.html b/personalni/templates/personalni/profil/orgorozcestnik.html index 27071b66..2dbc853b 100644 --- a/personalni/templates/personalni/profil/orgorozcestnik.html +++ b/personalni/templates/personalni/profil/orgorozcestnik.html @@ -78,7 +78,7 @@
  • hlasování o přednáškách
  • -
  • proběhlá soustředění +
  • proběhlá soustředění
    • vytvoření galerie
    • stažení seznamu účastníků
    • From a372aa1bc2a8f16ca608b7620c243d9916de4f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 5 Nov 2024 17:46:27 +0100 Subject: [PATCH 43/51] Seminar v dokumentaci --- docs/struktura.rst | 9 +++++---- .../2021-12-06-testovani_dokumentace_codereview.md | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/struktura.rst b/docs/struktura.rst index 439e39e9..46eaee23 100644 --- a/docs/struktura.rst +++ b/docs/struktura.rst @@ -10,11 +10,9 @@ věci jako chybové hlášky a vzhled M&M stránek (menu, patička, atd.). Aktu i veškeré csv. Další jsou pak jednotlivé aplikace (pokud něco hledáte, tak zřejmě chcete najít -tu aplikaci, která tomu odpovídá, respektive se k ní dostat přes url), za -zmínku stojí seminar, kde jsou takové ty věci, co zbyly. Plus jsou tam aktuálně -téměř všechny modely, protože je těžké je přesunout jinam. +tu aplikaci, která tomu odpovídá, respektive se k ní dostat přes url). -**TLDR: Nevšímejte si složky data/ a souborů přímo v kořenové složce.** +**TLDR: Nevšímejte si složek data/ seminar/ a souborů přímo v kořenové složce.** Kromě věcí potřebných ke gitu, :doc:`ke spuštění ` a fukci djanga, dalších drobností, lokální databáze a již zmíněných aplikací jsou tu ``data``, kde je takový ten obsah webu, co by se měl dát snadno měnit (tudíž musí být v @@ -22,6 +20,9 @@ databázi), tj. statické stránky, menu a obrázky v pozadí menu. Ten je třeb měnit hlavně na produkci a sekundárně tady (může to dělat i newebař a nechcete přepsat jeho práci). Vše, co nejsou aplikace je popsáno :doc:`tady `. +Ještě je tu aplikace ``seminar/``, kde bylo původně skoro všechno, a tak nám +tam zbývá spoustu historických migrací (čehož se jen tak nezbavíme). + Základy djanga -------------- diff --git a/docs/zapisy/2021-12-06-testovani_dokumentace_codereview.md b/docs/zapisy/2021-12-06-testovani_dokumentace_codereview.md index c2cfc1b5..e0da9440 100644 --- a/docs/zapisy/2021-12-06-testovani_dokumentace_codereview.md +++ b/docs/zapisy/2021-12-06-testovani_dokumentace_codereview.md @@ -116,7 +116,7 @@ Aktuálně: Jakýsi coding style zhruba existuje, není popsaný, šíří se li - Nesmí být striktně vynucovaný - Musel by být hodně nastavitelný - Nechceme mít kód plný `#NOQA: WTF42` -- Nejspíš vždycky bude mít false positives (`tvorba.utils.roman_numerals`) i false negatives (`seminar.models.tvorba.Cislo.posli_cislo_mailem`) +- Nejspíš vždycky bude mít false positives (`tvorba.utils.roman_numerals`) i false negatives (`tvorba.models.Cislo.posli_cislo_mailem`) - Možná dobrý sluha, ale určitě špatný pán (also: špatná zkušenost ☺) - __Důsledek:__ Hrozí, že těch falešných varování bude moc, čímž to ztratí smysl úplně - Potenciálně by šlo aplikovat jen lokálně na změny? From 62a42675f80dc286ec59859f0adad2ed0b27b769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 5 Nov 2024 18:53:38 +0100 Subject: [PATCH 44/51] mv SeminarModelBase a OverwriteStorage --- odevzdavatko/models.py | 8 ++--- personalni/models.py | 2 +- seminar/migrations/0100_auto_20211129_2354.py | 5 +-- seminar/models/__init__.py | 3 -- seminar/models/base.py | 23 ------------ seminar/models/tvorba.py | 14 -------- soustredeni/models.py | 2 +- treenode/models.py | 2 +- tvorba/migrations/0001_tvorba_create.py | 4 +-- tvorba/models.py | 3 +- various/models.py | 36 ++++++++++++++++--- 11 files changed, 44 insertions(+), 58 deletions(-) delete mode 100644 seminar/models/base.py delete mode 100644 seminar/models/tvorba.py diff --git a/odevzdavatko/models.py b/odevzdavatko/models.py index dea32f83..3b510fe7 100644 --- a/odevzdavatko/models.py +++ b/odevzdavatko/models.py @@ -10,13 +10,13 @@ from django.utils import timezone from django.conf import settings from tvorba.models import Problem, Deadline, Cislo, Uloha, aux_generate_filename -from seminar.models import base as bm +from various.models import SeminarModelBase from odevzdavatko.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet from personalni.models import Resitel @reversion.register(ignore_duplicates=True) -class Reseni(bm.SeminarModelBase): +class Reseni(SeminarModelBase): class Meta: db_table = 'seminar_reseni' @@ -88,7 +88,7 @@ class Reseni(bm.SeminarModelBase): # self.cislo_body = self.problem.cislo_reseni # super(Reseni, self).save(*args, **kwargs) -class Hodnoceni(bm.SeminarModelBase): +class Hodnoceni(SeminarModelBase): class Meta: db_table = 'seminar_hodnoceni' verbose_name = 'Hodnocení' @@ -181,7 +181,7 @@ def generate_filename(self, filename): @reversion.register(ignore_duplicates=True) -class PrilohaReseni(bm.SeminarModelBase): +class PrilohaReseni(SeminarModelBase): class Meta: db_table = 'seminar_priloha_reseni' diff --git a/personalni/models.py b/personalni/models.py index b8bcbdec..636b132e 100644 --- a/personalni/models.py +++ b/personalni/models.py @@ -11,7 +11,7 @@ from django_countries.fields import CountryField from reversion import revisions as reversion -from seminar.models.base import SeminarModelBase +from various.models import SeminarModelBase logger = logging.getLogger(__name__) diff --git a/seminar/migrations/0100_auto_20211129_2354.py b/seminar/migrations/0100_auto_20211129_2354.py index 80906d4e..87f0b824 100644 --- a/seminar/migrations/0100_auto_20211129_2354.py +++ b/seminar/migrations/0100_auto_20211129_2354.py @@ -1,7 +1,8 @@ # Generated by Django 2.2.24 on 2021-11-29 22:54 from django.db import migrations, models -import seminar.models.tvorba +import various.models +import seminar.models class Migration(migrations.Migration): @@ -14,6 +15,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='cislo', name='pdf', - field=models.FileField(blank=True, help_text='PDF čísla, které si mohou řešitelé stáhnout', null=True, storage=seminar.models.tvorba.OverwriteStorage(), upload_to=seminar.models.tvorba.cislo_pdf_filename, verbose_name='pdf'), + field=models.FileField(blank=True, help_text='PDF čísla, které si mohou řešitelé stáhnout', null=True, storage=various.models.OverwriteStorage(), upload_to=seminar.models.cislo_pdf_filename, verbose_name='pdf'), ), ] diff --git a/seminar/models/__init__.py b/seminar/models/__init__.py index e0377ff0..bd660e0c 100644 --- a/seminar/models/__init__.py +++ b/seminar/models/__init__.py @@ -1,6 +1,3 @@ -from .tvorba import * -from .base import * - from various.models import Nastaveni from personalni.models import Organizator, Resitel, Skola, Prijemce, Osoba from soustredeni.models import Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Konfera, Konfery_Ucastnici diff --git a/seminar/models/base.py b/seminar/models/base.py deleted file mode 100644 index 6ea34242..00000000 --- a/seminar/models/base.py +++ /dev/null @@ -1,23 +0,0 @@ -from django.urls import reverse -from django.db import models - - -class SeminarModelBase(models.Model): - - class Meta: - abstract = True - - def verejne(self): - return False - - # def get_absolute_url(self): - # return "https://" + str(get_current_site(None)) + self.verejne_url() - - def admin_url(self): - app_name = self._meta.app_label - model_name = self._meta.model_name - return reverse('admin:{}_{}_change'.format(app_name, model_name), args=(self.id, )) - -# def verejne_url(self): -# return None - diff --git a/seminar/models/tvorba.py b/seminar/models/tvorba.py deleted file mode 100644 index c1f91196..00000000 --- a/seminar/models/tvorba.py +++ /dev/null @@ -1,14 +0,0 @@ -import os -import logging - -from django.core.files.storage import FileSystemStorage - -logger = logging.getLogger(__name__) - -class OverwriteStorage(FileSystemStorage): - """ Varianta FileSystemStorage, která v případě, že soubor cílového - jména již existuje, ho smaže a místo něj uloží soubor nový""" - def get_available_name(self,name, max_length=None): - if self.exists(name): - os.remove(os.path.join(self.location,name)) - return super().get_available_name(name,max_length) diff --git a/soustredeni/models.py b/soustredeni/models.py index 8a6f6853..57e3741f 100644 --- a/soustredeni/models.py +++ b/soustredeni/models.py @@ -9,7 +9,7 @@ from django.conf import settings from personalni.models import Resitel, Organizator -from seminar.models.base import SeminarModelBase +from various.models import SeminarModelBase from tvorba.models import Rocnik, Problem, aux_generate_filename logger = logging.getLogger(__name__) diff --git a/treenode/models.py b/treenode/models.py index e5f91c4d..54e9b896 100644 --- a/treenode/models.py +++ b/treenode/models.py @@ -8,7 +8,7 @@ from unidecode import unidecode # Používám pro získání ID odkazu (ještě from polymorphic.models import PolymorphicModel -from seminar.models import SeminarModelBase +from various.models import SeminarModelBase from personalni.models import Organizator from odevzdavatko.models import Reseni diff --git a/tvorba/migrations/0001_tvorba_create.py b/tvorba/migrations/0001_tvorba_create.py index 085cb5b4..e8a6b027 100644 --- a/tvorba/migrations/0001_tvorba_create.py +++ b/tvorba/migrations/0001_tvorba_create.py @@ -4,7 +4,7 @@ import datetime from django.db import migrations, models import django.db.models.deletion import django.utils.timezone -import seminar.models.tvorba +import various.models import tvorba.models import taggit.managers @@ -35,7 +35,7 @@ class Migration(migrations.Migration): ('datum_vydani', models.DateField(blank=True, help_text='Datum vydání finální verze', null=True, verbose_name='datum vydání')), ('verejne_db', models.BooleanField(db_column='verejne', default=False, verbose_name='číslo zveřejněno')), ('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k číslu (plain text)', verbose_name='neveřejná poznámka')), - ('pdf', models.FileField(blank=True, help_text='PDF čísla, které si mohou řešitelé stáhnout', null=True, storage=seminar.models.tvorba.OverwriteStorage(), upload_to=tvorba.models.cislo_pdf_filename, verbose_name='pdf')), + ('pdf', models.FileField(blank=True, help_text='PDF čísla, které si mohou řešitelé stáhnout', null=True, storage=various.models.OverwriteStorage(), upload_to=tvorba.models.cislo_pdf_filename, verbose_name='pdf')), ('titulka_nahled', models.ImageField(blank=True, help_text='Obrázek titulní strany, generuje se automaticky', null=True, upload_to=tvorba.models.cislo_png_filename, verbose_name='Obrázek titulní strany')), ('rocnik', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='cisla', to='tvorba.rocnik', verbose_name='ročník')), ], diff --git a/tvorba/models.py b/tvorba/models.py index c00d0935..3a3a6b4e 100644 --- a/tvorba/models.py +++ b/tvorba/models.py @@ -31,8 +31,7 @@ from polymorphic.models import PolymorphicModel from django.core.mail import EmailMessage -from seminar.models.base import SeminarModelBase -from seminar.models.tvorba import OverwriteStorage +from various.models import SeminarModelBase, OverwriteStorage from personalni.models import Prijemce, Organizator logger = logging.getLogger(__name__) diff --git a/various/models.py b/various/models.py index 85ba4702..363c3137 100644 --- a/various/models.py +++ b/various/models.py @@ -1,11 +1,12 @@ +import os + from django.db import models +from django.core.files.storage import FileSystemStorage +from django.urls import reverse from reversion import revisions as reversion from solo.models import SingletonModel -from tvorba.models import Cislo - -from django.urls import reverse @reversion.register(ignore_duplicates=True) class Nastaveni(SingletonModel): @@ -14,10 +15,10 @@ class Nastaveni(SingletonModel): db_table = 'seminar_nastaveni' verbose_name = 'Nastavení semináře' -# aktualni_rocnik = models.ForeignKey(Rocnik, verbose_name='aktuální ročník', +# aktualni_rocnik = models.ForeignKey("tvorba.Rocnik", verbose_name='aktuální ročník', # null=False, on_delete=models.PROTECT) - aktualni_cislo = models.ForeignKey(Cislo, verbose_name='Aktuální číslo', + aktualni_cislo = models.ForeignKey("tvorba.Cislo", verbose_name='Aktuální číslo', null=True, blank=False, on_delete=models.PROTECT, ) @@ -37,3 +38,28 @@ class Nastaveni(SingletonModel): def verejne(self): return False + + +class OverwriteStorage(FileSystemStorage): + """ Varianta FileSystemStorage, která v případě, že soubor cílového + jména již existuje, ho smaže a místo něj uloží soubor nový""" + def get_available_name(self,name, max_length=None): + if self.exists(name): + os.remove(os.path.join(self.location,name)) + return super().get_available_name(name,max_length) + + +class SeminarModelBase(models.Model): + class Meta: + abstract = True + + def verejne(self): + return False + + # def get_absolute_url(self): + # return "https://" + str(get_current_site(None)) + self.verejne_url() + + def admin_url(self): + app_name = self._meta.app_label + model_name = self._meta.model_name + return reverse('admin:{}_{}_change'.format(app_name, model_name), args=(self.id, )) From f5c2e2212142dfd640625121740367215ac2672f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 5 Nov 2024 18:54:01 +0100 Subject: [PATCH 45/51] =?UTF-8?q?Zapomenut=C3=BD=20seminar.models?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- treenode/templatetags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/treenode/templatetags.py b/treenode/templatetags.py index 701965c5..d2d3d83e 100644 --- a/treenode/templatetags.py +++ b/treenode/templatetags.py @@ -49,7 +49,7 @@ def isKonfera(value): @register.filter def isClanek(value): # FIXME neexistuje - # return isinstance(value, seminar.models.ClanekNode) + # return isinstance(value, ClanekNode) return False @register.filter From 8156f7828e0d50311c5aab8dd6e4ad0d3eeb1fae Mon Sep 17 00:00:00 2001 From: om Date: Tue, 5 Nov 2024 19:25:36 +0100 Subject: [PATCH 46/51] +5 let limit maturity --- personalni/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/personalni/forms.py b/personalni/forms.py index ea0891e7..e9e98dbc 100644 --- a/personalni/forms.py +++ b/personalni/forms.py @@ -62,7 +62,7 @@ class UdajeForm(forms.Form): rok_maturity = forms.IntegerField( label='Rok maturity', min_value=date.today().year, - max_value=date.today().year+8, + max_value=date.today().year+13, required=True, ) From c1938c8ff7f2641d6994657de85f1c6ddaa159c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 5 Nov 2024 19:56:07 +0100 Subject: [PATCH 47/51] =?UTF-8?q?Odst=C5=99el=20funkc=C3=AD=20pro=20migrac?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/migrations/0001_initial.py | 5 +++-- .../0001_squashed_0098_auto_20210906_0305.py | 21 +++++++++++-------- seminar/migrations/0002_add_body_views.py | 6 +----- .../migrations/0028_add_body_celkem_views.py | 6 +----- .../migrations/0029_fix_body_celkem_views.py | 6 +----- seminar/migrations/0031_cislo_pdf.py | 4 ++-- .../migrations/0032_cislo_pdf_blank_typos.py | 5 +++-- seminar/migrations/0041_konfery.py | 7 ++++--- seminar/migrations/0042_auto_20161005_0847.py | 6 +++--- seminar/migrations/0081_auto_20200408_2221.py | 5 +++-- seminar/migrations/0082_auto_20200506_1951.py | 4 ++-- seminar/migrations/0100_auto_20211129_2354.py | 4 ++-- seminar/migrations/0103_deadline.py | 12 +++++------ .../0105_odstraneni_deadlinu_cisla.py | 2 +- .../0106_remove_cislo_verejna_vysledkovka.py | 2 +- seminar/models.py | 12 +++++++++++ seminar/models/__init__.py | 18 ---------------- 17 files changed, 57 insertions(+), 68 deletions(-) create mode 100644 seminar/models.py delete mode 100644 seminar/models/__init__.py diff --git a/seminar/migrations/0001_initial.py b/seminar/migrations/0001_initial.py index cd36a7dc..9b7a35a5 100644 --- a/seminar/migrations/0001_initial.py +++ b/seminar/migrations/0001_initial.py @@ -2,10 +2,11 @@ from __future__ import unicode_literals from django.db import models, migrations import django_countries.fields -import seminar.models import django.utils.timezone from django.conf import settings +from odevzdavatko.models import generate_filename + class Migration(migrations.Migration): @@ -75,7 +76,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(serialize=False, primary_key=True)), ('timestamp', models.DateTimeField(auto_now=True, verbose_name='vytvo\u0159eno')), - ('soubor', models.FileField(upload_to=seminar.models.generate_filename, verbose_name='soubor')), + ('soubor', models.FileField(upload_to=generate_filename, verbose_name='soubor')), ('poznamka', models.TextField(help_text='Neve\u0159ejn\xe1 pozn\xe1mka k p\u0159\xedloze \u0159e\u0161en\xed (plain text), nap\u0159. o p\u016fvodu', verbose_name='neve\u0159ejn\xe1 pozn\xe1mka', blank=True)), ], options={ diff --git a/seminar/migrations/0001_squashed_0098_auto_20210906_0305.py b/seminar/migrations/0001_squashed_0098_auto_20210906_0305.py index a95f9c1b..f9eea4fe 100644 --- a/seminar/migrations/0001_squashed_0098_auto_20210906_0305.py +++ b/seminar/migrations/0001_squashed_0098_auto_20210906_0305.py @@ -7,9 +7,12 @@ import django.db.models.deletion import django.utils.timezone import django_countries.fields import imagekit.models.fields -import seminar.models import taggit.managers +from soustredeni.models import generate_filename_konfera +from odevzdavatko.models import generate_filename +from tvorba.models import cislo_pdf_filename, cislo_png_filename + from datetime import date from django.db.models import Q from treenode.treelib import get_parent @@ -962,7 +965,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(primary_key=True, serialize=False)), ('timestamp', models.DateTimeField(auto_now=True, verbose_name='vytvořeno')), - ('soubor', models.FileField(upload_to=seminar.models.generate_filename, verbose_name='soubor')), + ('soubor', models.FileField(upload_to=generate_filename, verbose_name='soubor')), ('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu', verbose_name='neveřejná poznámka')), ('reseni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='prilohy', to='seminar.Reseni', verbose_name='řešení')), ], @@ -1284,7 +1287,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='cislo', name='pdf', - field=models.FileField(blank=True, help_text='Pdf čísla, které si mohou řešitelé stáhnout', null=True, upload_to=seminar.models.cislo_pdf_filename, verbose_name='pdf'), + field=models.FileField(blank=True, help_text='Pdf čísla, které si mohou řešitelé stáhnout', null=True, upload_to=cislo_pdf_filename, verbose_name='pdf'), ), migrations.AlterField( model_name='problem', @@ -1361,8 +1364,8 @@ class Migration(migrations.Migration): ('abstrakt', models.TextField(blank=True, help_text='Abstrakt konfery tak, jak byl uveden ve sborníku', verbose_name='abstrakt')), ('org_poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka ke konfeře(plain text)', verbose_name='neveřejná poznámka')), ('typ_prezentace', models.CharField(choices=[(b'veletrh', 'Veletrh (postery)'), (b'prezentace', 'Prezentace (přednáška)')], default=b'veletrh', max_length=16, verbose_name='typ prezentace')), - ('prezentace', models.FileField(help_text='Prezentace nebo fotka posteru', upload_to=seminar.models.generate_filename_konfera, verbose_name='prezentace')), - ('materialy', models.FileField(help_text='Další materiály ke konfeře zabalené do jednoho souboru', upload_to=seminar.models.generate_filename_konfera, verbose_name='materialy')), + ('prezentace', models.FileField(help_text='Prezentace nebo fotka posteru', upload_to=generate_filename_konfera, verbose_name='prezentace')), + ('materialy', models.FileField(help_text='Další materiály ke konfeře zabalené do jednoho souboru', upload_to=generate_filename_konfera, verbose_name='materialy')), ('organizator', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='konfery', to='seminar.Organizator', verbose_name='organizátor')), ('soustredeni', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='konfery', to='seminar.Soustredeni', verbose_name='soustředění')), ], @@ -1400,12 +1403,12 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='konfera', name='materialy', - field=models.FileField(blank=True, help_text='Další materiály ke konfeře zabalené do jednoho souboru', upload_to=seminar.models.generate_filename_konfera, verbose_name='materialy'), + field=models.FileField(blank=True, help_text='Další materiály ke konfeře zabalené do jednoho souboru', upload_to=generate_filename_konfera, verbose_name='materialy'), ), migrations.AlterField( model_name='konfera', name='prezentace', - field=models.FileField(blank=True, help_text='Prezentace nebo fotka posteru', upload_to=seminar.models.generate_filename_konfera, verbose_name='prezentace'), + field=models.FileField(blank=True, help_text='Prezentace nebo fotka posteru', upload_to=generate_filename_konfera, verbose_name='prezentace'), ), migrations.AddField( model_name='konfera', @@ -2648,12 +2651,12 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='cislo', name='pdf', - field=models.FileField(blank=True, help_text='PDF čísla, které si mohou řešitelé stáhnout', null=True, upload_to=seminar.models.cislo_pdf_filename, verbose_name='pdf'), + field=models.FileField(blank=True, help_text='PDF čísla, které si mohou řešitelé stáhnout', null=True, upload_to=cislo_pdf_filename, verbose_name='pdf'), ), migrations.AddField( model_name='cislo', name='titulka_nahled', - field=models.ImageField(blank=True, help_text='Obrázek titulní strany, generuje se automaticky', null=True, upload_to=seminar.models.cislo_png_filename, verbose_name='Obrázek titulní strany'), + field=models.ImageField(blank=True, help_text='Obrázek titulní strany, generuje se automaticky', null=True, upload_to=cislo_png_filename, verbose_name='Obrázek titulní strany'), ), migrations.AlterField( model_name='treenode', diff --git a/seminar/migrations/0002_add_body_views.py b/seminar/migrations/0002_add_body_views.py index 1ece029e..19851d58 100644 --- a/seminar/migrations/0002_add_body_views.py +++ b/seminar/migrations/0002_add_body_views.py @@ -1,10 +1,6 @@ from __future__ import unicode_literals -from django.db import models, migrations -import django_countries.fields -import seminar.models -import django.utils.timezone -from django.conf import settings +from django.db import migrations CREATE_VIEWS=""" create view seminar_body_za_cislo as diff --git a/seminar/migrations/0028_add_body_celkem_views.py b/seminar/migrations/0028_add_body_celkem_views.py index c26baec2..b4e7fd90 100644 --- a/seminar/migrations/0028_add_body_celkem_views.py +++ b/seminar/migrations/0028_add_body_celkem_views.py @@ -1,10 +1,6 @@ from __future__ import unicode_literals -from django.db import models, migrations -import django_countries.fields -import seminar.models -import django.utils.timezone -from django.conf import settings +from django.db import migrations CREATE_VIEWS=""" drop view seminar_body_k_cislu; diff --git a/seminar/migrations/0029_fix_body_celkem_views.py b/seminar/migrations/0029_fix_body_celkem_views.py index b2b9f24a..8577f3e9 100644 --- a/seminar/migrations/0029_fix_body_celkem_views.py +++ b/seminar/migrations/0029_fix_body_celkem_views.py @@ -1,10 +1,6 @@ from __future__ import unicode_literals -from django.db import models, migrations -import django_countries.fields -import seminar.models -import django.utils.timezone -from django.conf import settings +from django.db import migrations CREATE_VIEWS=""" diff --git a/seminar/migrations/0031_cislo_pdf.py b/seminar/migrations/0031_cislo_pdf.py index b2b65102..8392a581 100644 --- a/seminar/migrations/0031_cislo_pdf.py +++ b/seminar/migrations/0031_cislo_pdf.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals from django.db import models, migrations -import seminar.models +from tvorba.models import cislo_pdf_filename class Migration(migrations.Migration): @@ -14,7 +14,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='cislo', name='pdf', - field=models.FileField(help_text='Pdf \u010d\xedsla, kter\xe9 si mohou \u0159e\u0161itel\xe9 st\xe1hnout', upload_to=seminar.models.cislo_pdf_filename, null=True, verbose_name='pdf'), + field=models.FileField(help_text='Pdf \u010d\xedsla, kter\xe9 si mohou \u0159e\u0161itel\xe9 st\xe1hnout', upload_to=cislo_pdf_filename, null=True, verbose_name='pdf'), preserve_default=True, ), ] diff --git a/seminar/migrations/0032_cislo_pdf_blank_typos.py b/seminar/migrations/0032_cislo_pdf_blank_typos.py index b72ad75d..3d37d347 100644 --- a/seminar/migrations/0032_cislo_pdf_blank_typos.py +++ b/seminar/migrations/0032_cislo_pdf_blank_typos.py @@ -2,7 +2,8 @@ from __future__ import unicode_literals from django.db import models, migrations import django_countries.fields -import seminar.models + +from tvorba.models import cislo_pdf_filename class Migration(migrations.Migration): @@ -25,7 +26,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='cislo', name='pdf', - field=models.FileField(help_text='Pdf \u010d\xedsla, kter\xe9 si mohou \u0159e\u0161itel\xe9 st\xe1hnout', upload_to=seminar.models.cislo_pdf_filename, null=True, verbose_name='pdf', blank=True), + field=models.FileField(help_text='Pdf \u010d\xedsla, kter\xe9 si mohou \u0159e\u0161itel\xe9 st\xe1hnout', upload_to=cislo_pdf_filename, null=True, verbose_name='pdf', blank=True), preserve_default=True, ), migrations.AlterField( diff --git a/seminar/migrations/0041_konfery.py b/seminar/migrations/0041_konfery.py index bb040465..d4941c3b 100644 --- a/seminar/migrations/0041_konfery.py +++ b/seminar/migrations/0041_konfery.py @@ -2,7 +2,8 @@ from __future__ import unicode_literals from django.db import migrations, models import django.db.models.deletion -import seminar.models + +from soustredeni.models import generate_filename_konfera class Migration(migrations.Migration): @@ -21,8 +22,8 @@ class Migration(migrations.Migration): ('abstrakt', models.TextField(help_text='Abstrakt konfery tak, jak byl uveden ve sborn\xedku', verbose_name='abstrakt', blank=True)), ('org_poznamka', models.TextField(help_text='Neve\u0159ejn\xe1 pozn\xe1mka ke konfe\u0159e(plain text)', verbose_name='neve\u0159ejn\xe1 pozn\xe1mka', blank=True)), ('typ_prezentace', models.CharField(default=b'veletrh', max_length=16, verbose_name='typ prezentace', choices=[(b'veletrh', 'Veletrh (postery)'), (b'prezentace', 'Prezentace (p\u0159edn\xe1\u0161ka)')])), - ('prezentace', models.FileField(help_text='Prezentace nebo fotka posteru', upload_to=seminar.models.generate_filename_konfera, verbose_name='prezentace')), - ('materialy', models.FileField(help_text='Dal\u0161\xed materi\xe1ly ke konfe\u0159e zabalen\xe9 do jednoho souboru', upload_to=seminar.models.generate_filename_konfera, verbose_name='materialy')), + ('prezentace', models.FileField(help_text='Prezentace nebo fotka posteru', upload_to=generate_filename_konfera, verbose_name='prezentace')), + ('materialy', models.FileField(help_text='Dal\u0161\xed materi\xe1ly ke konfe\u0159e zabalen\xe9 do jednoho souboru', upload_to=generate_filename_konfera, verbose_name='materialy')), ('organizator', models.ForeignKey(related_name='konfery', on_delete=django.db.models.deletion.SET_NULL, verbose_name='organiz\xe1tor', to='seminar.Organizator', null=True)), ], options={ diff --git a/seminar/migrations/0042_auto_20161005_0847.py b/seminar/migrations/0042_auto_20161005_0847.py index e606e325..c06986c3 100644 --- a/seminar/migrations/0042_auto_20161005_0847.py +++ b/seminar/migrations/0042_auto_20161005_0847.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.db import migrations, models import django.db.models.deletion -import seminar.models +from soustredeni.models import generate_filename_konfera class Migration(migrations.Migration): @@ -15,12 +15,12 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='konfera', name='materialy', - field=models.FileField(help_text='Dal\u0161\xed materi\xe1ly ke konfe\u0159e zabalen\xe9 do jednoho souboru', upload_to=seminar.models.generate_filename_konfera, verbose_name='materialy', blank=True), + field=models.FileField(help_text='Dal\u0161\xed materi\xe1ly ke konfe\u0159e zabalen\xe9 do jednoho souboru', upload_to=generate_filename_konfera, verbose_name='materialy', blank=True), ), migrations.AlterField( model_name='konfera', name='prezentace', - field=models.FileField(help_text='Prezentace nebo fotka posteru', upload_to=seminar.models.generate_filename_konfera, verbose_name='prezentace', blank=True), + field=models.FileField(help_text='Prezentace nebo fotka posteru', upload_to=generate_filename_konfera, verbose_name='prezentace', blank=True), ), migrations.AlterField( model_name='konfera', diff --git a/seminar/migrations/0081_auto_20200408_2221.py b/seminar/migrations/0081_auto_20200408_2221.py index 6cf908bb..0f1edb75 100644 --- a/seminar/migrations/0081_auto_20200408_2221.py +++ b/seminar/migrations/0081_auto_20200408_2221.py @@ -1,7 +1,8 @@ # Generated by Django 2.2.9 on 2020-04-08 20:21 from django.db import migrations, models -import seminar.models + +from tvorba.models import cislo_pdf_filename class Migration(migrations.Migration): @@ -19,6 +20,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='cislo', name='pdf', - field=models.FileField(blank=True, help_text='PDF čísla, které si mohou řešitelé stáhnout', null=True, upload_to=seminar.models.cislo_pdf_filename, verbose_name='pdf'), + field=models.FileField(blank=True, help_text='PDF čísla, které si mohou řešitelé stáhnout', null=True, upload_to=cislo_pdf_filename, verbose_name='pdf'), ), ] diff --git a/seminar/migrations/0082_auto_20200506_1951.py b/seminar/migrations/0082_auto_20200506_1951.py index 3132c434..99ef70e9 100644 --- a/seminar/migrations/0082_auto_20200506_1951.py +++ b/seminar/migrations/0082_auto_20200506_1951.py @@ -1,7 +1,7 @@ # Generated by Django 2.2.12 on 2020-05-06 17:51 from django.db import migrations, models -import seminar.models +from tvorba.models import cislo_png_filename class Migration(migrations.Migration): @@ -14,6 +14,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='cislo', name='titulka_nahled', - field=models.ImageField(blank=True, help_text='Obrázek titulní strany, generuje se automaticky', null=True, upload_to=seminar.models.cislo_png_filename, verbose_name='Obrázek titulní strany'), + field=models.ImageField(blank=True, help_text='Obrázek titulní strany, generuje se automaticky', null=True, upload_to=cislo_png_filename, verbose_name='Obrázek titulní strany'), ), ] diff --git a/seminar/migrations/0100_auto_20211129_2354.py b/seminar/migrations/0100_auto_20211129_2354.py index 87f0b824..dfe92833 100644 --- a/seminar/migrations/0100_auto_20211129_2354.py +++ b/seminar/migrations/0100_auto_20211129_2354.py @@ -2,7 +2,7 @@ from django.db import migrations, models import various.models -import seminar.models +from tvorba.models import cislo_pdf_filename class Migration(migrations.Migration): @@ -15,6 +15,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='cislo', name='pdf', - field=models.FileField(blank=True, help_text='PDF čísla, které si mohou řešitelé stáhnout', null=True, storage=various.models.OverwriteStorage(), upload_to=seminar.models.cislo_pdf_filename, verbose_name='pdf'), + field=models.FileField(blank=True, help_text='PDF čísla, které si mohou řešitelé stáhnout', null=True, storage=various.models.OverwriteStorage(), upload_to=cislo_pdf_filename, verbose_name='pdf'), ), ] diff --git a/seminar/migrations/0103_deadline.py b/seminar/migrations/0103_deadline.py index 78577373..ba24df13 100644 --- a/seminar/migrations/0103_deadline.py +++ b/seminar/migrations/0103_deadline.py @@ -5,7 +5,7 @@ from django.db import migrations, models import django.db.models.deletion from django.utils import timezone -import seminar.models as m +from tvorba.models import Deadline as mDeadline def vytvor_deadliny(apps, schema_editor): @@ -16,7 +16,7 @@ def vytvor_deadliny(apps, schema_editor): if cislo.rocnik.rocnik < 26: Deadline.objects.create( cislo=cislo, - typ=m.Deadline.TYP_CISLA, + typ=mDeadline.TYP_CISLA, deadline=timezone.make_aware(datetime.datetime.combine(datetime.date(1994 + cislo.rocnik.rocnik, 6, int(cislo.poradi[0])), datetime.time.min)), verejna_vysledkovka=cislo.verejna_vysledkovka, ) @@ -33,24 +33,24 @@ def vytvor_deadliny(apps, schema_editor): if cislo.datum_deadline_soustredeni and cislo.datum_deadline_soustredeni == cislo.datum_preddeadline: vytvor_deadline( date=cislo.datum_deadline_soustredeni, - typ=m.Deadline.TYP_PRVNI_A_SOUS + typ=mDeadline.TYP_PRVNI_A_SOUS ) else: if cislo.datum_deadline_soustredeni: vytvor_deadline( date=cislo.datum_deadline_soustredeni, - typ=m.Deadline.TYP_SOUS + typ=mDeadline.TYP_SOUS ) if cislo.datum_preddeadline: vytvor_deadline( date=cislo.datum_preddeadline, - typ=m.Deadline.TYP_PRVNI + typ=mDeadline.TYP_PRVNI ) if cislo.datum_deadline: vytvor_deadline( date=cislo.datum_deadline, - typ=m.Deadline.TYP_CISLA + typ=mDeadline.TYP_CISLA ) diff --git a/seminar/migrations/0105_odstraneni_deadlinu_cisla.py b/seminar/migrations/0105_odstraneni_deadlinu_cisla.py index c3ab3541..262de179 100644 --- a/seminar/migrations/0105_odstraneni_deadlinu_cisla.py +++ b/seminar/migrations/0105_odstraneni_deadlinu_cisla.py @@ -1,7 +1,7 @@ # Generated by Django 3.2.15 on 2022-10-09 10:14 from django.db import migrations -from seminar.models import Deadline +from tvorba.models import Deadline def vrat_deadliny(apps, schema_editor): diff --git a/seminar/migrations/0106_remove_cislo_verejna_vysledkovka.py b/seminar/migrations/0106_remove_cislo_verejna_vysledkovka.py index edccf3c9..e9e652b3 100644 --- a/seminar/migrations/0106_remove_cislo_verejna_vysledkovka.py +++ b/seminar/migrations/0106_remove_cislo_verejna_vysledkovka.py @@ -1,7 +1,7 @@ # Generated by Django 3.2.15 on 2022-10-09 11:04 from django.db import migrations -from seminar.models import Deadline +from tvorba.models import Deadline def vrat_verejnost(apps, schema_editor): diff --git a/seminar/models.py b/seminar/models.py new file mode 100644 index 00000000..8c034a82 --- /dev/null +++ b/seminar/models.py @@ -0,0 +1,12 @@ +# Tento soubor slouží pouze pro shell a podobné. Nikde neimportovat v kódu! +from galerie.models import * +from header_fotky.models import * +from korektury.models import * +from novinky.models import * +from odevzdavatko.models import * +from personalni.models import * +from prednasky.models import * +from soustredeni.models import * +from treenode.models import * +from tvorba.models import * +from various.models import * diff --git a/seminar/models/__init__.py b/seminar/models/__init__.py deleted file mode 100644 index bd660e0c..00000000 --- a/seminar/models/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -from various.models import Nastaveni -from personalni.models import Organizator, Resitel, Skola, Prijemce, Osoba -from soustredeni.models import Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Konfera, Konfery_Ucastnici -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 -# migr. 0001 -from odevzdavatko.models import generate_filename -# migr. 0031, 0032, 0081 -from tvorba.models import cislo_pdf_filename -# migr. 0082 -from tvorba.models import cislo_png_filename -# migr 0100 (hack) -import tvorba.models as tvorba From 6df9665af351bc8f9b56f4f8d0baca01ad5650d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 5 Nov 2024 20:15:41 +0100 Subject: [PATCH 48/51] =?UTF-8?q?Migrace=20uti=C5=A1uj=C3=ADc=C3=AD=20Djan?= =?UTF-8?q?go=20(stejn=C4=9B=20se=20default=20nikdy=20nepou=C5=BE=C3=ADv?= =?UTF-8?q?=C3=A1).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../0006_alter_deadline_deadline.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tvorba/migrations/0006_alter_deadline_deadline.py diff --git a/tvorba/migrations/0006_alter_deadline_deadline.py b/tvorba/migrations/0006_alter_deadline_deadline.py new file mode 100644 index 00000000..4191b52f --- /dev/null +++ b/tvorba/migrations/0006_alter_deadline_deadline.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.16 on 2024-11-05 18:56 +# +# Tahle migrace je tu jen, aby Django nechtělo vyrábět migrace. Smiřte se s tím. +# Stejně se default u Deadlinu nikdy nepoužívá. + +import datetime +from django.db import migrations, models +from django.utils import timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('tvorba', '0005_odstrel_treenode_post'), + ] + + operations = [ + migrations.AlterField( + model_name='deadline', + name='deadline', + field=models.DateTimeField(default=timezone.make_aware(datetime.datetime.combine(timezone.now(), datetime.time.max))), + ), + ] From 71948383d229280656d2795d7455aaa2705314c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 5 Nov 2024 20:19:34 +0100 Subject: [PATCH 49/51] Upraven popis aplikace seminar --- seminar/__init__.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/seminar/__init__.py b/seminar/__init__.py index 09ee5004..849682d8 100644 --- a/seminar/__init__.py +++ b/seminar/__init__.py @@ -1,6 +1,4 @@ """ -Zde bývalo vše. Teď tu zbývají všechny modely a části webu jako archiv, -přehled orgů, aktuální (k aktuálnímu číslu) věci, titulka a jak řešit. - -Také je tu generování testovacích (lokálních) dat. -""" \ No newline at end of file +Zde bývalo vše. Teď tu zbývají migrace. +A kód pro `import seminar.models as m` pro ./manage.py shell. +""" From 227d438ebd8e7e1ff3a2efc6bbd37d233ec540ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 5 Nov 2024 20:22:04 +0100 Subject: [PATCH 50/51] =?UTF-8?q?Posledn=C3=AD=20obrana=20proti=20importov?= =?UTF-8?q?=C3=A1n=C3=AD=20seminar.models?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/seminar/models.py b/seminar/models.py index 8c034a82..efb5e48c 100644 --- a/seminar/models.py +++ b/seminar/models.py @@ -1,4 +1,7 @@ # Tento soubor slouží pouze pro shell a podobné. Nikde neimportovat v kódu! + +print("Naimportoval jsi `seminar.models`. Pevně věřím, že to nebylo nikde v kódu. Díky.") + from galerie.models import * from header_fotky.models import * from korektury.models import * From bf726df1170bc0ed9e2dfb709c406c7369e524ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 5 Nov 2024 20:30:55 +0100 Subject: [PATCH 51/51] =?UTF-8?q?P=C5=99eps=C3=A1n=C3=AD=20loggeru,=20prot?= =?UTF-8?q?o=C5=BEe=20to=20vypad=C3=A1=20l=C3=A9pe=20(i=20kdyby=20to=20nef?= =?UTF-8?q?ungovalo)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- personalni/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/personalni/forms.py b/personalni/forms.py index 50b9d705..350d9b7b 100644 --- a/personalni/forms.py +++ b/personalni/forms.py @@ -147,7 +147,7 @@ class PrihlaskaForm(PasswordResetForm, UdajeForm): class ProfileEditForm(UdajeForm): - err_logger = logging.getLogger('personalni.edit.problem') + err_logger = logging.getLogger('personalni.prihlaska.problem.edit') username = forms.CharField( label='Přihlašovací jméno', max_length=256,