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] =?UTF-8?q?Dal=C5=A1=C3=AD=20=C4=8D=C3=A1ste=C4=8Dn=C3=A9?= =?UTF-8?q?=20=C5=99e=C5=A1en=C3=AD=20#1465=20(Podez=C5=99el=C3=A9=20`semi?= =?UTF-8?q?nar`e).=20Z=C3=A1m=C4=9Brn=C4=9B=20se=20vyh=C3=BDb=C3=A1=20tree?= =?UTF-8?q?node.?= 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)