Browse Source

`import seminar.models as m` na from `seminar.models.neco import neco`

v3
Jonas Havelka 1 year ago
parent
commit
e45c819424
  1. 4
      api/tests/test_skola_autocomplete.py
  2. 17
      api/views/autocomplete.py
  3. 4
      api/views/exports.py
  4. 2
      galerie/forms.py
  5. 2
      galerie/models.py
  6. 2
      galerie/views.py
  7. 2
      korektury/models.py
  8. 14
      odevzdavatko/admin.py
  9. 23
      odevzdavatko/forms.py
  10. 4
      odevzdavatko/templatetags/jmena.py
  11. 80
      odevzdavatko/views.py
  12. 16
      personalni/admin.py
  13. 2
      personalni/forms.py
  14. 4
      personalni/utils.py
  15. 42
      personalni/views.py
  16. 2
      prednasky/admin.py
  17. 3
      prednasky/models.py
  18. 3
      prednasky/views.py
  19. 108
      seminar/admin.py
  20. 2
      seminar/management/commands/generate_thumbnails.py
  21. 4
      seminar/management/commands/pregeneruj_zmrazene_vysledkovky.py
  22. 4
      seminar/management/commands/testdata.py
  23. 4
      seminar/migrations/0001_initial.py
  24. 20
      seminar/migrations/0001_squashed_0098_auto_20210906_0305.py
  25. 1
      seminar/migrations/0002_add_body_views.py
  26. 1
      seminar/migrations/0028_add_body_celkem_views.py
  27. 1
      seminar/migrations/0029_fix_body_celkem_views.py
  28. 4
      seminar/migrations/0031_cislo_pdf.py
  29. 4
      seminar/migrations/0032_cislo_pdf_blank_typos.py
  30. 6
      seminar/migrations/0041_konfery.py
  31. 6
      seminar/migrations/0042_auto_20161005_0847.py
  32. 4
      seminar/migrations/0081_auto_20200408_2221.py
  33. 4
      seminar/migrations/0082_auto_20200506_1951.py
  34. 4
      seminar/migrations/0100_auto_20211129_2354.py
  35. 12
      seminar/migrations/0103_deadline.py
  36. 2
      seminar/migrations/0105_odstraneni_deadlinu_cisla.py
  37. 2
      seminar/migrations/0106_remove_cislo_verejna_vysledkovka.py
  38. 34
      seminar/models/odevzdavatko.py
  39. 8
      seminar/models/soustredeni.py
  40. 14
      seminar/models/treenode.py
  41. 22
      seminar/templatetags/deadliny.py
  42. 43
      seminar/testutils.py
  43. 44
      seminar/utils.py
  44. 14
      seminar/views/docasne.py
  45. 56
      seminar/views/views_all.py
  46. 10
      soustredeni/admin.py
  47. 4
      soustredeni/views.py
  48. 66
      treenode/admin.py
  49. 4
      treenode/forms.py
  50. 131
      treenode/serializers.py
  51. 26
      treenode/templatetags.py
  52. 12
      treenode/tests.py
  53. 6
      treenode/treelib.py
  54. 83
      treenode/views.py
  55. 22
      treenode/viewsets.py
  56. 2
      vyroci/views.py
  57. 77
      vysledkovky/utils.py

4
api/tests/test_skola_autocomplete.py

@ -1,6 +1,6 @@
from django.test import TestCase from django.test import TestCase
from django.urls import reverse from django.urls import reverse
import seminar.models as m from seminar.models.personalni import Skola
import seminar.views as v import seminar.views as v
from seminar.utils import sync_skoly from seminar.utils import sync_skoly
@ -48,7 +48,7 @@ class OrgSkolyAutocompleteTestCase(TestCase):
"""Testuje, že pro každého orga je jeho škola ve výsledném QuerySetu""" """Testuje, že pro každého orga je jeho škola ve výsledném QuerySetu"""
for pfx, id in self.spravna_data: for pfx, id in self.spravna_data:
with self.subTest(prefix=pfx, spravne_id=id): 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í # Zeptáme se view, co si myslí
resp = self.client.get(reverse('autocomplete_skola')+'?q='+pfx).json() resp = self.client.get(reverse('autocomplete_skola')+'?q='+pfx).json()
ids = [int(x['id']) for x in resp['results']] ids = [int(x['id']) for x in resp['results']]

17
api/views/autocomplete.py

@ -5,7 +5,8 @@ from dal import autocomplete
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.db.models import Q from django.db.models import Q
import seminar.models as m from seminar.models.personalni import Skola, Resitel
from seminar.models.tvorba import Problem, Nastaveni
from .helpers import LoginRequiredAjaxMixin from .helpers import LoginRequiredAjaxMixin
# TODO filosofie - zkratky, jak v databázi, tak ve vyhledávání (SPŠE, GASOŠ, Kpt., soukr) # TODO filosofie - zkratky, jak v databázi, tak ve vyhledávání (SPŠE, GASOŠ, Kpt., soukr)
@ -13,7 +14,7 @@ class SkolaAutocomplete(autocomplete.Select2QuerySetView):
""" View k :mod:`dal.autocomplete` pro vyhledávání škol hlavně při registraci. """ """ View k :mod:`dal.autocomplete` pro vyhledávání škol hlavně při registraci. """
def get_queryset(self): def get_queryset(self):
# Don't forget to filter out results depending on the visitor ! # Don't forget to filter out results depending on the visitor !
qs = m.Skola.objects.all() qs = Skola.objects.all()
if self.q: if self.q:
words = self.q.split(' ') #TODO re split podle bileho znaku words = self.q.split(' ') #TODO re split podle bileho znaku
partq = Q() partq = Q()
@ -31,7 +32,7 @@ class SkolaAutocomplete(autocomplete.Select2QuerySetView):
class ResitelAutocomplete(LoginRequiredAjaxMixin,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. """ """ View k :mod:`dal.autocomplete` pro vyhledávání řešitelů především v odevzdávátku. """
def get_queryset(self): def get_queryset(self):
qs = m.Resitel.objects.all() qs = Resitel.objects.all()
if self.q: if self.q:
parts = self.q.split() parts = self.q.split()
query = Q() query = Q()
@ -51,8 +52,8 @@ class PublicResitelAutocomplete(LoginRequiredAjaxMixin, autocomplete.Select2Quer
především v odevzdávátku. především v odevzdávátku.
""" """
def get_queryset(self): def get_queryset(self):
letos = m.Nastaveni.get_solo().aktualni_rocnik letos = Nastaveni.get_solo().aktualni_rocnik
qs = m.Resitel.objects.filter( qs = Resitel.objects.filter(
rok_maturity__gte=letos.druhy_rok() rok_maturity__gte=letos.druhy_rok()
).filter( ).filter(
prezdivka_resitele__isnull=False prezdivka_resitele__isnull=False
@ -70,7 +71,7 @@ class PublicResitelAutocomplete(LoginRequiredAjaxMixin, autocomplete.Select2Quer
class OdevzdatelnyProblemAutocomplete(autocomplete.Select2QuerySetView): class OdevzdatelnyProblemAutocomplete(autocomplete.Select2QuerySetView):
""" View k :mod:`dal.autocomplete` pro vyhledávání problémů především v odevzdávátku. """ """ View k :mod:`dal.autocomplete` pro vyhledávání problémů především v odevzdávátku. """
def get_queryset(self): 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: if self.q:
qs = qs.filter( qs = qs.filter(
Q(nazev__icontains=self.q)) Q(nazev__icontains=self.q))
@ -87,12 +88,12 @@ class ProblemAutocomplete(autocomplete.Select2QuerySetView):
""" View k :mod:`dal.autocomplete` pro vyhledávání problémů především v odevzdávátku. """ """ View k :mod:`dal.autocomplete` pro vyhledávání problémů především v odevzdávátku. """
def get_queryset(self): def get_queryset(self):
# FIXME i starší úlohy # FIXME i starší úlohy
nastaveni = get_object_or_404(m.Nastaveni) nastaveni = get_object_or_404(Nastaveni)
rocnik = nastaveni.aktualni_rocnik rocnik = nastaveni.aktualni_rocnik
temaQ = Q(Tema___rocnik = rocnik) temaQ = Q(Tema___rocnik = rocnik)
ulohaQ = Q(Uloha___cislo_zadani__rocnik=rocnik) ulohaQ = Q(Uloha___cislo_zadani__rocnik=rocnik)
clanekQ = Q(Clanek___cislo__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: if self.q:
qs = qs.filter( qs = qs.filter(
Q(nazev__icontains=self.q)) Q(nazev__icontains=self.q))

4
api/views/exports.py

@ -1,4 +1,4 @@
import seminar.models as m from seminar.models.personalni import Skola
from django.core import serializers as ser from django.core import serializers as ser
from django.http import HttpResponse from django.http import HttpResponse
def exportSkolView(request): def exportSkolView(request):
@ -8,7 +8,7 @@ def exportSkolView(request):
# Některé fieldy nechceme: Kontaktní osoby, AESOP ID, org poznámky. # Některé fieldy nechceme: Kontaktní osoby, AESOP ID, org poznámky.
fields = ('id', 'izo', 'nazev', 'kratky_nazev', 'ulice', 'mesto', 'psc', 'stat', 'je_zs', 'je_ss') fields = ('id', 'izo', 'nazev', 'kratky_nazev', 'ulice', 'mesto', 'psc', 'stat', 'je_zs', 'je_ss')
# TODO: Použít JSONL, aby protistrana mohla číst po řádkách a nesežralo to tunu paměti úplně hned # TODO: Použít JSONL, aby protistrana mohla číst po řádkách a nesežralo to tunu paměti úplně hned
skoly_json = ser.serialize("json", m.Skola.objects.all(), fields=fields) skoly_json = ser.serialize("json", Skola.objects.all(), fields=fields)
response = HttpResponse( response = HttpResponse(
content = skoly_json, content = skoly_json,
content_type = 'text/json', content_type = 'text/json',

2
galerie/forms.py

@ -1,7 +1,7 @@
#coding: utf-8 #coding: utf-8
from django import forms from django import forms
from seminar.models import Soustredeni from seminar.models.soustredeni import Soustredeni
class KomentarForm(forms.Form): class KomentarForm(forms.Form):
komentar = forms.CharField(label = "Komentář:", max_length = 300, required=False) komentar = forms.CharField(label = "Komentář:", max_length = 300, required=False)

2
galerie/models.py

@ -8,7 +8,7 @@ from imagekit.processors import ResizeToFit, Transpose
import os import os
from seminar.models import Soustredeni from seminar.models.soustredeni import Soustredeni
VZDY=0 VZDY=0
ORG=1 ORG=1

2
galerie/views.py

@ -8,7 +8,7 @@ from django.template import RequestContext
from datetime import datetime from datetime import datetime
from galerie.models import Obrazek, Galerie from galerie.models import Obrazek, Galerie
from seminar.models import Soustredeni from seminar.models.soustredeni import Soustredeni
from galerie.forms import KomentarForm, NewGalerieForm from galerie.forms import KomentarForm, NewGalerieForm
def zobrazit(galerie, request): def zobrazit(galerie, request):

2
korektury/models.py

@ -21,7 +21,7 @@ from django.core.exceptions import ObjectDoesNotExist
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.text import get_valid_filename from django.utils.text import get_valid_filename
from seminar.models import Organizator from seminar.models.personalni import Organizator
import subprocess import subprocess
from reversion import revisions as reversion from reversion import revisions as reversion

14
odevzdavatko/admin.py

@ -1,20 +1,20 @@
from django.contrib import admin from django.contrib import admin
from django_reverse_admin import ReverseModelAdmin from django_reverse_admin import ReverseModelAdmin
import seminar.models as m from seminar.models.odevzdavatko import *
class PrilohaReseniInline(admin.TabularInline): class PrilohaReseniInline(admin.TabularInline):
model = m.PrilohaReseni model = PrilohaReseni
extra = 1 extra = 1
class Reseni_ResiteleInline(admin.TabularInline): class Reseni_ResiteleInline(admin.TabularInline):
model = m.Reseni_Resitele model = Reseni_Resitele
@admin.register(m.Reseni) @admin.register(Reseni)
class ReseniAdmin(ReverseModelAdmin): class ReseniAdmin(ReverseModelAdmin):
base_model = m.Reseni base_model = Reseni
inline_type = 'tabular' inline_type = 'tabular'
# inline_reverse = ['text_cely','resitele'] TODO vrátit zpět a zrychlit dotaz # inline_reverse = ['text_cely','resitele'] TODO vrátit zpět a zrychlit dotaz
inline_reverse = ['resitele'] inline_reverse = ['resitele']
@ -24,5 +24,5 @@ class ReseniAdmin(ReverseModelAdmin):
# inlines = [PrilohaReseniInline,Reseni_ResiteleInline] # inlines = [PrilohaReseniInline,Reseni_ResiteleInline]
admin.site.register(m.PrilohaReseni) admin.site.register(PrilohaReseni)
admin.site.register(m.Hodnoceni) admin.site.register(Hodnoceni)

23
odevzdavatko/forms.py

@ -4,8 +4,9 @@ from django.forms import formset_factory
from django.forms.models import inlineformset_factory from django.forms.models import inlineformset_factory
from django.utils import timezone from django.utils import timezone
from seminar.models import Resitel from seminar.models.personalni import Resitel
import seminar.models as m from seminar.models.tvorba import Problem, Deadline, Nastaveni
from seminar.models.odevzdavatko import *
import logging import logging
@ -22,7 +23,7 @@ class DateInput(forms.DateInput):
class PosliReseniForm(forms.Form): class PosliReseniForm(forms.Form):
problem = forms.ModelMultipleChoiceField( problem = forms.ModelMultipleChoiceField(
queryset=m.Problem.objects.all(), queryset=Problem.objects.all(),
label="Problémy", label="Problémy",
widget=autocomplete.ModelSelect2Multiple( widget=autocomplete.ModelSelect2Multiple(
url='autocomplete_problem', url='autocomplete_problem',
@ -58,7 +59,7 @@ class PosliReseniForm(forms.Form):
#cas_doruceni = models.DateTimeField('čas_doručení', default=timezone.now, blank=True) #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, #forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False,
# default=FORMA_EMAIL) # default=FORMA_EMAIL)
@ -69,7 +70,7 @@ class PosliReseniForm(forms.Form):
class NahrajReseniForm(forms.ModelForm): class NahrajReseniForm(forms.ModelForm):
class Meta: class Meta:
model = m.Reseni model = Reseni
fields = ('problem', 'resitele') fields = ('problem', 'resitele')
help_texts = {'problem':''} # Nezobrazovat help text ve formuláři help_texts = {'problem':''} # Nezobrazovat help text ve formuláři
@ -109,11 +110,11 @@ class NahrajReseniForm(forms.ModelForm):
def clean_problem(self): def clean_problem(self):
problem = self.cleaned_data.get('problem') problem = self.cleaned_data.get('problem')
for p in 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!") raise forms.ValidationError("Problém " + str(p) + " již nelze řešit!")
return problem return problem
ReseniSPrilohamiFormSet = inlineformset_factory(m.Reseni,m.PrilohaReseni, ReseniSPrilohamiFormSet = inlineformset_factory(Reseni,PrilohaReseni,
form = NahrajReseniForm, form = NahrajReseniForm,
fields = ('soubor','res_poznamka'), fields = ('soubor','res_poznamka'),
widgets = {'res_poznamka':forms.TextInput()}, widgets = {'res_poznamka':forms.TextInput()},
@ -125,7 +126,7 @@ ReseniSPrilohamiFormSet = inlineformset_factory(m.Reseni,m.PrilohaReseni,
class JednoHodnoceniForm(forms.ModelForm): class JednoHodnoceniForm(forms.ModelForm):
class Meta: class Meta:
model = m.Hodnoceni model = Hodnoceni
fields = ('problem', 'body', 'deadline_body', 'feedback',) fields = ('problem', 'body', 'deadline_body', 'feedback',)
widgets = { widgets = {
'problem': autocomplete.ModelSelect2( 'problem': autocomplete.ModelSelect2(
@ -158,7 +159,7 @@ OhodnoceniReseniFormSet = formset_factory(JednoHodnoceniForm,
class PoznamkaReseniForm(forms.ModelForm): class PoznamkaReseniForm(forms.ModelForm):
class Meta: class Meta:
model = m.Reseni model = Reseni
fields = ('poznamka',) fields = ('poznamka',)
# FIXME: Ideálně by mělo být součástí třídy níž, ale neumím to udělat # FIXME: Ideálně by mělo být součástí třídy níž, ale neumím to udělat
@ -198,7 +199,7 @@ class OdevzdavatkoTabulkaFiltrForm(forms.Form):
from django.db.utils import OperationalError from django.db.utils import OperationalError
try: try:
aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik aktualni_rocnik = Nastaveni.get_solo().aktualni_rocnik
except OperationalError: except OperationalError:
# django.db.utils.OperationalError: no such table: seminar_nastaveni # 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 # Nemáme databázi, takže to selhalo. Pro jistotu vrátíme aspoň dvě možnosti, ať to nepadá dál
@ -214,7 +215,7 @@ class OdevzdavatkoTabulkaFiltrForm(forms.Form):
result.append(("0001-01-01", f"Odjakživa")) 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(), deadline__lte=timezone.now(),
cislo__rocnik=aktualni_rocnik cislo__rocnik=aktualni_rocnik
).order_by("deadline"): ).order_by("deadline"):

4
odevzdavatko/templatetags/jmena.py

@ -2,8 +2,8 @@ from django import template
register = template.Library() register = template.Library()
from personalni.utils import normalizuj_jmeno from personalni.utils import normalizuj_jmeno
import seminar.models as m # jen kvůli typové anotaci… from seminar.models.personalni import Osoba # jen kvůli typové anotaci…
@register.filter @register.filter
def jmeno_jako_prefix(o: m.Osoba): def jmeno_jako_prefix(o: Osoba):
return normalizuj_jmeno(o).replace(' ', '_') return normalizuj_jmeno(o).replace(' ', '_')

80
odevzdavatko/views.py

@ -16,7 +16,9 @@ import datetime
from itertools import groupby from itertools import groupby
import logging import logging
import seminar.models as m from seminar.models.odevzdavatko import *
from seminar.models.tvorba import Problem, Nastaveni, Rocnik, Deadline
from seminar.models.personalni import Resitel, Organizator, Osoba
from . import forms as f from . import forms as f
from .forms import OdevzdavatkoTabulkaFiltrForm as FiltrForm from .forms import OdevzdavatkoTabulkaFiltrForm as FiltrForm
from seminar.utils import resi_v_rocniku from seminar.utils import resi_v_rocniku
@ -47,20 +49,20 @@ class SouhrnReseni:
class TabulkaOdevzdanychReseniView(ListView): class TabulkaOdevzdanychReseniView(ListView):
template_name = 'odevzdavatko/tabulka.html' template_name = 'odevzdavatko/tabulka.html'
model = m.Hodnoceni model = Hodnoceni
def inicializuj_osy_tabulky(self): def inicializuj_osy_tabulky(self):
"""Vyrobí prvotní querysety pro sloupce a řádky, tj. seznam všech řešitelů a problémů""" """Vyrobí prvotní querysety pro sloupce a řádky, tj. seznam všech řešitelů a problémů"""
# FIXME: jméno metody není vypovídající... # 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 # 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, ... # TODO: Prefetches, Select related, ...
self.resitele = m.Resitel.objects.all() self.resitele = Resitel.objects.all()
self.problemy = m.Problem.objects.all() self.problemy = Problem.objects.all()
self.reseni = m.Reseni.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: 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) form = FiltrForm(self.request.GET, rocnik=self.aktualni_rocnik)
if form.is_valid(): if form.is_valid():
@ -91,14 +93,14 @@ class TabulkaOdevzdanychReseniView(ListView):
self.resitele = self.resitele.filter(rok_maturity__gt=self.aktualni_rocnik.prvni_rok) self.resitele = self.resitele.filter(rok_maturity__gt=self.aktualni_rocnik.prvni_rok)
if problemy == FiltrForm.PROBLEMY_MOJE: 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( self.problemy = self.problemy.filter(
Q(autor=org)|Q(garant=org)|Q(opravovatele=org), 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: elif problemy == FiltrForm.PROBLEMY_LETOSNI:
self.problemy = self.problemy.filter( 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.... #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. # 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.
@ -164,7 +166,7 @@ class TabulkaOdevzdanychReseniView(ListView):
# Pro použití hacku na automatické {{form.media}} v template: # Pro použití hacku na automatické {{form.media}} v template:
ctx['form'] = ctx['filtr'] ctx['form'] = ctx['filtr']
# Pro maximum v přesměrovátku ročníků # 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
if 'rocnik' in self.kwargs: if 'rocnik' in self.kwargs:
ctx['rocnik'] = self.kwargs['rocnik'] ctx['rocnik'] = self.kwargs['rocnik']
else: else:
@ -174,7 +176,7 @@ class TabulkaOdevzdanychReseniView(ListView):
# Velmi silně inspirováno zdrojáky, FIXME: Nedá se to udělat smysluplněji? # Velmi silně inspirováno zdrojáky, FIXME: Nedá se to udělat smysluplněji?
class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixin, View): class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixin, View):
model = m.Reseni model = Reseni
template_name = 'odevzdavatko/seznam.html' template_name = 'odevzdavatko/seznam.html'
def get_queryset(self): def get_queryset(self):
@ -186,8 +188,8 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi
if problem_id is None: if problem_id is None:
raise ValueError("Nemám problém! (To je problém!)") raise ValueError("Nemám problém! (To je problém!)")
resitel = m.Resitel.objects.get(id=resitel_id) resitel = Resitel.objects.get(id=resitel_id)
problem = m.Problem.objects.get(id=problem_id) problem = Problem.objects.get(id=problem_id)
qs = qs.filter( qs = qs.filter(
problem__in=[problem], problem__in=[problem],
resitele__in=[resitel], resitele__in=[resitel],
@ -217,13 +219,13 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi
## XXX: https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#avoid-anything-more-complex ## XXX: https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#avoid-anything-more-complex
class DetailReseniView(DetailView): class DetailReseniView(DetailView):
""" Náhled na řešení. Editace je v :py:class:`EditReseniView`. """ """ Náhled na řešení. Editace je v :py:class:`EditReseniView`. """
model = m.Reseni model = Reseni
template_name = 'odevzdavatko/detail.html' template_name = 'odevzdavatko/detail.html'
def aktualni_hodnoceni(self): 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 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 = [ seznam_atributu = [
"problem", "problem",
"body", "body",
@ -280,7 +282,7 @@ class EditReseniView(DetailReseniView):
def hodnoceniReseniView(request, pk, *args, **kwargs): 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}) success_url = reverse('odevzdavatko_detail_reseni', kwargs={'pk': pk})
# FIXME: Použit initial i tady a nebastlit hodnocení tak nízkoúrovňově # FIXME: Použit initial i tady a nebastlit hodnocení tak nízkoúrovňově
@ -296,7 +298,7 @@ def hodnoceniReseniView(request, pk, *args, **kwargs):
poznamka_form.save() poznamka_form.save()
# Smažeme všechna dosavadní hodnocení tohoto řešení # 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}") logger.info(f"Will delete {qs.count()} objects: {qs}")
qs.delete() qs.delete()
@ -307,7 +309,7 @@ def hodnoceniReseniView(request, pk, *args, **kwargs):
del(data_for_hodnoceni["body_celkem"]) del(data_for_hodnoceni["body_celkem"])
del(data_for_hodnoceni["body_neprepocitane"]) del(data_for_hodnoceni["body_neprepocitane"])
del(data_for_hodnoceni["body_neprepocitane_celkem"]) del(data_for_hodnoceni["body_neprepocitane_celkem"])
hodnoceni = m.Hodnoceni( hodnoceni = Hodnoceni(
reseni=reseni, reseni=reseni,
**form.cleaned_data, **form.cleaned_data,
) )
@ -327,14 +329,14 @@ def hodnoceniReseniView(request, pk, *args, **kwargs):
class PrehledOdevzdanychReseni(ListView): class PrehledOdevzdanychReseni(ListView):
model = m.Hodnoceni model = Hodnoceni
template_name = 'odevzdavatko/prehled_reseni.html' template_name = 'odevzdavatko/prehled_reseni.html'
def get_queryset(self): def get_queryset(self):
if not self.request.user.is_authenticated: if not self.request.user.is_authenticated:
raise RuntimeError("Uživatel měl být přihlášený!") raise RuntimeError("Uživatel měl být přihlášený!")
# get_or_none, aby neexistence řešitele (např. u orgů) neházela chybu # 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 = super().get_queryset()
qs = qs.filter(reseni__resitele__in=[resitel]) 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í # Setřídíme podle času doručení řešení, aby se netřídily podle okamžiku vyrobení Hodnocení
@ -355,13 +357,13 @@ class PrehledOdevzdanychReseni(ListView):
# Přehled všech řešení kvůli debugování # Přehled všech řešení kvůli debugování
class SeznamReseniView(ListView): class SeznamReseniView(ListView):
model = m.Reseni model = Reseni
template_name = 'odevzdavatko/seznam.html' template_name = 'odevzdavatko/seznam.html'
class SeznamAktualnichReseniView(SeznamReseniView): class SeznamAktualnichReseniView(SeznamReseniView):
def get_queryset(self): def get_queryset(self):
qs = super().get_queryset() 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) 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 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 return qs
@ -373,7 +375,7 @@ class VlozReseniView(LoginRequiredMixin, FormView):
def form_valid(self, form): def form_valid(self, form):
data = form.cleaned_data data = form.cleaned_data
nove_reseni = m.Reseni.objects.create( nove_reseni = Reseni.objects.create(
cas_doruceni=data['cas_doruceni'], cas_doruceni=data['cas_doruceni'],
forma=data['forma'], forma=data['forma'],
poznamka=data['poznamka'], poznamka=data['poznamka'],
@ -400,35 +402,35 @@ class VlozReseniView(LoginRequiredMixin, FormView):
class NahrajReseniRozcestnikTematekView(LoginRequiredMixin, ListView): class NahrajReseniRozcestnikTematekView(LoginRequiredMixin, ListView):
model = m.Problem model = Problem
template_name = 'odevzdavatko/nahraj_reseni_nadproblem.html' template_name = 'odevzdavatko/nahraj_reseni_nadproblem.html'
def get_queryset(self): 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): class NahrajReseniView(LoginRequiredMixin, CreateView):
model = m.Reseni model = Reseni
template_name = 'odevzdavatko/nahraj_reseni.html' template_name = 'odevzdavatko/nahraj_reseni.html'
form_class = f.NahrajReseniForm form_class = f.NahrajReseniForm
nadproblem: m.Problem nadproblem: Problem
def setup(self, request, *args, **kwargs): def setup(self, request, *args, **kwargs):
super().setup(request, *args, **kwargs) super().setup(request, *args, **kwargs)
nadproblem_id = self.kwargs["nadproblem_id"] 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): def get(self, request, *args, **kwargs):
# Zaříznutí nezadaných problémů # Zaříznutí nezadaných problémů
if self.nadproblem.stav != m.Problem.STAV_ZADANY: if self.nadproblem.stav != Problem.STAV_ZADANY:
raise PermissionDenied() raise PermissionDenied()
# Zaříznutí starých řešitelů: # Zaříznutí starých řešitelů:
# FIXME: Je to tady dost naprasené, mělo by to asi být jinde… # 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 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', { return render(request, 'universal.html', {
'title': 'Nelze odevzdat', 'title': 'Nelze odevzdat',
'error': 'Zdá se, že jsi již odmaturoval/a, a tedy nemůžeš odevzdat do našeho semináře řešení.', 'error': 'Zdá se, že jsi již odmaturoval/a, a tedy nemůžeš odevzdat do našeho semináře řešení.',
@ -440,7 +442,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
nadproblem_id = self.nadproblem.id nadproblem_id = self.nadproblem.id
return { return {
"nadproblem_id": nadproblem_id, "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
} }
@ -452,7 +454,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
data['prilohy'] = f.ReseniSPrilohamiFormSet() data['prilohy'] = f.ReseniSPrilohamiFormSet()
data["nadproblem_id"] = self.nadproblem.id 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 return data
# FIXME prepsat tak, aby form_valid se volalo jen tehdy, kdyz je form i formset validni # FIXME prepsat tak, aby form_valid se volalo jen tehdy, kdyz je form i formset validni
@ -464,17 +466,17 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
return super().form_invalid(form) return super().form_invalid(form)
with transaction.atomic(): with transaction.atomic():
self.object = form.save() 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.resitele.add(*form.cleaned_data["resitele"])
self.object.cas_doruceni = timezone.now() self.object.cas_doruceni = timezone.now()
self.object.forma = m.Reseni.FORMA_UPLOAD self.object.forma = Reseni.FORMA_UPLOAD
self.object.save() self.object.save()
prilohy.instance = self.object prilohy.instance = self.object
prilohy.save() prilohy.save()
for hodnoceni in self.object.hodnoceni_set.all(): 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() hodnoceni.save()
# Pošleme mail opravovatelům a garantovi # Pošleme mail opravovatelům a garantovi
@ -492,7 +494,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
# FIXME: Víc informativní obsah mailů, možná vč. příloh? # FIXME: Víc informativní obsah mailů, možná vč. příloh?
prijemci = map(lambda it: it.osoba.email, prijemci) 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 = "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 })") seznam_do_subjectu = "problému " + str(problemy[0]) + ("" if len(problemy) == 1 else f" (a dalším { len(problemy) - 1 })")

16
personalni/admin.py

@ -1,10 +1,10 @@
from django.contrib import admin from django.contrib import admin
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django_reverse_admin import ReverseModelAdmin from django_reverse_admin import ReverseModelAdmin
import seminar.models as m from seminar.models.personalni import *
@admin.register(m.Osoba) @admin.register(Osoba)
class OsobaAdmin(admin.ModelAdmin): class OsobaAdmin(admin.ModelAdmin):
actions = ['synchronizuj_maily', 'udelej_orgem'] actions = ['synchronizuj_maily', 'udelej_orgem']
search_fields = ['jmeno', 'prijmeni', 'prezdivka'] search_fields = ['jmeno', 'prijmeni', 'prezdivka']
@ -27,25 +27,25 @@ class OsobaAdmin(admin.ModelAdmin):
user.groups.add(org_group) user.groups.add(org_group)
user.is_staff = True user.is_staff = True
user.save() user.save()
org = m.Organizator.objects.create(osoba=o) org = Organizator.objects.create(osoba=o)
org.save() org.save()
udelej_orgem.short_description = "Udělej vybraných osob organizátory" udelej_orgem.short_description = "Udělej vybraných osob organizátory"
class OsobaInline(admin.TabularInline): class OsobaInline(admin.TabularInline):
model = m.Osoba model = Osoba
@admin.register(m.Organizator) @admin.register(Organizator)
class OrganizatorAdmin(ReverseModelAdmin): class OrganizatorAdmin(ReverseModelAdmin):
search_fields = ['osoba__jmeno', 'osoba__prijmeni', 'osoba__prezdivka'] search_fields = ['osoba__jmeno', 'osoba__prijmeni', 'osoba__prezdivka']
inline_type = 'stacked' inline_type = 'stacked'
inline_reverse = ['osoba'] inline_reverse = ['osoba']
@admin.register(m.Resitel) @admin.register(Resitel)
class ResitelAdmin(ReverseModelAdmin): class ResitelAdmin(ReverseModelAdmin):
search_fields = ['osoba__jmeno', 'osoba__prijmeni', 'osoba__prezdivka'] search_fields = ['osoba__jmeno', 'osoba__prijmeni', 'osoba__prezdivka']
ordering = ('osoba__prijmeni', 'osoba__jmeno') ordering = ('osoba__prijmeni', 'osoba__jmeno')
inline_type = 'stacked' inline_type = 'stacked'
inline_reverse = ['osoba'] inline_reverse = ['osoba']
admin.site.register(m.Skola) admin.site.register(Skola)
admin.site.register(m.Prijemce) admin.site.register(Prijemce)

2
personalni/forms.py

@ -4,7 +4,7 @@ from django.contrib.auth.forms import PasswordResetForm
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.contrib.auth.models import User from django.contrib.auth.models import User
from seminar.models import Skola, Resitel, Osoba from seminar.models.personalni import *
from datetime import date from datetime import date
import logging import logging

4
personalni/utils.py

@ -1,8 +1,8 @@
import seminar.models as m from seminar.models.personalni import *
from various.utils import bez_diakritiky_translate from various.utils import bez_diakritiky_translate
import re import re
def normalizuj_jmeno(o: m.Osoba) -> str: def normalizuj_jmeno(o: Osoba) -> str:
# FIXME: Možná není potřeba vázat na model? # FIXME: Možná není potřeba vázat na model?
cele_jmeno = f'{o.jmeno} {o.prijmeni}' cele_jmeno = f'{o.jmeno} {o.prijmeni}'
cele_jmeno = cele_jmeno.translate(bez_diakritiky_translate) cele_jmeno = cele_jmeno.translate(bez_diakritiky_translate)

42
personalni/views.py

@ -9,8 +9,10 @@ from django.contrib.auth.mixins import LoginRequiredMixin
from django.db import transaction from django.db import transaction
from django.http import HttpResponse from django.http import HttpResponse
import seminar.models as s from seminar.models.odevzdavatko import *
import seminar.models as m from seminar.models.personalni import Organizator, Resitel, Osoba
from seminar.models.tvorba import Tema, Uloha, Clanek, Nastaveni
from seminar.models.soustredeni import Soustredeni
from .forms import PrihlaskaForm, ProfileEditForm, PoMaturiteProfileEditForm from .forms import PrihlaskaForm, ProfileEditForm, PoMaturiteProfileEditForm
from datetime import date from datetime import date
@ -31,22 +33,22 @@ class OrgoRozcestnikView(TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**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() nastaveni = Nastaveni.objects.first()
aktualni_rocnik = nastaveni.aktualni_rocnik aktualni_rocnik = nastaveni.aktualni_rocnik
context['posledni_cislo_url'] = nastaveni.aktualni_cislo.verejne_url() context['posledni_cislo_url'] = nastaveni.aktualni_cislo.verejne_url()
# TODO možná chceme odkazovat na právě rozpracované číslo, a ne to poslední vydané # TODO možná chceme odkazovat na právě rozpracované číslo, a ne to poslední vydané
# pokud nechceme haluzit kód (= poradi) dalšího čísla, bude asi potřeba jít # 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) # přes treenody (a dát si přitom pozor na MezicisloNode)
neobodovana_reseni = s.Hodnoceni.objects.filter(body__isnull=True) neobodovana_reseni = Hodnoceni.objects.filter(body__isnull=True)
reseni_mimo_cislo = s.Hodnoceni.objects.filter(deadline_body__isnull=True) reseni_mimo_cislo = Hodnoceni.objects.filter(deadline_body__isnull=True)
context['pocet_neobodovanych_reseni'] = neobodovana_reseni.count() context['pocet_neobodovanych_reseni'] = neobodovana_reseni.count()
context['pocet_reseni_mimo_cislo'] = reseni_mimo_cislo.count() context['pocet_reseni_mimo_cislo'] = reseni_mimo_cislo.count()
u = self.request.user u = self.request.user
os = s.Osoba.objects.get(user=u) os = Osoba.objects.get(user=u)
organizator = s.Organizator.objects.get(osoba=os) organizator = 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_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() context['muj_pocet_reseni_mimo_cislo'] = reseni_mimo_cislo.filter(Q(problem__garant=organizator) | Q(problem__autor=organizator) | Q(problem__opravovatele__in=[organizator])).count()
@ -56,11 +58,11 @@ class OrgoRozcestnikView(TemplateView):
context["pocty_neopravenych_reseni"] = [(it['problem__nazev'], it['cas'].date) for it in pocty_neopravenych_reseni.all()] context["pocty_neopravenych_reseni"] = [(it['problem__nazev'], it['cas'].date) for it in pocty_neopravenych_reseni.all()]
#FIXME: přidat stav='STAV_ZADANY' #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() 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() 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() cislo__rocnik=aktualni_rocnik).distinct()
context['temata'] = temata context['temata'] = temata
@ -74,12 +76,12 @@ class OrgoRozcestnikView(TemplateView):
class ResitelView(LoginRequiredMixin,generic.DetailView): class ResitelView(LoginRequiredMixin,generic.DetailView):
model = s.Resitel model = Resitel
template_name = 'personalni/profil/resitel.html' template_name = 'personalni/profil/resitel.html'
def get_object(self, queryset=None): def get_object(self, queryset=None):
print(self.request.user) print(self.request.user)
return s.Resitel.objects.get(osoba__user=self.request.user) return Resitel.objects.get(osoba__user=self.request.user)
### Formulare ### Formulare
@ -100,7 +102,7 @@ def resitelEditView(request):
err_logger = logging.getLogger('seminar.prihlaska.problem') 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 ## Načtení objektů Osoba a Resitel patřících k aktuálně přihlášenému uživateli
u = request.user u = request.user
osoba_edit = s.Osoba.objects.get(user=u) osoba_edit = Osoba.objects.get(user=u)
if hasattr(osoba_edit,'resitel'): if hasattr(osoba_edit,'resitel'):
resitel_edit = osoba_edit.resitel resitel_edit = osoba_edit.resitel
else: else:
@ -206,7 +208,7 @@ def prihlaskaView(request):
resitel_grp = Group.objects.filter(name__exact='resitel').first() resitel_grp = Group.objects.filter(name__exact='resitel').first()
u.groups.add(resitel_grp) u.groups.add(resitel_grp)
o = s.Osoba( o = Osoba(
jmeno = fcd['jmeno'], jmeno = fcd['jmeno'],
prijmeni = fcd['prijmeni'], prijmeni = fcd['prijmeni'],
pohlavi_muz = fcd['pohlavi_muz'], pohlavi_muz = fcd['pohlavi_muz'],
@ -234,9 +236,9 @@ def prihlaskaView(request):
# Dovolujeme doregistraci uživatele pro existující mail, takže naopak chceme doplnit/aktualizovat údaje do stávajícího objektu # Dovolujeme doregistraci uživatele pro existující mail, takže naopak chceme doplnit/aktualizovat údaje do stávajícího objektu
try: try:
orig_osoba = m.Osoba.objects.get(email=fcd['email']) orig_osoba = Osoba.objects.get(email=fcd['email'])
orig_osoba.poznamka += '\nDOREGISTRACE K EXISTUJÍCÍMU E-MAILU, diff níže.' orig_osoba.poznamka += '\nDOREGISTRACE K EXISTUJÍCÍMU E-MAILU, diff níže.'
except m.Osoba.DoesNotExist: except Osoba.DoesNotExist:
# Trik: Budeme aktualizovat údaje nové osoby, takže se asi nic nezmění, ale fungovat to bude. # Trik: Budeme aktualizovat údaje nové osoby, takže se asi nic nezmění, ale fungovat to bude.
orig_osoba = o orig_osoba = o
@ -264,11 +266,11 @@ def prihlaskaView(request):
o.save() o.save()
# Jednoduchá kvazi-kontrola duplicitních Osob # Jednoduchá kvazi-kontrola duplicitních Osob
kolize = m.Osoba.objects.filter(jmeno=o.jmeno, prijmeni=o.prijmeni) kolize = Osoba.objects.filter(jmeno=o.jmeno, prijmeni=o.prijmeni)
if kolize.count() > 1: # Jednu z nich jsme právě uložili 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]}') err_logger.warning(f'Zaregistrovala se osoba s kolizním jménem. ID osob: {[o.id for o in kolize]}')
r = s.Resitel( r = Resitel(
prezdivka_resitele=fcd['prezdivka_resitele'] if fcd['prezdivka_resitele'] != "" else None, prezdivka_resitele=fcd['prezdivka_resitele'] if fcd['prezdivka_resitele'] != "" else None,
rok_maturity = fcd['rok_maturity'], rok_maturity = fcd['rok_maturity'],
zasilat = fcd['zasilat'], zasilat = fcd['zasilat'],
@ -287,7 +289,7 @@ def prihlaskaView(request):
try: try:
orig_resitel = o.resitel orig_resitel = o.resitel
orig_resitel.poznamka += '\nDOREGISTRACE ŘEŠITELE, diff:' orig_resitel.poznamka += '\nDOREGISTRACE ŘEŠITELE, diff:'
except m.Resitel.DoesNotExist: except Resitel.DoesNotExist:
# Stejný trik: # Stejný trik:
orig_resitel = r orig_resitel = r
resitel_attrs = ['skola', 'rok_maturity', 'zasilat', 'zasilat_cislo_emailem', 'zasilat_cislo_papirove'] resitel_attrs = ['skola', 'rok_maturity', 'zasilat', 'zasilat_cislo_emailem', 'zasilat_cislo_papirove']

2
prednasky/admin.py

@ -5,7 +5,7 @@ from django.utils.safestring import mark_safe
from django.utils.html import escape from django.utils.html import escape
from .models import Prednaska, Seznam, STAV_NAVRH from .models import Prednaska, Seznam, STAV_NAVRH
from seminar.models import Soustredeni from seminar.models.soustredeni import Soustredeni
class Seznam_PrednaskaInline(admin.TabularInline): class Seznam_PrednaskaInline(admin.TabularInline):

3
prednasky/models.py

@ -2,7 +2,8 @@
from django.db import models from django.db import models
from django.utils.encoding import force_text from django.utils.encoding import force_text
from seminar.models import Organizator, Soustredeni from seminar.models.soustredeni import Soustredeni
from seminar.models.personalni import Organizator
STAV_NAVRH = 1 STAV_NAVRH = 1
STAV_BUDE = 2 STAV_BUDE = 2

3
prednasky/views.py

@ -6,7 +6,8 @@ from django.db.models import Sum
from django.forms import Form from django.forms import Form
from prednasky.models import Prednaska, Hlasovani, Seznam, STAV_NAVRH from prednasky.models import Prednaska, Hlasovani, Seznam, STAV_NAVRH
from seminar.models import Soustredeni, Osoba from seminar.models.soustredeni import Soustredeni
from seminar.models.personalni import Osoba
def newPrednaska(request): def newPrednaska(request):
# hlasovani se vztahuje k nejnovejsimu soustredeni # hlasovani se vztahuje k nejnovejsimu soustredeni

108
seminar/admin.py

@ -9,51 +9,55 @@ from django.utils.safestring import mark_safe
# Todo: reversion # Todo: reversion
import seminar.models as m from seminar.models.tvorba import Rocnik, Cislo, Deadline, ZmrazenaVysledkovka, Problem, Uloha, Tema, Clanek, Nastaveni
from seminar.models.personalni import Resitel
from seminar.models.soustredeni import Konfera
from seminar.models.novinky import Novinky
from seminar.models.pomocne import Text, Obrazek
admin.site.register(m.Rocnik) admin.site.register(Rocnik)
admin.site.register(m.Deadline) admin.site.register(Deadline)
admin.site.register(m.ZmrazenaVysledkovka) admin.site.register(ZmrazenaVysledkovka)
class DeadlineAdminInline(admin.TabularInline): class DeadlineAdminInline(admin.TabularInline):
model = m.Deadline model = Deadline
extra = 0 extra = 0
class CisloForm(ModelForm): class CisloForm(ModelForm):
class Meta: class Meta:
model = m.Cislo model = Cislo
fields = '__all__' fields = '__all__'
def clean(self): def clean(self):
if self.cleaned_data.get('verejne_db') == False: if self.cleaned_data.get('verejne_db') == False:
return self.cleaned_data return self.cleaned_data
# cn = m.CisloNode.objects.get(cislo=self.instance) # cn = CisloNode.objects.get(cislo=self.instance)
# errors = [] # errors = []
# for ch in tl.all_children(cn): # for ch in tl.all_children(cn):
# if isinstance(ch, m.TemaVCisleNode): # if isinstance(ch, TemaVCisleNode):
# if ch.tema.stav not in \ # if ch.tema.stav not in \
# (m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): # (Problem.STAV_ZADANY, Problem.STAV_VYRESENY):
# errors.append(ValidationError('Téma %(tema)s není zadané ani vyřešené', params={'tema':ch.tema})) # errors.append(ValidationError('Téma %(tema)s není zadané ani vyřešené', params={'tema':ch.tema}))
# #
# if isinstance(ch, m.UlohaZadaniNode) or isinstance(ch, m.UlohaVzorakNode): # if isinstance(ch, UlohaZadaniNode) or isinstance(ch, UlohaVzorakNode):
# if ch.uloha.stav not in \ # if ch.uloha.stav not in \
# (m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): # (Problem.STAV_ZADANY, Problem.STAV_VYRESENY):
# errors.append(ValidationError('Úloha %(uloha)s není zadaná ani vyřešená', params={'uloha':ch.uloha})) # errors.append(ValidationError('Úloha %(uloha)s není zadaná ani vyřešená', params={'uloha':ch.uloha}))
# if isinstance(ch, m.ReseniNode): # if isinstance(ch, ReseniNode):
# for problem in ch.reseni.problem_set: # for problem in ch.reseni.problem_set:
# if problem not in \ # if problem not in \
# (m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): # (Problem.STAV_ZADANY, Problem.STAV_VYRESENY):
# errors.append(ValidationError('Problém %s není zadaný ani vyřešený', code=problem)) # errors.append(ValidationError('Problém %s není zadaný ani vyřešený', code=problem))
# if errors: # if errors:
# errors.append(ValidationError(mark_safe('<b>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 <a href="/admin/seminar/cislo">seznamu čísel</a></b>'))) # errors.append(ValidationError(mark_safe('<b>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 <a href="/admin/seminar/cislo">seznamu čísel</a></b>')))
# raise ValidationError(errors) # raise ValidationError(errors)
errors = [] errors = []
for ch in m.Uloha.objects.filter(cislo_zadani=self.instance): for ch in Uloha.objects.filter(cislo_zadani=self.instance):
if ch.stav not in (m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): if ch.stav not in (Problem.STAV_ZADANY, Problem.STAV_VYRESENY):
errors.append( errors.append(
ValidationError('Úloha %(uloha)s není zadaná ani vyřešená', params={'uloha': ch})) ValidationError('Úloha %(uloha)s není zadaná ani vyřešená', params={'uloha': ch}))
if errors: if errors:
@ -68,7 +72,7 @@ class CisloForm(ModelForm):
return self.cleaned_data return self.cleaned_data
@admin.register(m.Cislo) @admin.register(Cislo)
class CisloAdmin(admin.ModelAdmin): class CisloAdmin(admin.ModelAdmin):
form = CisloForm form = CisloForm
actions = ['force_publish'] actions = ['force_publish']
@ -76,31 +80,31 @@ class CisloAdmin(admin.ModelAdmin):
def force_publish(self,request,queryset): def force_publish(self,request,queryset):
for cislo in queryset: for cislo in queryset:
# cn = m.CisloNode.objects.get(cislo=cislo) # cn = CisloNode.objects.get(cislo=cislo)
# for ch in tl.all_children(cn): # for ch in tl.all_children(cn):
# if isinstance(ch, m.TemaVCisleNode): # if isinstance(ch, TemaVCisleNode):
# if ch.tema.stav not in (m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): # if ch.tema.stav not in (Problem.STAV_ZADANY, Problem.STAV_VYRESENY):
# ch.tema.stav = m.Problem.STAV_ZADANY # ch.tema.stav = Problem.STAV_ZADANY
# ch.tema.save() # ch.tema.save()
# #
# if isinstance(ch, m.UlohaZadaniNode) or isinstance(ch, m.UlohaVzorakNode): # if isinstance(ch, UlohaZadaniNode) or isinstance(ch, UlohaVzorakNode):
# if ch.uloha.stav not in (m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): # if ch.uloha.stav not in (Problem.STAV_ZADANY, Problem.STAV_VYRESENY):
# ch.uloha.stav = m.Problem.STAV_ZADANY # ch.uloha.stav = Problem.STAV_ZADANY
# ch.uloha.save() # ch.uloha.save()
# if isinstance(ch, m.ReseniNode): # if isinstance(ch, ReseniNode):
# for problem in ch.reseni.problem_set: # for problem in ch.reseni.problem_set:
# if problem not in (m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): # if problem not in (Problem.STAV_ZADANY, Problem.STAV_VYRESENY):
# problem.stav = m.Problem.STAV_ZADANY # problem.stav = Problem.STAV_ZADANY
# problem.save() # problem.save()
for ch in m.Uloha.objects.filter(cislo_zadani=cislo): for ch in Uloha.objects.filter(cislo_zadani=cislo):
if ch.stav not in (m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): if ch.stav not in (Problem.STAV_ZADANY, Problem.STAV_VYRESENY):
ch.stav = m.Problem.STAV_ZADANY ch.stav = Problem.STAV_ZADANY
ch.save() ch.save()
hp = ch.hlavni_problem hp = ch.hlavni_problem
if hp.stav not in (m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): if hp.stav not in (Problem.STAV_ZADANY, Problem.STAV_VYRESENY):
hp.stav = m.Problem.STAV_ZADANY hp.stav = Problem.STAV_ZADANY
hp.save() hp.save()
# TODO Řešení, vzoráky? # TODO Řešení, vzoráky?
@ -112,14 +116,14 @@ class CisloAdmin(admin.ModelAdmin):
force_publish.short_description = 'Zveřejnit vybraná čísla a všechny návrhy úloh v nich učinit zadanými' force_publish.short_description = 'Zveřejnit vybraná čísla a všechny návrhy úloh v nich učinit zadanými'
@admin.register(m.Problem) @admin.register(Problem)
class ProblemAdmin(PolymorphicParentModelAdmin): class ProblemAdmin(PolymorphicParentModelAdmin):
base_model = m.Problem base_model = Problem
child_models = [ child_models = [
m.Tema, Tema,
m.Clanek, Clanek,
m.Uloha, Uloha,
m.Konfera, Konfera,
] ]
# Pokud chceme orezavat na aktualni rocnik, musime do modelu pridat odkaz na rocnik. Zatim bere vse. # Pokud chceme orezavat na aktualni rocnik, musime do modelu pridat odkaz na rocnik. Zatim bere vse.
search_fields = ['nazev'] search_fields = ['nazev']
@ -131,38 +135,38 @@ class ProblemAdminMixin(object):
filter_horizontal = ['opravovatele'] filter_horizontal = ['opravovatele']
@admin.register(m.Tema) @admin.register(Tema)
class TemaAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin): class TemaAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin):
base_model = m.Tema base_model = Tema
@admin.register(m.Clanek) @admin.register(Clanek)
class ClanekAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin): class ClanekAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin):
base_model = m.Clanek base_model = Clanek
@admin.register(m.Uloha) @admin.register(Uloha)
class UlohaAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin): class UlohaAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin):
base_model = m.Uloha base_model = Uloha
@admin.register(m.Konfera) @admin.register(Konfera)
class KonferaAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin): class KonferaAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin):
base_model = m.Konfera base_model = Konfera
class TextAdminInline(admin.TabularInline): class TextAdminInline(admin.TabularInline):
model = m.Text model = Text
formfield_overrides = { formfield_overrides = {
models.TextField: {'widget': widgets.TextInput} models.TextField: {'widget': widgets.TextInput}
} }
exclude = ['text_zkraceny_set','text_zkraceny'] exclude = ['text_zkraceny_set','text_zkraceny']
admin.site.register(m.Text) admin.site.register(Text)
class ResitelInline(admin.TabularInline): class ResitelInline(admin.TabularInline):
model = m.Resitel model = Resitel
extra = 1 extra = 1
# admin.site.register(m.Pohadka) # admin.site.register(Pohadka)
admin.site.register(m.Obrazek) admin.site.register(Obrazek)
admin.site.register(m.Nastaveni, SingletonModelAdmin) admin.site.register(Nastaveni, SingletonModelAdmin)
admin.site.register(m.Novinky) admin.site.register(Novinky)

2
seminar/management/commands/generate_thumbnails.py

@ -1,6 +1,6 @@
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from seminar.models import Cislo from seminar.models.tvorba import Cislo
from subprocess import CalledProcessError from subprocess import CalledProcessError
import logging import logging

4
seminar/management/commands/pregeneruj_zmrazene_vysledkovky.py

@ -1,11 +1,11 @@
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
import seminar.models as m from seminar.models.tvorba import Deadline
class Command(BaseCommand): class Command(BaseCommand):
help = "Všem deadlinům se zveřejněnou výsledkovkou vygeneruj výsledkovku" help = "Všem deadlinům se zveřejněnou výsledkovkou vygeneruj výsledkovku"
def handle(self, *args, **options): 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() deadline.vygeneruj_vysledkovku()

4
seminar/management/commands/testdata.py

@ -7,7 +7,9 @@ from django.core.management.base import BaseCommand
from django.core.management import call_command from django.core.management import call_command
from django.conf import settings from django.conf import settings
from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni from seminar.models.personalni import Skola, Resitel
from seminar.models.tvorba import Rocnik, Cislo, Problem
from seminar.models.odevzdavatko import Reseni
from seminar.testutils import create_test_data from seminar.testutils import create_test_data
import django.contrib.auth import django.contrib.auth
User = django.contrib.auth.get_user_model() User = django.contrib.auth.get_user_model()

4
seminar/migrations/0001_initial.py

@ -2,7 +2,7 @@ from __future__ import unicode_literals
from django.db import models, migrations from django.db import models, migrations
import django_countries.fields import django_countries.fields
import seminar.models from seminar.models.odevzdavatko import generate_filename
import django.utils.timezone import django.utils.timezone
from django.conf import settings from django.conf import settings
@ -75,7 +75,7 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(serialize=False, primary_key=True)), ('id', models.AutoField(serialize=False, primary_key=True)),
('timestamp', models.DateTimeField(auto_now=True, verbose_name='vytvo\u0159eno')), ('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)), ('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={ options={

20
seminar/migrations/0001_squashed_0098_auto_20210906_0305.py

@ -7,7 +7,9 @@ import django.db.models.deletion
import django.utils.timezone import django.utils.timezone
import django_countries.fields import django_countries.fields
import imagekit.models.fields import imagekit.models.fields
import seminar.models from seminar.models.odevzdavatko import generate_filename
from seminar.models.soustredeni import generate_filename_konfera
from seminar.models.tvorba import cislo_pdf_filename, cislo_png_filename
import taggit.managers import taggit.managers
from datetime import date from datetime import date
@ -962,7 +964,7 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(primary_key=True, serialize=False)), ('id', models.AutoField(primary_key=True, serialize=False)),
('timestamp', models.DateTimeField(auto_now=True, verbose_name='vytvořeno')), ('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')), ('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í')), ('reseni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='prilohy', to='seminar.Reseni', verbose_name='řešení')),
], ],
@ -1284,7 +1286,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='cislo', model_name='cislo',
name='pdf', 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( migrations.AlterField(
model_name='problem', model_name='problem',
@ -1361,8 +1363,8 @@ class Migration(migrations.Migration):
('abstrakt', models.TextField(blank=True, help_text='Abstrakt konfery tak, jak byl uveden ve sborníku', verbose_name='abstrakt')), ('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')), ('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')), ('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')), ('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=seminar.models.generate_filename_konfera, verbose_name='materialy')), ('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')), ('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í')), ('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 +1402,12 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='konfera', model_name='konfera',
name='materialy', 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( migrations.AlterField(
model_name='konfera', model_name='konfera',
name='prezentace', 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( migrations.AddField(
model_name='konfera', model_name='konfera',
@ -2648,12 +2650,12 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='cislo', model_name='cislo',
name='pdf', 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( migrations.AddField(
model_name='cislo', model_name='cislo',
name='titulka_nahled', 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( migrations.AlterField(
model_name='treenode', model_name='treenode',

1
seminar/migrations/0002_add_body_views.py

@ -2,7 +2,6 @@ from __future__ import unicode_literals
from django.db import models, migrations from django.db import models, migrations
import django_countries.fields import django_countries.fields
import seminar.models
import django.utils.timezone import django.utils.timezone
from django.conf import settings from django.conf import settings

1
seminar/migrations/0028_add_body_celkem_views.py

@ -2,7 +2,6 @@ from __future__ import unicode_literals
from django.db import models, migrations from django.db import models, migrations
import django_countries.fields import django_countries.fields
import seminar.models
import django.utils.timezone import django.utils.timezone
from django.conf import settings from django.conf import settings

1
seminar/migrations/0029_fix_body_celkem_views.py

@ -2,7 +2,6 @@ from __future__ import unicode_literals
from django.db import models, migrations from django.db import models, migrations
import django_countries.fields import django_countries.fields
import seminar.models
import django.utils.timezone import django.utils.timezone
from django.conf import settings from django.conf import settings

4
seminar/migrations/0031_cislo_pdf.py

@ -1,7 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models, migrations from django.db import models, migrations
import seminar.models from seminar.models.tvorba import cislo_pdf_filename
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -14,7 +14,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='cislo', model_name='cislo',
name='pdf', 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, preserve_default=True,
), ),
] ]

4
seminar/migrations/0032_cislo_pdf_blank_typos.py

@ -2,7 +2,7 @@ from __future__ import unicode_literals
from django.db import models, migrations from django.db import models, migrations
import django_countries.fields import django_countries.fields
import seminar.models from seminar.models.tvorba import cislo_pdf_filename
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -25,7 +25,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='cislo', model_name='cislo',
name='pdf', 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, preserve_default=True,
), ),
migrations.AlterField( migrations.AlterField(

6
seminar/migrations/0041_konfery.py

@ -2,7 +2,7 @@ from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import seminar.models from seminar.models.soustredeni import generate_filename_konfera
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -21,8 +21,8 @@ class Migration(migrations.Migration):
('abstrakt', models.TextField(help_text='Abstrakt konfery tak, jak byl uveden ve sborn\xedku', verbose_name='abstrakt', blank=True)), ('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)), ('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)')])), ('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')), ('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=seminar.models.generate_filename_konfera, verbose_name='materialy')), ('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)), ('organizator', models.ForeignKey(related_name='konfery', on_delete=django.db.models.deletion.SET_NULL, verbose_name='organiz\xe1tor', to='seminar.Organizator', null=True)),
], ],
options={ options={

6
seminar/migrations/0042_auto_20161005_0847.py

@ -2,7 +2,7 @@ from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import seminar.models from seminar.models.soustredeni import generate_filename_konfera
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -15,12 +15,12 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='konfera', model_name='konfera',
name='materialy', 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( migrations.AlterField(
model_name='konfera', model_name='konfera',
name='prezentace', 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( migrations.AlterField(
model_name='konfera', model_name='konfera',

4
seminar/migrations/0081_auto_20200408_2221.py

@ -1,7 +1,7 @@
# Generated by Django 2.2.9 on 2020-04-08 20:21 # Generated by Django 2.2.9 on 2020-04-08 20:21
from django.db import migrations, models from django.db import migrations, models
import seminar.models from seminar.models.tvorba import cislo_pdf_filename
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -19,6 +19,6 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='cislo', model_name='cislo',
name='pdf', 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'),
), ),
] ]

4
seminar/migrations/0082_auto_20200506_1951.py

@ -1,7 +1,7 @@
# Generated by Django 2.2.12 on 2020-05-06 17:51 # Generated by Django 2.2.12 on 2020-05-06 17:51
from django.db import migrations, models from django.db import migrations, models
import seminar.models from seminar.models.tvorba import cislo_png_filename
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -14,6 +14,6 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='cislo', model_name='cislo',
name='titulka_nahled', 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'),
), ),
] ]

4
seminar/migrations/0100_auto_20211129_2354.py

@ -1,7 +1,7 @@
# Generated by Django 2.2.24 on 2021-11-29 22:54 # Generated by Django 2.2.24 on 2021-11-29 22:54
from django.db import migrations, models from django.db import migrations, models
import seminar.models.tvorba from seminar.models.tvorba import cislo_pdf_filename, OverwriteStorage
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -14,6 +14,6 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='cislo', model_name='cislo',
name='pdf', 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=OverwriteStorage(), upload_to=cislo_pdf_filename, verbose_name='pdf'),
), ),
] ]

12
seminar/migrations/0103_deadline.py

@ -5,7 +5,7 @@ from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
from django.utils import timezone from django.utils import timezone
import seminar.models as m from seminar.models.tvorba import Deadline as mDeadline
def vytvor_deadliny(apps, schema_editor): def vytvor_deadliny(apps, schema_editor):
@ -16,7 +16,7 @@ def vytvor_deadliny(apps, schema_editor):
if cislo.rocnik.rocnik < 26: if cislo.rocnik.rocnik < 26:
Deadline.objects.create( Deadline.objects.create(
cislo=cislo, 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)), 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, 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: if cislo.datum_deadline_soustredeni and cislo.datum_deadline_soustredeni == cislo.datum_preddeadline:
vytvor_deadline( vytvor_deadline(
date=cislo.datum_deadline_soustredeni, date=cislo.datum_deadline_soustredeni,
typ=m.Deadline.TYP_PRVNI_A_SOUS typ=mDeadline.TYP_PRVNI_A_SOUS
) )
else: else:
if cislo.datum_deadline_soustredeni: if cislo.datum_deadline_soustredeni:
vytvor_deadline( vytvor_deadline(
date=cislo.datum_deadline_soustredeni, date=cislo.datum_deadline_soustredeni,
typ=m.Deadline.TYP_SOUS typ=mDeadline.TYP_SOUS
) )
if cislo.datum_preddeadline: if cislo.datum_preddeadline:
vytvor_deadline( vytvor_deadline(
date=cislo.datum_preddeadline, date=cislo.datum_preddeadline,
typ=m.Deadline.TYP_PRVNI typ=mDeadline.TYP_PRVNI
) )
if cislo.datum_deadline: if cislo.datum_deadline:
vytvor_deadline( vytvor_deadline(
date=cislo.datum_deadline, date=cislo.datum_deadline,
typ=m.Deadline.TYP_CISLA typ=mDeadline.TYP_CISLA
) )

2
seminar/migrations/0105_odstraneni_deadlinu_cisla.py

@ -1,7 +1,7 @@
# Generated by Django 3.2.15 on 2022-10-09 10:14 # Generated by Django 3.2.15 on 2022-10-09 10:14
from django.db import migrations from django.db import migrations
from seminar.models import Deadline from seminar.models.tvorba import Deadline
def vrat_deadliny(apps, schema_editor): def vrat_deadliny(apps, schema_editor):

2
seminar/migrations/0106_remove_cislo_verejna_vysledkovka.py

@ -1,7 +1,7 @@
# Generated by Django 3.2.15 on 2022-10-09 11:04 # Generated by Django 3.2.15 on 2022-10-09 11:04
from django.db import migrations from django.db import migrations
from seminar.models import Deadline from seminar.models.tvorba import Deadline
def vrat_verejnost(apps, schema_editor): def vrat_verejnost(apps, schema_editor):

34
seminar/models/odevzdavatko.py

@ -9,16 +9,16 @@ from django.urls import reverse_lazy
from django.utils import timezone from django.utils import timezone
from django.conf import settings from django.conf import settings
from seminar.models import tvorba as am from seminar.models.tvorba import Cislo, Deadline, Problem, Uloha, aux_generate_filename
from seminar.models import personalni as pm from seminar.models.personalni import Resitel
from seminar.models import treenode as tm from seminar.models.treenode import TreeNode
from seminar.models import base as bm from seminar.models.base import SeminarModelBase
from seminar.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet from seminar.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet
@reversion.register(ignore_duplicates=True) @reversion.register(ignore_duplicates=True)
class Reseni(bm.SeminarModelBase): class Reseni(SeminarModelBase):
class Meta: class Meta:
db_table = 'seminar_reseni' db_table = 'seminar_reseni'
@ -31,10 +31,10 @@ class Reseni(bm.SeminarModelBase):
id = models.AutoField(primary_key = True) id = models.AutoField(primary_key = True)
# Ke každé dvojici řešní a problém existuje nanejvýš jedno hodnocení, doplnění vazby. # 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') through='Hodnoceni')
resitele = models.ManyToManyField(pm.Resitel, verbose_name='autoři řešení', resitele = models.ManyToManyField(Resitel, verbose_name='autoři řešení',
help_text='Seznam autorů řešení', through='Reseni_Resitele') help_text='Seznam autorů řešení', through='Reseni_Resitele')
@ -81,7 +81,7 @@ class Reseni(bm.SeminarModelBase):
# NOTE: Potenciální DB HOG (bez select_related) # NOTE: Potenciální DB HOG (bez select_related)
def deadline_reseni(self): 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: ## Pravdepodobne uz nebude potreba:
# def save(self, *args, **kwargs): # def save(self, *args, **kwargs):
@ -90,7 +90,7 @@ class Reseni(bm.SeminarModelBase):
# self.cislo_body = self.problem.cislo_reseni # self.cislo_body = self.problem.cislo_reseni
# super(Reseni, self).save(*args, **kwargs) # super(Reseni, self).save(*args, **kwargs)
class Hodnoceni(bm.SeminarModelBase): class Hodnoceni(SeminarModelBase):
class Meta: class Meta:
db_table = 'seminar_hodnoceni' db_table = 'seminar_hodnoceni'
verbose_name = 'Hodnocení' verbose_name = 'Hodnocení'
@ -103,16 +103,16 @@ class Hodnoceni(bm.SeminarModelBase):
body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body', body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body',
blank=True, null=True) 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) 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 # 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) related_name='hodnoceni', 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)
problem = models.ForeignKey(am.Problem, verbose_name='problém', problem = models.ForeignKey(Problem, verbose_name='problém',
related_name='hodnoceni', on_delete=models.PROTECT) 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)') feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)')
@ -168,7 +168,7 @@ class Hodnoceni(bm.SeminarModelBase):
@property @property
def body_neprepocitane_max(self): 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 None
return self.problem.uloha.max_body return self.problem.uloha.max_body
@ -178,12 +178,12 @@ class Hodnoceni(bm.SeminarModelBase):
def generate_filename(self, filename): def generate_filename(self, filename):
return os.path.join( return os.path.join(
settings.SEMINAR_RESENI_DIR, settings.SEMINAR_RESENI_DIR,
am.aux_generate_filename(self, filename) aux_generate_filename(self, filename)
) )
@reversion.register(ignore_duplicates=True) @reversion.register(ignore_duplicates=True)
class PrilohaReseni(bm.SeminarModelBase): class PrilohaReseni(SeminarModelBase):
class Meta: class Meta:
db_table = 'seminar_priloha_reseni' db_table = 'seminar_priloha_reseni'
@ -229,7 +229,7 @@ class Reseni_Resitele(models.Model):
# Interní ID # Interní ID
id = models.AutoField(primary_key = True) id = models.AutoField(primary_key = True)
resitele = models.ForeignKey(pm.Resitel, verbose_name='řešitel', on_delete=models.PROTECT) resitele = models.ForeignKey(Resitel, verbose_name='řešitel', 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)
@ -240,7 +240,7 @@ class Reseni_Resitele(models.Model):
return '{} od {}'.format(self.reseni, self.resitel) return '{} od {}'.format(self.reseni, self.resitel)
# NOTE: Poteciální DB HOG bez select_related # NOTE: Poteciální DB HOG bez select_related
class ReseniNode(tm.TreeNode): class ReseniNode(TreeNode):
class Meta: class Meta:
db_table = 'seminar_nodes_otistene_reseni' db_table = 'seminar_nodes_otistene_reseni'
verbose_name = 'Otištěné řešení (Node)' verbose_name = 'Otištěné řešení (Node)'

8
seminar/models/soustredeni.py

@ -10,7 +10,7 @@ from django.conf import settings
from . import personalni as pm from . import personalni as pm
from .base import SeminarModelBase from .base import SeminarModelBase
from seminar.models import tvorba as am from seminar.models.tvorba import Rocnik, Problem, aux_generate_filename
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -27,7 +27,7 @@ class Soustredeni(SeminarModelBase):
# Interní ID # Interní ID
id = models.AutoField(primary_key = True) 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) on_delete=models.PROTECT)
datum_zacatku = models.DateField('datum začátku', blank=True, null=True, 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): def generate_filename_konfera(self, filename):
return os.path.join( return os.path.join(
settings.SEMINAR_KONFERY_DIR, settings.SEMINAR_KONFERY_DIR,
am.aux_generate_filename(self, filename) aux_generate_filename(self, filename)
) )
## ##
@reversion.register(ignore_duplicates=True) @reversion.register(ignore_duplicates=True)
class Konfera(am.Problem): class Konfera(Problem):
class Meta: class Meta:
db_table = 'seminar_konfera' db_table = 'seminar_konfera'
verbose_name = 'Konfera' verbose_name = 'Konfera'

14
seminar/models/treenode.py

@ -14,7 +14,7 @@ from .pomocne import Text
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
from seminar.models import tvorba as am from seminar.models.tvorba import Rocnik, Cislo, Tema, Uloha, Pohadka
class TreeNode(PolymorphicModel): class TreeNode(PolymorphicModel):
class Meta: class Meta:
@ -95,7 +95,7 @@ class RocnikNode(TreeNode):
db_table = 'seminar_nodes_rocnik' db_table = 'seminar_nodes_rocnik'
verbose_name = 'Ročník (Node)' verbose_name = 'Ročník (Node)'
verbose_name_plural = 'Ročníky (Node)' verbose_name_plural = 'Ročníky (Node)'
rocnik = models.OneToOneField(am.Rocnik, rocnik = models.OneToOneField(Rocnik,
on_delete = models.PROTECT, # Pokud chci mazat ročník, musím si Node pořešit ručně on_delete = models.PROTECT, # Pokud chci mazat ročník, musím si Node pořešit ručně
verbose_name = "ročník") verbose_name = "ročník")
@ -107,7 +107,7 @@ class CisloNode(TreeNode):
db_table = 'seminar_nodes_cislo' db_table = 'seminar_nodes_cislo'
verbose_name = 'Číslo (Node)' verbose_name = 'Číslo (Node)'
verbose_name_plural = 'Čísla (Node)' verbose_name_plural = 'Čísla (Node)'
cislo = models.OneToOneField(am.Cislo, cislo = models.OneToOneField(Cislo,
on_delete = models.PROTECT, # Pokud chci mazat číslo, musím si Node pořešit ručně on_delete = models.PROTECT, # Pokud chci mazat číslo, musím si Node pořešit ručně
verbose_name = "číslo") verbose_name = "číslo")
@ -149,7 +149,7 @@ class TemaVCisleNode(TreeNode):
db_table = 'seminar_nodes_temavcisle' db_table = 'seminar_nodes_temavcisle'
verbose_name = 'Téma v čísle (Node)' verbose_name = 'Téma v čísle (Node)'
verbose_name_plural = 'Témata v čísle (Node)' verbose_name_plural = 'Témata v čísle (Node)'
tema = models.ForeignKey(am.Tema, tema = models.ForeignKey(Tema,
on_delete=models.PROTECT, # Pokud chci mazat téma, musím si Node pořešit ručně on_delete=models.PROTECT, # Pokud chci mazat téma, musím si Node pořešit ručně
verbose_name = "téma v čísle") verbose_name = "téma v čísle")
@ -190,7 +190,7 @@ class UlohaZadaniNode(TreeNode):
db_table = 'seminar_nodes_uloha_zadani' db_table = 'seminar_nodes_uloha_zadani'
verbose_name = 'Zadání úlohy (Node)' verbose_name = 'Zadání úlohy (Node)'
verbose_name_plural = 'Zadání úloh (Node)' verbose_name_plural = 'Zadání úloh (Node)'
uloha = models.OneToOneField(am.Uloha, uloha = models.OneToOneField(Uloha,
on_delete=models.PROTECT, # Pokud chci mazat téma, musím si Node pořešit ručně on_delete=models.PROTECT, # Pokud chci mazat téma, musím si Node pořešit ručně
verbose_name = "úloha", verbose_name = "úloha",
null=True, null=True,
@ -208,7 +208,7 @@ class PohadkaNode(TreeNode):
db_table = 'seminar_nodes_pohadka' db_table = 'seminar_nodes_pohadka'
verbose_name = 'Pohádka (Node)' verbose_name = 'Pohádka (Node)'
verbose_name_plural = 'Pohádky (Node)' verbose_name_plural = 'Pohádky (Node)'
pohadka = models.OneToOneField(am.Pohadka, pohadka = models.OneToOneField(Pohadka,
on_delete=models.PROTECT, # Pokud chci mazat pohádku, musím si Node pořešit ručně on_delete=models.PROTECT, # Pokud chci mazat pohádku, musím si Node pořešit ručně
verbose_name = "pohádka", verbose_name = "pohádka",
) )
@ -221,7 +221,7 @@ class UlohaVzorakNode(TreeNode):
db_table = 'seminar_nodes_uloha_vzorak' db_table = 'seminar_nodes_uloha_vzorak'
verbose_name = 'Vzorák úlohy (Node)' verbose_name = 'Vzorák úlohy (Node)'
verbose_name_plural = 'Vzoráky úloh (Node)' verbose_name_plural = 'Vzoráky úloh (Node)'
uloha = models.OneToOneField(am.Uloha, uloha = models.OneToOneField(Uloha,
on_delete=models.PROTECT, # Pokud chci mazat téma, musím si Node pořešit ručně on_delete=models.PROTECT, # Pokud chci mazat téma, musím si Node pořešit ručně
verbose_name = "úloha", verbose_name = "úloha",
null=True, null=True,

22
seminar/templatetags/deadliny.py

@ -1,30 +1,30 @@
from django import template from django import template
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
register = template.Library() register = template.Library()
import seminar.models as m from seminar.models.tvorba import Deadline
@register.filter(name='deadline_kratseji') @register.filter(name='deadline_kratseji')
def deadline_kratsi_text(deadline: m.Deadline): def deadline_kratsi_text(deadline: Deadline):
if deadline is None: if deadline is None:
return 'NONE' return 'NONE'
strings = { strings = {
m.Deadline.TYP_PRVNI: f"{deadline.cislo}", Deadline.TYP_PRVNI: f"{deadline.cislo}",
m.Deadline.TYP_SOUS: f"{deadline.cislo}", Deadline.TYP_SOUS: f"{deadline.cislo}",
m.Deadline.TYP_PRVNI_A_SOUS: f"{deadline.cislo} ⭯Ⓢ", Deadline.TYP_PRVNI_A_SOUS: f"{deadline.cislo} ⭯Ⓢ",
m.Deadline.TYP_CISLA: f"{deadline.cislo}", Deadline.TYP_CISLA: f"{deadline.cislo}",
} }
return strings[deadline.typ] return strings[deadline.typ]
@register.filter(name='deadline_html') @register.filter(name='deadline_html')
def deadline_html(deadline: m.Deadline): def deadline_html(deadline: Deadline):
if deadline is None: if deadline is None:
return 'Neznámý deadline' return 'Neznámý deadline'
text = deadline_kratsi_text(deadline) text = deadline_kratsi_text(deadline)
classes = { classes = {
m.Deadline.TYP_PRVNI: 'preddeadline', Deadline.TYP_PRVNI: 'preddeadline',
m.Deadline.TYP_SOUS: 'sous_deadline', Deadline.TYP_SOUS: 'sous_deadline',
m.Deadline.TYP_PRVNI_A_SOUS: 'sous_deadline', Deadline.TYP_PRVNI_A_SOUS: 'sous_deadline',
m.Deadline.TYP_CISLA: 'final_deadline', Deadline.TYP_CISLA: 'final_deadline',
} }
return mark_safe(f'<span class="{classes[deadline.typ]}" title="{deadline}">{text}</span>') return mark_safe(f'<span class="{classes[deadline.typ]}" title="{deadline}">{text}</span>')

43
seminar/testutils.py

@ -12,8 +12,13 @@ import unidecode
import logging import logging
from korektury.testutils import create_test_pdf from korektury.testutils import create_test_pdf
from seminar.models import Skola, Resitel, Rocnik, Cislo, Deadline, Problem, Reseni, PrilohaReseni, Nastaveni, Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Osoba, Organizator, Prijemce, Tema, Uloha, Konfera, TextNode, UlohaVzorakNode, RocnikNode, CisloNode, TemaVCisleNode, Text, Hodnoceni, UlohaZadaniNode, Novinky, TreeNode from seminar.models.personalni import *
import seminar.models as m from seminar.models.tvorba import *
from seminar.models.odevzdavatko import *
from seminar.models.soustredeni import *
from seminar.models.novinky import *
from seminar.models.pomocne import *
from seminar.models.treenode import *
from django.contrib.flatpages.models import FlatPage from django.contrib.flatpages.models import FlatPage
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
@ -493,31 +498,31 @@ def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod):
insert_last_child(cislo_node, tema_cislo_node) insert_last_child(cislo_node, tema_cislo_node)
# Přidávání obsahu do čísla # 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) add_first_child(tema_cislo_node, cast_node)
text_node = 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) 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) add_first_child(text_node, cast_node2)
text_node2 = 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) 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) add_first_child(text_node2, cast_node3)
text_node3 = 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) 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) add_first_child(text_node3, cast_node4)
text_node4 = 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) 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) "druhým podproblémem", root=cislo_node.root)
cast_node3.succ = cast_node3a cast_node3.succ = cast_node3a
cast_node3.save() cast_node3.save()
@ -527,10 +532,10 @@ def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod):
# Občas přidáme mezičíslo # Občas přidáme mezičíslo
if rnd.randint(1, 3) == 1: 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 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) nadpis = "Příspěvek k mezičíslu".format(cislo.kod), root=cislo_node.root)
add_first_child(mezicislo_node, cast_node_mezicislo) add_first_child(mezicislo_node, cast_node_mezicislo)
@ -728,7 +733,7 @@ def otec_syn(otec, syn):
def gen_clanek(rnd, organizatori, resitele): def gen_clanek(rnd, organizatori, resitele):
logger.info("Generuji článek do čísla 22.2") logger.info("Generuji článek do čísla 22.2")
clanek = m.Clanek.objects.create( clanek = Clanek.objects.create(
nazev="Článek o Lorem ipsum", nazev="Článek o Lorem ipsum",
nadproblem=None, nadproblem=None,
stav='vyreseny', stav='vyreseny',
@ -738,16 +743,16 @@ def gen_clanek(rnd, organizatori, resitele):
) )
clanek.save() clanek.save()
reseni = m.Reseni.objects.create( reseni = Reseni.objects.create(
zverejneno=True, zverejneno=True,
) )
reseni.resitele.add(rnd.choice(resitele)) reseni.resitele.add(rnd.choice(resitele))
reseni.save() reseni.save()
cislo = m.Cislo.objects.get(rocnik__rocnik=22, poradi=2) cislo = Cislo.objects.get(rocnik__rocnik=22, poradi=2)
cislonode = cislo.cislonode cislonode = cislo.cislonode
hodnoceni = m.Hodnoceni.objects.create( hodnoceni = Hodnoceni.objects.create(
body=15.0, body=15.0,
cislo_body=cislo, cislo_body=cislo,
reseni=reseni, reseni=reseni,
@ -755,7 +760,7 @@ def gen_clanek(rnd, organizatori, resitele):
) )
hodnoceni.save() hodnoceni.save()
reseninode = m.ReseniNode.objects.create( reseninode = ReseniNode.objects.create(
reseni=reseni reseni=reseni
) )
reseninode.save() reseninode.save()
@ -771,28 +776,28 @@ def gen_clanek(rnd, organizatori, resitele):
# FIXME: Ten, kdo vymyslel TreeLib (mj. týž, kdo psal tenhle kód), # 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 # nevyrobil vhodnou funkci, takže to postavíme pozpátku pomocí create_child
# (které vyrábí _prvního_ syna) # (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í... # Taky ten člověk nevyrobil vracení nových věcí...
castnode = reseninode.first_child castnode = reseninode.first_child
# Úvodní odstaveček # Úvodní odstaveček
obsah = "Tohle je zamyšlení o textu lorem ipsum. Začneme a skončíme ukázkou." 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, na_web=obsah,
do_cisla=obsah, do_cisla=obsah,
) )
text.save() text.save()
create_child(reseninode, m.TextNode, text=text) create_child(reseninode, TextNode, text=text)
# Několik odstavců lorem ipsum # Několik odstavců lorem ipsum
for _ in range(rnd.randint(3, 7)): for _ in range(rnd.randint(3, 7)):
lipsum = lorem.paragraph() lipsum = lorem.paragraph()
text = m.Text.objects.create( text = Text.objects.create(
na_web=lipsum, na_web=lipsum,
do_cisla=lipsum, do_cisla=lipsum,
) )
text.save() 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})") logger.info(f"Článek vygenerován (reseni={reseni.id}, treenode={reseninode.id})")

44
seminar/utils.py

@ -16,7 +16,11 @@ from django.core.exceptions import ObjectDoesNotExist
import logging import logging
import seminar.models as m from seminar.models.personalni import Organizator, Resitel, Skola, Prijemce
from seminar.models.tvorba import Clanek, Rocnik
from seminar.models.treenode import CisloNode
from seminar.models.soustredeni import Konfery_Ucastnici, Soustredeni_Ucastnici
from seminar.models.odevzdavatko import Reseni_Resitele
import treenode.treelib as t import treenode.treelib as t
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -121,31 +125,31 @@ def seznam_problemu():
# Duplicita jmen # Duplicita jmen
jmena = {} jmena = {}
for r in m.Resitel.objects.all(): for r in Resitel.objects.all():
j = r.osoba.plne_jmeno() j = r.osoba.plne_jmeno()
if j not in jmena: if j not in jmena:
jmena[j] = [] jmena[j] = []
jmena[j].append(r) jmena[j].append(r)
for j in jmena: for j in jmena:
if len(jmena[j]) > 1: 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í # Data maturity a narození
for r in m.Resitel.objects.all(): for r in Resitel.objects.all():
if not r.rok_maturity: 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): 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 ( if r.osoba.datum_narozeni and (
r.osoba.datum_narozeni.year < 1970 or r.osoba.datum_narozeni.year > datetime.date.today().year - 12): 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: # if not r.email:
# prb(Resitel, u'Neznámý email', [r]) # prb(Resitel, u'Neznámý email', [r])
## Kontroly konzistence databáze a TreeNodů ## Kontroly konzistence databáze a TreeNodů
# Články # Č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ě # získáme řešení svázané se článkem a z něj node ve stromě
reseni = clanek.reseni_set reseni = clanek.reseni_set
if (reseni.count() != 1): if (reseni.count() != 1):
@ -155,7 +159,7 @@ def seznam_problemu():
# content type je věc pomáhající rozeznávat různé typy objektů v django-polymorphic # 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 # protože isinstance vrátí vždy jen TreeNode
# https://django-polymorphic.readthedocs.io/en/stable/migrating.html # 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 node = clanek_node
while node is not None: while node is not None:
node_ct = node.polymorphic_ctype node_ct = node.polymorphic_ctype
@ -164,7 +168,7 @@ def seznam_problemu():
# .cislonode je opačná vazba k treenode_ptr, abychom z TreeNode dostali # .cislonode je opačná vazba k treenode_ptr, abychom z TreeNode dostali
# CisloNode # CisloNode
if clanek.cislo != node.cislonode.cislo: 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]) "číslem otištění podle struktury treenodů.", [clanek])
break break
node = t.get_parent(node) node = t.get_parent(node)
@ -185,10 +189,10 @@ def resi_v_rocniku(rocnik, cislo=None):
if cislo is None: if cislo is None:
# filtrujeme pouze podle ročníku # filtrujeme pouze podle ročníku
return m.Resitel.objects.filter(rok_maturity__gte=rocnik.druhy_rok(), return Resitel.objects.filter(rok_maturity__gte=rocnik.druhy_rok(),
reseni__hodnoceni__deadline_body__cislo__rocnik=rocnik).distinct() reseni__hodnoceni__deadline_body__cislo__rocnik=rocnik).distinct()
else: # filtrujeme podle ročníku i čísla else: # filtrujeme podle ročníku i čísla
return m.Resitel.objects.filter(rok_maturity__gte=rocnik.druhy_rok(), return Resitel.objects.filter(rok_maturity__gte=rocnik.druhy_rok(),
reseni__hodnoceni__deadline_body__cislo__rocnik=rocnik, reseni__hodnoceni__deadline_body__cislo__rocnik=rocnik,
reseni__hodnoceni__deadline_body__cislo__poradi__lte=cislo.poradi).distinct() reseni__hodnoceni__deadline_body__cislo__poradi__lte=cislo.poradi).distinct()
@ -218,7 +222,7 @@ def aktivniResitele(cislo, pouze_letosni=False):
zacatek_rocniku = False zacatek_rocniku = False
try: try:
loni = m.Rocnik.objects.get(rocnik=letos.rocnik - 1) loni = Rocnik.objects.get(rocnik=letos.rocnik - 1)
except ObjectDoesNotExist: except ObjectDoesNotExist:
# Pro první ročník neexistuje ročník předchozí # Pro první ročník neexistuje ročník předchozí
zacatek_rocniku = False zacatek_rocniku = False
@ -310,11 +314,11 @@ def merge_resitele(cilovy, zdrojovy):
# Přepojit všechny vazby ze zdrojového na cílového # Přepojit všechny vazby ze zdrojového na cílového
print('Přepojuji vazby') 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) # 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í') print(f' Přepojeno {ct} řešení')
ct = m.Konfery_Ucastnici.objects.filter(resitel=zdrojovy).update(resitel=cilovy) ct = Konfery_Ucastnici.objects.filter(resitel=zdrojovy).update(resitel=cilovy)
print(f' Přepojeno {ct} konfer') print(f' Přepojeno {ct} konfer')
ct = m.Soustredeni_Ucastnici.objects.filter(resitel=zdrojovy).update(resitel=cilovy) ct = Soustredeni_Ucastnici.objects.filter(resitel=zdrojovy).update(resitel=cilovy)
print(f' Přepojeno {ct} sousů') print(f' Přepojeno {ct} sousů')
# Teď by na zdrojovém řešiteli nemělo nic viset, smazat ho, pamatujíce si jeho Osobu # Teď by na zdrojovém řešiteli nemělo nic viset, smazat ho, pamatujíce si jeho Osobu
@ -368,14 +372,14 @@ def merge_osoby(cilova, zdrojova):
# Vazby: Řešitel, User, Příjemce, Organizátor, Škola.kontaktní_osoba # Vazby: Řešitel, User, Příjemce, Organizátor, Škola.kontaktní_osoba
print('Přepojuji vazby') print('Přepojuji vazby')
ct = m.Skola.objects.filter(kontaktni_osoba=zdrojova).update(kontaktni_osoba=cilova) ct = Skola.objects.filter(kontaktni_osoba=zdrojova).update(kontaktni_osoba=cilova)
print(f' Přepojeno {ct} kontaktních osob') print(f' Přepojeno {ct} kontaktních osob')
# Ostatní vazby vyřeší OneToOneFieldy, ale někdy nemusí existovat… # Ostatní vazby vyřeší OneToOneFieldy, ale někdy nemusí existovat…
ct = m.Resitel.objects.filter(osoba=zdrojova).update(osoba=cilova) ct = Resitel.objects.filter(osoba=zdrojova).update(osoba=cilova)
print(f' Přepojeno {ct} řešitelů') print(f' Přepojeno {ct} řešitelů')
ct = m.Prijemce.objects.filter(osoba=zdrojova).update(osoba=cilova) ct = Prijemce.objects.filter(osoba=zdrojova).update(osoba=cilova)
print(f' Přepojeno {ct} příjemců') print(f' Přepojeno {ct} příjemců')
ct = m.Organizator.objects.filter(osoba=zdrojova).update(osoba=cilova) ct = Organizator.objects.filter(osoba=zdrojova).update(osoba=cilova)
print(f' Přepojeno {ct} organizátorů') print(f' Přepojeno {ct} organizátorů')
# Uživatelé vedou opačným směrem, radši chceme zkontrolovat, že jsou různí ručně: # Uživatelé vedou opačným směrem, radši chceme zkontrolovat, že jsou různí ručně:
if zdrojova.user != cilova.user: if zdrojova.user != cilova.user:

14
seminar/views/docasne.py

@ -5,13 +5,13 @@ from django.db import transaction
from django.forms import Form, CharField, IntegerField from django.forms import Form, CharField, IntegerField
from django.views.generic import FormView from django.views.generic import FormView
import seminar.models as m from seminar.models.tvorba import Cislo, Problem, Tema, Uloha
from django.shortcuts import render, get_object_or_404 from django.shortcuts import render, get_object_or_404
def problemView(request, pk): def problemView(request, pk):
# Pokud problém neexistuje, hodíme obyčejnou 404 # Pokud problém neexistuje, hodíme obyčejnou 404
# Taktéž v případě, že takový problém nemá být vidět # 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 # Problém existuje, neumíme ho zobrazit, renderujeme nějakou haluz
template = 'universal.html' template = 'universal.html'
ctx = { ctx = {
@ -32,7 +32,7 @@ class HromadnePridaniForm(Form):
def clean_tema(self): def clean_tema(self):
""" Kontrola, že `tema` je název právě jednoho tématu """ """ 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'], nazev__exact=self.cleaned_data['tema'],
nadproblem=None).count() != 1: nadproblem=None).count() != 1:
raise ValidationError("Špatný nebo nepřesně zadaný název témátka") raise ValidationError("Špatný nebo nepřesně zadaný název témátka")
@ -67,20 +67,20 @@ class HromadnePridaniView(FormView):
dil = cd["dil"] dil = cd["dil"]
body = list(map(int, cd["body"].split(","))) 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(): with transaction.atomic():
pfx = f"{t.nazev}, díl {dil}, " pfx = f"{t.nazev}, díl {dil}, "
for k, b in enumerate(body, 1): for k, b in enumerate(body, 1):
u = m.Uloha.objects.create( u = Uloha.objects.create(
nadproblem=t, nadproblem=t,
nazev=pfx + f"{'úloha' if b > 0 else 'problém'} {k}", nazev=pfx + f"{'úloha' if b > 0 else 'problém'} {k}",
autor=t.autor, autor=t.autor,
garant=t.garant, garant=t.garant,
max_body=b, max_body=b,
cislo_zadani=m.Cislo.get(t.rocnik.rocnik, dil), cislo_zadani=Cislo.get(t.rocnik.rocnik, dil),
kod=k, kod=k,
stav=m.Problem.STAV_ZADANY, stav=Problem.STAV_ZADANY,
) )
u.opravovatele.set(t.opravovatele.all()) u.opravovatele.set(t.opravovatele.all())
return super().form_valid(form) return super().form_valid(form)

56
seminar/views/views_all.py

@ -9,11 +9,9 @@ from django.db.models import Q, Sum, Count
from django.views.generic.base import RedirectView from django.views.generic.base import RedirectView
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
import seminar.models as s from seminar.models.tvorba import Rocnik, Cislo, Nastaveni, Deadline, Problem, Tema, Clanek
import seminar.models as m from seminar.models.personalni import Resitel, Organizator
from seminar.models import Problem, Cislo, Reseni, Nastaveni, Rocnik, \ from seminar.models.novinky import Novinky
Organizator, Resitel, Novinky, Tema, Clanek, \
Deadline # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci
#from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva #from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva
from seminar import utils from seminar import utils
from treenode import treelib from treenode import treelib
@ -57,7 +55,7 @@ def get_problemy_k_tematu(tema):
# FIXME: Pozor, níž je ještě jeden ProblemView! # FIXME: Pozor, níž je ještě jeden ProblemView!
#class ProblemView(generic.DetailView): #class ProblemView(generic.DetailView):
# model = s.Problem # model = Problem
# # Zkopírujeme template_name od TreeNodeView, protože jsme prakticky jen trošku upravený TreeNodeView # # Zkopírujeme template_name od TreeNodeView, protože jsme prakticky jen trošku upravený TreeNodeView
# template_name = TreeNodeView.template_name # template_name = TreeNodeView.template_name
# #
@ -69,17 +67,17 @@ def get_problemy_k_tematu(tema):
# if False: # if False:
# # Hezčí formátování zbytku :-P # # Hezčí formátování zbytku :-P
# pass # 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 # # Tyhle Problémy mají ŘešeníNode
# context['tnldata'] = TNLData.from_treenode(self.object.reseninode,user) # 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 # # 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_zadani = TNLData.from_treenode(self.object.ulohazadaninode,user)
# tnl_vzorak = TNLData.from_treenode(self.object.ulohavzoraknode,user) # tnl_vzorak = TNLData.from_treenode(self.object.ulohavzoraknode,user)
# context['tnldata'] = TNLData.from_tnldata_list([tnl_zadani, tnl_vzorak]) # 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 # 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, TemaVCisleNode))
# else: # else:
# raise ValueError("Obecný problém nejde zobrazit.") # raise ValueError("Obecný problém nejde zobrazit.")
# return context # return context
@ -114,7 +112,7 @@ def ZadaniTemataView(request):
nastaveni = get_object_or_404(Nastaveni) nastaveni = get_object_or_404(Nastaveni)
verejne = nastaveni.aktualni_cislo.verejne() verejne = nastaveni.aktualni_cislo.verejne()
akt_rocnik = nastaveni.aktualni_cislo.rocnik 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, 'seminar/tematka/rozcestnik.html', return render(request, 'seminar/tematka/rozcestnik.html',
{ {
'tematka': temata, 'tematka': temata,
@ -139,14 +137,14 @@ def ZadaniTemataView(request):
# #
# #
#def TematkoView(request, rocnik, tematko): #def TematkoView(request, rocnik, tematko):
# nastaveni = s.Nastaveni.objects.first() # nastaveni = Nastaveni.objects.first()
# rocnik_object = s.Rocnik.objects.filter(rocnik=rocnik) # rocnik_object = Rocnik.objects.filter(rocnik=rocnik)
# tematko_object = s.Tema.objects.filter(rocnik=rocnik_object[0], kod=tematko) # tematko_object = Tema.objects.filter(rocnik=rocnik_object[0], kod=tematko)
# seznam = vytahniZLesaSeznam(tematko_object[0], nastaveni.aktualni_rocnik().rocniknode) # seznam = vytahniZLesaSeznam(tematko_object[0], nastaveni.aktualni_rocnik().rocniknode)
# for node, depth in seznam: # for node, depth in seznam:
# if node.isinstance(node, s.KonferaNode): # if node.isinstance(node, KonferaNode):
# raise Exception("Not implemented yet") # raise Exception("Not implemented yet")
# if node.isinstance(node, s.PohadkaNode): # Mohu ignorovat, má pod sebou # if node.isinstance(node, PohadkaNode): # Mohu ignorovat, má pod sebou
# pass # pass
# #
# return render(request, 'seminar/tematka/toaletak.html', {}) # return render(request, 'seminar/tematka/toaletak.html', {})
@ -154,8 +152,8 @@ def ZadaniTemataView(request):
# #
#def TemataRozcestnikView(request): #def TemataRozcestnikView(request):
# print("=============================================") # print("=============================================")
# nastaveni = s.Nastaveni.objects.first() # nastaveni = Nastaveni.objects.first()
# tematka_objects = s.Tema.objects.filter(rocnik=nastaveni.aktualni_rocnik()) # 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 # tematka = [] #List tematka obsahuje pro kazde tematko object a list vsech TemaVCisleNodu - implementované pomocí slovníku
# for tematko_object in tematka_objects: # for tematko_object in tematka_objects:
# print("AKTUALNI TEMATKO") # print("AKTUALNI TEMATKO")
@ -245,7 +243,7 @@ class TitulniStranaView(generic.ListView):
context = super(TitulniStranaView, self).get_context_data(**kwargs) context = super(TitulniStranaView, self).get_context_data(**kwargs)
nastaveni = get_object_or_404(Nastaveni) nastaveni = get_object_or_404(Nastaveni)
deadline = m.Deadline.objects.filter(deadline__gte=timezone.now()).order_by("deadline").first() deadline = Deadline.objects.filter(deadline__gte=timezone.now()).order_by("deadline").first()
context['nejblizsi_deadline'] = deadline context['nejblizsi_deadline'] = deadline
# Aktuální témata # Aktuální témata
@ -350,7 +348,7 @@ def resiteleRocnikuCsvExportView(request, rocnik):
assert request.method in ('GET', 'HEAD') assert request.method in ('GET', 'HEAD')
return dataResiteluCsvResponse( return dataResiteluCsvResponse(
utils.resi_v_rocniku( utils.resi_v_rocniku(
get_object_or_404(m.Rocnik, rocnik=rocnik) get_object_or_404(Rocnik, rocnik=rocnik)
) )
) )
@ -363,10 +361,10 @@ def resiteleRocnikuCsvExportView(request, rocnik):
# def get_template_names(self, **kwargs): # 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. # # 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 = { # spravne_templaty = {
# s.Uloha: "uloha", # Uloha: "uloha",
# s.Tema: "tema", # Tema: "tema",
# s.Konfera: "konfera", # Konfera: "konfera",
# s.Clanek: "clanek", # Clanek: "clanek",
# } # }
# context = super().get_context_data(**kwargs) # context = super().get_context_data(**kwargs)
# return ['seminar/archiv/problem_' + spravne_templaty[context['object'].__class__] + '.html'] # return ['seminar/archiv/problem_' + spravne_templaty[context['object'].__class__] + '.html']
@ -412,10 +410,10 @@ class CisloView(generic.DetailView):
deadliny_s_vysledkovkami = [] deadliny_s_vysledkovkami = []
nadpisy = { nadpisy = {
m.Deadline.TYP_CISLA: "Výsledkovka", Deadline.TYP_CISLA: "Výsledkovka",
m.Deadline.TYP_PRVNI: "Výsledkovka do prvního deadlinu", 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í", 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_SOUS: "Výsledkovka do deadlinu pro účast na soustředění",
} }
for deadline in deadliny: for deadline in deadliny:
@ -706,5 +704,5 @@ class AktualniRocnikRedirectView(RedirectView):
pattern_name = 'seminar_rocnik' pattern_name = 'seminar_rocnik'
def get_redirect_url(self, *args, **kwargs): 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) return super().get_redirect_url(rocnik=aktualni_rocnik, *args, **kwargs)

10
soustredeni/admin.py

@ -2,11 +2,11 @@ from django.contrib import admin
from django.forms import widgets from django.forms import widgets
from django.db import models from django.db import models
from seminar.models import soustredeni as m from seminar.models.soustredeni import *
class SoustredeniUcastniciInline(admin.TabularInline): class SoustredeniUcastniciInline(admin.TabularInline):
model = m.Soustredeni_Ucastnici model = Soustredeni_Ucastnici
extra = 1 extra = 1
fields = ['resitel','poznamka'] fields = ['resitel','poznamka']
autocomplete_fields = ['resitel'] autocomplete_fields = ['resitel']
@ -21,7 +21,7 @@ class SoustredeniUcastniciInline(admin.TabularInline):
class SoustredeniOrganizatoriInline(admin.TabularInline): class SoustredeniOrganizatoriInline(admin.TabularInline):
model = m.Soustredeni.organizatori.through model = Soustredeni.organizatori.through
extra = 1 extra = 1
fields = ['organizator','poznamka'] fields = ['organizator','poznamka']
autocomplete_fields = ['organizator'] autocomplete_fields = ['organizator']
@ -35,9 +35,9 @@ class SoustredeniOrganizatoriInline(admin.TabularInline):
return qs.select_related('organizator', 'soustredeni') return qs.select_related('organizator', 'soustredeni')
@admin.register(m.Soustredeni) @admin.register(Soustredeni)
class SoustredeniAdmin(admin.ModelAdmin): class SoustredeniAdmin(admin.ModelAdmin):
model = m.Soustredeni model = Soustredeni
inline_type = 'tabular' inline_type = 'tabular'
inlines = [SoustredeniUcastniciInline, SoustredeniOrganizatoriInline] inlines = [SoustredeniUcastniciInline, SoustredeniOrganizatoriInline]

4
soustredeni/views.py

@ -3,7 +3,9 @@ from django.http import HttpResponse
from django.views import generic from django.views import generic
from django.conf import settings from django.conf import settings
from django.contrib.staticfiles.finders import find from django.contrib.staticfiles.finders import find
from seminar.models import Soustredeni, Resitel, Soustredeni_Ucastnici, Nastaveni # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci from seminar.models.soustredeni import *
from seminar.models.personalni import Resitel
from seminar.models.tvorba import Nastaveni
import csv import csv
import tempfile import tempfile
import shutil import shutil

66
treenode/admin.py

@ -2,25 +2,25 @@ from django.contrib import admin
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter
import seminar.models as m from seminar.models.treenode import *
# Polymorfismus pro stromy # Polymorfismus pro stromy
# TODO: Inlines podle https://django-polymorphic.readthedocs.io/en/stable/admin.html # TODO: Inlines podle https://django-polymorphic.readthedocs.io/en/stable/admin.html
@admin.register(m.TreeNode) @admin.register(TreeNode)
class TreeNodeAdmin(PolymorphicParentModelAdmin): class TreeNodeAdmin(PolymorphicParentModelAdmin):
base_model = m.TreeNode base_model = TreeNode
child_models = [ child_models = [
m.RocnikNode, RocnikNode,
m.CisloNode, CisloNode,
m.MezicisloNode, MezicisloNode,
m.TemaVCisleNode, TemaVCisleNode,
m.UlohaZadaniNode, UlohaZadaniNode,
m.PohadkaNode, PohadkaNode,
m.UlohaVzorakNode, UlohaVzorakNode,
m.TextNode, TextNode,
m.CastNode, CastNode,
m.OrgTextNode, OrgTextNode,
] ]
actions = ['aktualizuj_nazvy'] actions = ['aktualizuj_nazvy']
@ -34,55 +34,55 @@ class TreeNodeAdmin(PolymorphicParentModelAdmin):
self.message_user(request, "Názvy aktualizovány.") self.message_user(request, "Názvy aktualizovány.")
aktualizuj_nazvy.short_description = "Aktualizuj vybraným TreeNodům názvy" aktualizuj_nazvy.short_description = "Aktualizuj vybraným TreeNodům názvy"
@admin.register(m.RocnikNode) @admin.register(RocnikNode)
class RocnikNodeAdmin(PolymorphicChildModelAdmin): class RocnikNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.RocnikNode base_model = RocnikNode
show_in_index = True show_in_index = True
@admin.register(m.CisloNode) @admin.register(CisloNode)
class CisloNodeAdmin(PolymorphicChildModelAdmin): class CisloNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.CisloNode base_model = CisloNode
show_in_index = True show_in_index = True
@admin.register(m.MezicisloNode) @admin.register(MezicisloNode)
class MezicisloNodeAdmin(PolymorphicChildModelAdmin): class MezicisloNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.MezicisloNode base_model = MezicisloNode
show_in_index = True show_in_index = True
@admin.register(m.TemaVCisleNode) @admin.register(TemaVCisleNode)
class TemaVCisleNodeAdmin(PolymorphicChildModelAdmin): class TemaVCisleNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.TemaVCisleNode base_model = TemaVCisleNode
show_in_index = True show_in_index = True
@admin.register(m.UlohaZadaniNode) @admin.register(UlohaZadaniNode)
class UlohaZadaniNodeAdmin(PolymorphicChildModelAdmin): class UlohaZadaniNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.UlohaZadaniNode base_model = UlohaZadaniNode
show_in_index = True show_in_index = True
@admin.register(m.PohadkaNode) @admin.register(PohadkaNode)
class PohadkaNodeAdmin(PolymorphicChildModelAdmin): class PohadkaNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.PohadkaNode base_model = PohadkaNode
show_in_index = True show_in_index = True
@admin.register(m.UlohaVzorakNode) @admin.register(UlohaVzorakNode)
class UlohaVzorakNodeAdmin(PolymorphicChildModelAdmin): class UlohaVzorakNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.UlohaVzorakNode base_model = UlohaVzorakNode
show_in_index = True show_in_index = True
@admin.register(m.TextNode) @admin.register(TextNode)
class TextNodeAdmin(PolymorphicChildModelAdmin): class TextNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.TextNode base_model = TextNode
show_in_index = True show_in_index = True
@admin.register(m.CastNode) @admin.register(CastNode)
class TextNodeAdmin(PolymorphicChildModelAdmin): class TextNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.CastNode base_model = CastNode
show_in_index = True show_in_index = True
fields = ('nadpis',) fields = ('nadpis',)
@admin.register(m.OrgTextNode) @admin.register(OrgTextNode)
class TextNodeAdmin(PolymorphicChildModelAdmin): class TextNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.OrgTextNode base_model = OrgTextNode
show_in_index = True show_in_index = True

4
treenode/forms.py

@ -1,5 +1,5 @@
from django import forms from django import forms
import seminar.models as m from seminar.models.pomocne import Obrazek
# pro přidání políčka do formuláře je potřeba # pro přidání políčka do formuláře je potřeba
# - mít v modelu tu položku, kterou chci upravovat # - mít v modelu tu položku, kterou chci upravovat
@ -10,5 +10,5 @@ import seminar.models as m
class NahrajObrazekKTreeNoduForm(forms.ModelForm): class NahrajObrazekKTreeNoduForm(forms.ModelForm):
class Meta: class Meta:
model = m.Obrazek model = Obrazek
fields = ('na_web',) fields = ('na_web',)

131
treenode/serializers.py

@ -1,7 +1,10 @@
from rest_framework import serializers from rest_framework import serializers
from rest_polymorphic.serializers import PolymorphicSerializer from rest_polymorphic.serializers import PolymorphicSerializer
import seminar.models as m from seminar.models.treenode import *
from seminar.models.pomocne import Text
from seminar.models.odevzdavatko import Reseni, ReseniNode
from seminar.models.tvorba import Problem, Uloha
from treenode import treelib from treenode import treelib
DEFAULT_NODE_DEPTH = 2 DEFAULT_NODE_DEPTH = 2
@ -9,57 +12,57 @@ DEFAULT_NODE_DEPTH = 2
class TextSerializer(serializers.ModelSerializer): class TextSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = m.Text model = Text
fields = '__all__' fields = '__all__'
class ProblemSerializer(serializers.ModelSerializer): class ProblemSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = m.Problem model = Problem
fields = '__all__' fields = '__all__'
class UlohaSerializer(serializers.ModelSerializer): class UlohaSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = m.Uloha model = Uloha
fields = '__all__' fields = '__all__'
class ReseniSerializer(serializers.ModelSerializer): class ReseniSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = m.Reseni model = Reseni
fields = '__all__' fields = '__all__'
class RocnikNodeSerializer(serializers.ModelSerializer): class RocnikNodeSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = m.RocnikNode model = RocnikNode
fields = '__all__' fields = '__all__'
depth = DEFAULT_NODE_DEPTH depth = DEFAULT_NODE_DEPTH
class CisloNodeSerializer(serializers.ModelSerializer): class CisloNodeSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = m.CisloNode model = CisloNode
fields = '__all__' fields = '__all__'
depth = DEFAULT_NODE_DEPTH depth = DEFAULT_NODE_DEPTH
class MezicisloNodeSerializer(serializers.ModelSerializer): class MezicisloNodeSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = m.MezicisloNode model = MezicisloNode
fields = '__all__' fields = '__all__'
depth = DEFAULT_NODE_DEPTH depth = DEFAULT_NODE_DEPTH
class TemaVCisleNodeSerializer(serializers.ModelSerializer): class TemaVCisleNodeSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = m.TemaVCisleNode model = TemaVCisleNode
fields = '__all__' fields = '__all__'
depth = DEFAULT_NODE_DEPTH depth = DEFAULT_NODE_DEPTH
class OrgTextNodeSerializer(serializers.ModelSerializer): class OrgTextNodeSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = m.OrgTextNode model = OrgTextNode
fields = '__all__' fields = '__all__'
depth = DEFAULT_NODE_DEPTH depth = DEFAULT_NODE_DEPTH
class PohadkaNodeSerializer(serializers.ModelSerializer): class PohadkaNodeSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = m.PohadkaNode model = PohadkaNode
fields = '__all__' fields = '__all__'
depth = DEFAULT_NODE_DEPTH depth = DEFAULT_NODE_DEPTH
@ -67,7 +70,7 @@ class TextNodeSerializer(serializers.ModelSerializer):
text = TextSerializer() text = TextSerializer()
class Meta: class Meta:
model = m.TextNode model = TextNode
fields = ('id','text','polymorphic_ctype') fields = ('id','text','polymorphic_ctype')
depth = DEFAULT_NODE_DEPTH depth = DEFAULT_NODE_DEPTH
@ -80,7 +83,7 @@ class TextNodeWriteSerializer(serializers.ModelSerializer):
return node return node
class Meta: class Meta:
model = m.TextNode model = TextNode
fields = ('id','text') fields = ('id','text')
depth = DEFAULT_NODE_DEPTH depth = DEFAULT_NODE_DEPTH
@ -93,26 +96,26 @@ class TextNodeCreateSerializer(serializers.ModelSerializer):
temp_text = validated_data.pop('text') temp_text = validated_data.pop('text')
where = validated_data.pop('where') where = validated_data.pop('where')
refnode_id = validated_data.pop('refnode') refnode_id = validated_data.pop('refnode')
refnode = m.TreeNode.objects.get(pk=refnode_id) refnode = TreeNode.objects.get(pk=refnode_id)
text = m.Text.objects.create(**temp_text) text = Text.objects.create(**temp_text)
if where == 'syn': if where == 'syn':
node = treelib.create_child(refnode,m.TextNode,text=text) node = treelib.create_child(refnode,TextNode,text=text)
elif where == 'za': 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': 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.where = None
node.refnode = None node.refnode = None
return node return node
class Meta: class Meta:
model = m.TextNode model = TextNode
fields = ('text','where','refnode') fields = ('text','where','refnode')
depth = DEFAULT_NODE_DEPTH depth = DEFAULT_NODE_DEPTH
class CastNodeSerializer(serializers.ModelSerializer): class CastNodeSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = m.CastNode model = CastNode
fields = '__all__' fields = '__all__'
depth = DEFAULT_NODE_DEPTH depth = DEFAULT_NODE_DEPTH
@ -124,25 +127,25 @@ class CastNodeCreateSerializer(serializers.ModelSerializer):
temp_nadpis = validated_data.pop('nadpis') temp_nadpis = validated_data.pop('nadpis')
where = validated_data.pop('where') where = validated_data.pop('where')
refnode_id = validated_data.pop('refnode') refnode_id = validated_data.pop('refnode')
refnode = m.TreeNode.objects.get(pk=refnode_id) refnode = TreeNode.objects.get(pk=refnode_id)
if where == 'syn': 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': 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': 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.where = None
node.refnode = None node.refnode = None
return node return node
class Meta: class Meta:
model = m.CastNode model = CastNode
fields = ('nadpis','where','refnode') fields = ('nadpis','where','refnode')
depth = DEFAULT_NODE_DEPTH depth = DEFAULT_NODE_DEPTH
class UlohaZadaniNodeSerializer(serializers.ModelSerializer): class UlohaZadaniNodeSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = m.UlohaZadaniNode model = UlohaZadaniNode
fields = '__all__' fields = '__all__'
depth = DEFAULT_NODE_DEPTH depth = DEFAULT_NODE_DEPTH
@ -157,7 +160,7 @@ class UlohaZadaniNodeWriteSerializer(serializers.ModelSerializer):
return node return node
class Meta: class Meta:
model = m.TextNode model = TextNode
fields = ('id','uloha') fields = ('id','uloha')
depth = DEFAULT_NODE_DEPTH depth = DEFAULT_NODE_DEPTH
@ -171,28 +174,28 @@ class UlohaZadaniNodeCreateSerializer(serializers.ModelSerializer):
temp_uloha = validated_data.pop('uloha') temp_uloha = validated_data.pop('uloha')
where = validated_data.pop('where') where = validated_data.pop('where')
refnode_id = validated_data.pop('refnode') 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 # Z cesty ke koreni stromu zjistime, v jakem jsme tematu a v jakem cisle
cislo = None cislo = None
tema = None tema = None
travelnode = refnode travelnode = refnode
while travelnode is not None: while travelnode is not None:
if isinstance(travelnode, m.TemaVCisleNode): if isinstance(travelnode, TemaVCisleNode):
tema = travelnode.tema tema = travelnode.tema
if isinstance(travelnode, m.CisloNode): if isinstance(travelnode, CisloNode):
cislo = travelnode.cislo cislo = travelnode.cislo
travelnode = treelib.get_parent(travelnode) travelnode = treelib.get_parent(travelnode)
# Vyrobime ulohu # 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 # A vyrobime UlohaZadaniNode
if where == 'syn': if where == 'syn':
node = treelib.create_child(refnode,m.UlohaZadaniNode,uloha = uloha) node = treelib.create_child(refnode,UlohaZadaniNode,uloha = uloha)
elif where == 'za': 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': 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.where = None
node.refnode = None node.refnode = None
node.max_body = None node.max_body = None
@ -200,21 +203,21 @@ class UlohaZadaniNodeCreateSerializer(serializers.ModelSerializer):
return node return node
class Meta: class Meta:
model = m.UlohaZadaniNode model = UlohaZadaniNode
fields = ('uloha','where','refnode') fields = ('uloha','where','refnode')
depth = DEFAULT_NODE_DEPTH depth = DEFAULT_NODE_DEPTH
class UlohaVzorakNodeSerializer(serializers.ModelSerializer): class UlohaVzorakNodeSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = m.UlohaVzorakNode model = UlohaVzorakNode
fields = '__all__' fields = '__all__'
depth = DEFAULT_NODE_DEPTH depth = DEFAULT_NODE_DEPTH
class UlohaVzorakNodeWriteSerializer(serializers.ModelSerializer): 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: class Meta:
model = m.UlohaVzorakNode model = UlohaVzorakNode
fields = ('id','uloha') fields = ('id','uloha')
depth = DEFAULT_NODE_DEPTH depth = DEFAULT_NODE_DEPTH
@ -226,17 +229,17 @@ class UlohaVzorakNodeCreateSerializer(serializers.ModelSerializer):
def create(self, validated_data): def create(self, validated_data):
uloha_id = validated_data.pop('uloha_id') 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') where = validated_data.pop('where')
refnode_id = validated_data.pop('refnode') refnode_id = validated_data.pop('refnode')
refnode = m.TreeNode.objects.get(pk=refnode_id) refnode = TreeNode.objects.get(pk=refnode_id)
if where == 'syn': if where == 'syn':
node = treelib.create_child(refnode,m.UlohaVzorakNode,uloha = uloha) node = treelib.create_child(refnode,UlohaVzorakNode,uloha = uloha)
elif where == 'za': 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': 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.refnode = None
node.where = None node.where = None
node.uloha_id = None node.uloha_id = None
@ -244,7 +247,7 @@ class UlohaVzorakNodeCreateSerializer(serializers.ModelSerializer):
return node return node
class Meta: class Meta:
model = m.UlohaVzorakNode model = UlohaVzorakNode
fields = ('refnode', 'uloha_id', 'where') fields = ('refnode', 'uloha_id', 'where')
depth = DEFAULT_NODE_DEPTH depth = DEFAULT_NODE_DEPTH
@ -253,15 +256,15 @@ class UlohaVzorakNodeCreateSerializer(serializers.ModelSerializer):
class ReseniNodeSerializer(serializers.ModelSerializer): class ReseniNodeSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = m.ReseniNode model = ReseniNode
fields = '__all__' fields = '__all__'
depth = DEFAULT_NODE_DEPTH depth = DEFAULT_NODE_DEPTH
class ReseniNodeWriteSerializer(serializers.ModelSerializer): 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: class Meta:
model = m.ReseniNode model = ReseniNode
fields = ('id','reseni') fields = ('id','reseni')
depth = DEFAULT_NODE_DEPTH depth = DEFAULT_NODE_DEPTH
@ -273,41 +276,41 @@ class ReseniNodeCreateSerializer(serializers.ModelSerializer):
def create(self,validated_data): def create(self,validated_data):
# text_zadani = validated_data.pop('text_zadani') # text_zadani = validated_data.pop('text_zadani')
reseni_id = validated_data.pop('reseni_id') 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') where = validated_data.pop('where')
refnode_id = validated_data.pop('refnode') refnode_id = validated_data.pop('refnode')
refnode = m.TreeNode.objects.get(pk=refnode_id) refnode = TreeNode.objects.get(pk=refnode_id)
# A vyrobime UlohaZadaniNode # A vyrobime UlohaZadaniNode
if where == 'syn': if where == 'syn':
node = treelib.create_child(refnode,m.ReseniNode,reseni = reseni) node = treelib.create_child(refnode,ReseniNode,reseni = reseni)
elif where == 'za': 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': 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.where = None
node.refnode = None node.refnode = None
node.reseni_id = None node.reseni_id = None
return node return node
class Meta: class Meta:
model = m.ReseniNode model = ReseniNode
fields = ('reseni_id','where','refnode') fields = ('reseni_id','where','refnode')
depth = DEFAULT_NODE_DEPTH depth = DEFAULT_NODE_DEPTH
class TreeNodeSerializer(PolymorphicSerializer): class TreeNodeSerializer(PolymorphicSerializer):
model_serializer_mapping = { model_serializer_mapping = {
m.RocnikNode: RocnikNodeSerializer, RocnikNode: RocnikNodeSerializer,
m.CisloNode: CisloNodeSerializer, CisloNode: CisloNodeSerializer,
m.MezicisloNode: MezicisloNodeSerializer, MezicisloNode: MezicisloNodeSerializer,
m.TemaVCisleNode: TemaVCisleNodeSerializer, TemaVCisleNode: TemaVCisleNodeSerializer,
m.OrgTextNode: OrgTextNodeSerializer, OrgTextNode: OrgTextNodeSerializer,
m.UlohaZadaniNode: UlohaZadaniNodeSerializer, UlohaZadaniNode: UlohaZadaniNodeSerializer,
m.UlohaVzorakNode: UlohaVzorakNodeSerializer, UlohaVzorakNode: UlohaVzorakNodeSerializer,
m.PohadkaNode: PohadkaNodeSerializer, PohadkaNode: PohadkaNodeSerializer,
m.TextNode: TextNodeSerializer, TextNode: TextNodeSerializer,
m.CastNode: CastNodeSerializer, CastNode: CastNodeSerializer,
m.ReseniNode: ReseniNodeSerializer, ReseniNode: ReseniNodeSerializer,
} }

26
treenode/templatetags.py

@ -1,6 +1,6 @@
from django import template from django import template
from enum import Enum from enum import Enum
import seminar.models as m from seminar.models.treenode import *
register = template.Library() register = template.Library()
@ -22,53 +22,53 @@ def nodeType(value):
@register.filter @register.filter
def isRocnik(value): def isRocnik(value):
return isinstance(value, m.RocnikNode) return isinstance(value, RocnikNode)
@register.filter @register.filter
def isCislo(value): def isCislo(value):
return isinstance(value, m.CisloNode) return isinstance(value, CisloNode)
@register.filter @register.filter
def isCast(value): def isCast(value):
return isinstance(value, m.CastNode) return isinstance(value, CastNode)
@register.filter @register.filter
def isText(value): def isText(value):
return isinstance(value, m.TextNode) return isinstance(value, TextNode)
@register.filter @register.filter
def isTemaVCisle(value): def isTemaVCisle(value):
return isinstance(value, m.TemaVCisleNode) return isinstance(value, TemaVCisleNode)
@register.filter @register.filter
def isKonfera(value): def isKonfera(value):
return isinstance(value, m.KonferaNode) return isinstance(value, KonferaNode)
@register.filter @register.filter
def isClanek(value): def isClanek(value):
return isinstance(value, m.ClanekNode) return isinstance(value, ClanekNode)
@register.filter @register.filter
def isUlohaVzorak(value): def isUlohaVzorak(value):
return isinstance(value, m.UlohaVzorakNode) return isinstance(value, UlohaVzorakNode)
@register.filter @register.filter
def isUlohaZadani(value): def isUlohaZadani(value):
return isinstance(value, m.UlohaZadaniNode) return isinstance(value, UlohaZadaniNode)
@register.filter @register.filter
def isPohadka(value): def isPohadka(value):
return isinstance(value, m.PohadkaNode) return isinstance(value, PohadkaNode)
@register.filter @register.filter
def isReseni(value): def isReseni(value):
return False return False
# return isinstance(value, m.OtisteneReseniNode) # return isinstance(value, OtisteneReseniNode)
@register.filter @register.filter
def isOrgText(value): def isOrgText(value):
return False return False
# return isinstance(value, m.OrgTextNode) # return isinstance(value, OrgTextNode)
### ###

12
treenode/tests.py

@ -1,16 +1,16 @@
from django.test import TestCase from django.test import TestCase
import treenode.treelib as tl import treenode.treelib as tl
import seminar.models as m from seminar.models.treenode import *
class SimpleTreeLibTests(TestCase): class SimpleTreeLibTests(TestCase):
def setUp(self): def setUp(self):
# Vyrobíme pár nějakých Nodů # 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.root.save()
self.some_node = m.CastNode(root=self.root, first_child=None, succ=None, nadpis="Přetržené") self.some_node = 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.other_node = 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.some_orphan = 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.other_orphan = CastNode(root=None, first_child=None, succ=None, nadpis="Káčátko")
# Trochu je pospojujeme # Trochu je pospojujeme
self.root.first_child = self.some_node self.root.first_child = self.some_node

6
treenode/treelib.py

@ -238,7 +238,7 @@ class TreeLibError(RuntimeError):
# Editace stromu: # Editace stromu:
def create_node_after(predecessor, type, **kwargs): def create_node_after(predecessor, type, **kwargs):
from seminar.models import TreeNode from seminar.models.treenode import TreeNode
if predecessor is None: if predecessor is None:
raise TreeLibError("Nelze vyrábět sirotky! (predecessor=None)") raise TreeLibError("Nelze vyrábět sirotky! (predecessor=None)")
if not issubclass(type, TreeNode): 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) # Vyrábí prvního syna, ostatní nalepí za (existují-li)
def create_child(parent, type, **kwargs): def create_child(parent, type, **kwargs):
from seminar.models import TreeNode from seminar.models.treenode import TreeNode
if parent is None: if parent is None:
raise TreeLibError("Nelze vyrábět sirotky! (parent=None)") raise TreeLibError("Nelze vyrábět sirotky! (parent=None)")
if not issubclass(type, TreeNode): if not issubclass(type, TreeNode):
@ -293,7 +293,7 @@ def insert_last_child(parent, node):
last.save() last.save()
def create_node_before(successor, type, **kwargs): def create_node_before(successor, type, **kwargs):
from seminar.models import TreeNode from seminar.models.treenode import TreeNode
if successor is None: if successor is None:
raise TreeLibError("Nelze vyrábět sirotky! (successor=None)") raise TreeLibError("Nelze vyrábět sirotky! (successor=None)")
if not issubclass(type, TreeNode): if not issubclass(type, TreeNode):

83
treenode/views.py

@ -6,8 +6,9 @@ from django.views.generic.edit import CreateView
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
import seminar.models as s from seminar.models.treenode import *
import seminar.models as m from seminar.models.pomocne import Text, Obrazek
from seminar.models.odevzdavatko import ReseniNode
from treenode import treelib from treenode import treelib
import treenode.forms as f import treenode.forms as f
import treenode.templatetags as tnltt import treenode.templatetags as tnltt
@ -29,7 +30,7 @@ class TNLData(object):
if parent: if parent:
self.tema_in_path = parent.tema_in_path self.tema_in_path = parent.tema_in_path
if isinstance(anode, m.TemaVCisleNode): if isinstance(anode, TemaVCisleNode):
self.tema_in_path = True self.tema_in_path = True
def add_edit_options(self): def add_edit_options(self):
@ -51,11 +52,11 @@ class TNLData(object):
(All of them have method verejne.)""" (All of them have method verejne.)"""
parent = anode # chceme začít už od konkrétního node včetně parent = anode # chceme začít už od konkrétního node včetně
while True: while True:
rocnik = isinstance(parent, s.RocnikNode) rocnik = isinstance(parent, RocnikNode)
cislo = isinstance(parent, s.CisloNode) cislo = isinstance(parent, CisloNode)
uloha = (isinstance(parent, s.UlohaVzorakNode) or uloha = (isinstance(parent, UlohaVzorakNode) or
isinstance(parent, s.UlohaZadaniNode)) isinstance(parent, UlohaZadaniNode))
tema = isinstance(parent, s.TemaVCisleNode) tema = isinstance(parent, TemaVCisleNode)
if (rocnik or cislo or uloha or tema) or parent==None: if (rocnik or cislo or uloha or tema) or parent==None:
break break
@ -158,7 +159,7 @@ class TNLData(object):
class TreeNodeView(generic.DetailView): class TreeNodeView(generic.DetailView):
model = s.TreeNode model = TreeNode
template_name = 'treenode/treenode.html' template_name = 'treenode/treenode.html'
def get_context_data(self,**kwargs): def get_context_data(self,**kwargs):
@ -168,7 +169,7 @@ class TreeNodeView(generic.DetailView):
class TreeNodeJSONView(generic.DetailView): class TreeNodeJSONView(generic.DetailView):
model = s.TreeNode model = TreeNode
def get(self,request,*args, **kwargs): def get(self,request,*args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
@ -178,21 +179,21 @@ class TreeNodeJSONView(generic.DetailView):
class TreeNodePridatView(generic.View): class TreeNodePridatView(generic.View):
type_from_str = { type_from_str = {
'rocnikNode': m.RocnikNode, 'rocnikNode': RocnikNode,
'cisloNode': m.CisloNode, 'cisloNode': CisloNode,
'castNode': m.CastNode, 'castNode': CastNode,
'textNode': m.TextNode, 'textNode': TextNode,
'temaVCisleNode': m.TemaVCisleNode, 'temaVCisleNode': TemaVCisleNode,
'reseniNode': m.ReseniNode, 'reseniNode': ReseniNode,
'ulohaZadaniNode': m.UlohaZadaniNode, 'ulohaZadaniNode': UlohaZadaniNode,
'ulohaVzorakNode': m.UlohaVzorakNode, 'ulohaVzorakNode': UlohaVzorakNode,
'pohadkaNode': m.PohadkaNode, 'pohadkaNode': PohadkaNode,
'orgText': m.OrgTextNode, 'orgText': OrgTextNode,
} }
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
######## FIXME: ROZEPSANE, NEFUNGUJE, DOPSAT !!!!!! ########### ######## 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'] kam = self.kwargs['kam']
co = self.kwargs['co'] co = self.kwargs['co']
typ = self.type_from_str[co] typ = self.type_from_str[co]
@ -202,19 +203,19 @@ class TreeNodePridatView(generic.View):
if kam not in ('pred','syn','za'): if kam not in ('pred','syn','za'):
raise ValidationError('Přidat lze pouze před nebo za node nebo jako syna') raise ValidationError('Přidat lze pouze před nebo za node nebo jako syna')
if co == m.TextNode: if co == TextNode:
new_obj = m.Text() new_obj = Text()
new_obj.save() new_obj.save()
elif co == m.CastNode: elif co == CastNode:
new_obj = m.CastNode() new_obj = CastNode()
new_obj.nadpis = request.POST.get('pridat-castNode-{}-{}'.format(node.id,kam)) new_obj.nadpis = request.POST.get('pridat-castNode-{}-{}'.format(node.id,kam))
new_obj.save() new_obj.save()
elif co == m.ReseniNode: elif co == ReseniNode:
new_obj = m new_obj = m
pass pass
elif co == m.UlohaZadaniNode: elif co == UlohaZadaniNode:
pass pass
elif co == m.UlohaReseniNode: elif co == UlohaReseniNode:
pass pass
else: else:
new_obj = None new_obj = None
@ -225,15 +226,15 @@ class TreeNodePridatView(generic.View):
if kam == 'syn': if kam == 'syn':
if typ == m.TextNode: if typ == TextNode:
text_obj = m.Text() text_obj = Text()
text_obj.save() text_obj.save()
node = treelib.create_child(node, typ, text=text_obj) node = treelib.create_child(node, typ, text=text_obj)
else: else:
node = treelib.create_child(node, typ) node = treelib.create_child(node, typ)
if kam == 'za': if kam == 'za':
if typ == m.TextNode: if typ == TextNode:
text_obj = m.Text() text_obj = Text()
text_obj.save() text_obj.save()
node = treelib.create_node_after(node, typ, text=text_obj) node = treelib.create_node_after(node, typ, text=text_obj)
else: else:
@ -244,7 +245,7 @@ class TreeNodePridatView(generic.View):
class TreeNodeSmazatView(generic.base.View): class TreeNodeSmazatView(generic.base.View):
def post(self, request, *args, **kwargs): 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: if node.first_child:
raise NotImplementedError('Mazání TreeNode se syny není zatím podporováno!') raise NotImplementedError('Mazání TreeNode se syny není zatím podporováno!')
treelib.disconnect_node(node) treelib.disconnect_node(node)
@ -254,7 +255,7 @@ class TreeNodeSmazatView(generic.base.View):
class TreeNodeOdvesitPrycView(generic.base.View): class TreeNodeOdvesitPrycView(generic.base.View):
def post(self, request, *args, **kwargs): 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) treelib.disconnect_node(node)
node.root = None node.root = None
node.save() node.save()
@ -263,7 +264,7 @@ class TreeNodeOdvesitPrycView(generic.base.View):
class TreeNodePodvesitView(generic.base.View): class TreeNodePodvesitView(generic.base.View):
def post(self, request, *args, **kwargs): 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'] kam = self.kwargs['kam']
if kam == 'pred': if kam == 'pred':
treelib.lower_node(node) treelib.lower_node(node)
@ -274,21 +275,21 @@ class TreeNodePodvesitView(generic.base.View):
class TreeNodeProhoditView(generic.base.View): class TreeNodeProhoditView(generic.base.View):
def post(self, request, *args, **kwargs): 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) treelib.swap_succ(node)
return redirect(request.headers.get('referer')) return redirect(request.headers.get('referer'))
#FIXME ve formulari predat puvodni url a vratit redirect na ni #FIXME ve formulari predat puvodni url a vratit redirect na ni
class SirotcinecView(generic.ListView): class SirotcinecView(generic.ListView):
model = s.TreeNode model = TreeNode
template_name = 'treenode/orphanage.html' template_name = 'treenode/orphanage.html'
def get_queryset(self): 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 # FIXME pouzit Django REST Framework
class TextWebView(generic.DetailView): class TextWebView(generic.DetailView):
model = s.Text model = Text
def get(self,request,*args, **kwargs): def get(self,request,*args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
@ -300,7 +301,7 @@ class VueTestView(generic.TemplateView):
class NahrajObrazekKTreeNoduView(LoginRequiredMixin, CreateView): class NahrajObrazekKTreeNoduView(LoginRequiredMixin, CreateView):
model = s.Obrazek model = Obrazek
form_class = f.NahrajObrazekKTreeNoduForm form_class = f.NahrajObrazekKTreeNoduForm
def get_initial(self): def get_initial(self):
@ -316,7 +317,7 @@ class NahrajObrazekKTreeNoduView(LoginRequiredMixin, CreateView):
print(form) print(form)
self.object = form.save(commit=False) self.object = form.save(commit=False)
print(self.object.na_web) 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() self.object.save()
return JsonResponse({"url":self.object.na_web.url}) return JsonResponse({"url":self.object.na_web.url})

22
treenode/viewsets.py

@ -3,7 +3,9 @@ from rest_framework import status
from rest_framework.response import Response from rest_framework.response import Response
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from rest_framework.permissions import BasePermission, AllowAny from rest_framework.permissions import BasePermission, AllowAny
from seminar import models as m from seminar.models.treenode import *
from seminar.models.tvorba import Problem, Uloha
from seminar.models.odevzdavatko import Reseni, ReseniNode
import treenode.serializers as views import treenode.serializers as views
from treenode.permissions import AllowWrite from treenode.permissions import AllowWrite
@ -66,17 +68,17 @@ class ReadWriteSerializerMixin(object):
return self.create_serializer_class return self.create_serializer_class
class TextViewSet(PermissionMixin, viewsets.ModelViewSet): class TextViewSet(PermissionMixin, viewsets.ModelViewSet):
queryset = m.Text.objects.all() queryset = Text.objects.all()
serializer_class = views.TextSerializer serializer_class = views.TextSerializer
class TextNodeViewSet(PermissionMixin, ReadWriteSerializerMixin,viewsets.ModelViewSet): class TextNodeViewSet(PermissionMixin, ReadWriteSerializerMixin,viewsets.ModelViewSet):
queryset = m.TextNode.objects.all() queryset = TextNode.objects.all()
read_serializer_class = views.TextNodeSerializer read_serializer_class = views.TextNodeSerializer
write_serializer_class = views.TextNodeWriteSerializer write_serializer_class = views.TextNodeWriteSerializer
create_serializer_class = views.TextNodeCreateSerializer create_serializer_class = views.TextNodeCreateSerializer
class CastNodeViewSet(PermissionMixin, ReadWriteSerializerMixin,viewsets.ModelViewSet): class CastNodeViewSet(PermissionMixin, ReadWriteSerializerMixin,viewsets.ModelViewSet):
queryset = m.CastNode.objects.all() queryset = CastNode.objects.all()
read_serializer_class = views.CastNodeSerializer read_serializer_class = views.CastNodeSerializer
write_serializer_class = views.CastNodeSerializer write_serializer_class = views.CastNodeSerializer
create_serializer_class = views.CastNodeCreateSerializer create_serializer_class = views.CastNodeCreateSerializer
@ -95,7 +97,7 @@ class UlohaVzorakNodeViewSet(PermissionMixin, ReadWriteSerializerMixin, viewsets
create_serializer_class = views.UlohaVzorakNodeCreateSerializer create_serializer_class = views.UlohaVzorakNodeCreateSerializer
def get_queryset(self): def get_queryset(self):
queryset = m.UlohaVzorakNode.objects.all() queryset = UlohaVzorakNode.objects.all()
nazev = self.request.query_params.get('nazev',None) nazev = self.request.query_params.get('nazev',None)
if nazev is not None: if nazev is not None:
queryset = queryset.filter(nazev__contains=nazev) queryset = queryset.filter(nazev__contains=nazev)
@ -114,7 +116,7 @@ class ReseniViewSet(viewsets.ModelViewSet):
serializer_class = views.ReseniSerializer serializer_class = views.ReseniSerializer
def get_queryset(self): def get_queryset(self):
queryset = m.Reseni.objects.all() queryset = Reseni.objects.all()
#FIXME upravit nazvy dle skutecnych polozek reseni #FIXME upravit nazvy dle skutecnych polozek reseni
nazev = self.request.query_params.get('nazev',None) nazev = self.request.query_params.get('nazev',None)
if nazev is not None: if nazev is not None:
@ -128,7 +130,7 @@ class UlohaViewSet(viewsets.ModelViewSet):
serializer_class = views.UlohaSerializer serializer_class = views.UlohaSerializer
def get_queryset(self): def get_queryset(self):
queryset = m.Uloha.objects.all() queryset = Uloha.objects.all()
nazev = self.request.query_params.get('nazev',None) nazev = self.request.query_params.get('nazev',None)
if nazev is not None: if nazev is not None:
queryset = queryset.filter(nazev__contains=nazev) queryset = queryset.filter(nazev__contains=nazev)
@ -138,13 +140,13 @@ class UlohaViewSet(viewsets.ModelViewSet):
return queryset return queryset
class UlohaZadaniNodeViewSet(ReadWriteSerializerMixin, viewsets.ModelViewSet): class UlohaZadaniNodeViewSet(ReadWriteSerializerMixin, viewsets.ModelViewSet):
queryset = m.UlohaZadaniNode.objects.all() queryset = UlohaZadaniNode.objects.all()
read_serializer_class = views.UlohaZadaniNodeSerializer read_serializer_class = views.UlohaZadaniNodeSerializer
write_serializer_class = views.UlohaZadaniNodeWriteSerializer write_serializer_class = views.UlohaZadaniNodeWriteSerializer
create_serializer_class = views.UlohaZadaniNodeCreateSerializer create_serializer_class = views.UlohaZadaniNodeCreateSerializer
class ReseniNodeViewSet(ReadWriteSerializerMixin, viewsets.ModelViewSet): class ReseniNodeViewSet(ReadWriteSerializerMixin, viewsets.ModelViewSet):
queryset = m.ReseniNode.objects.all() queryset = ReseniNode.objects.all()
read_serializer_class = views.ReseniNodeSerializer read_serializer_class = views.ReseniNodeSerializer
write_serializer_class = views.ReseniNodeWriteSerializer write_serializer_class = views.ReseniNodeWriteSerializer
create_serializer_class = views.ReseniNodeCreateSerializer create_serializer_class = views.ReseniNodeCreateSerializer
@ -155,7 +157,7 @@ class ProblemViewSet(viewsets.ModelViewSet):
serializer_class = views.ProblemSerializer serializer_class = views.ProblemSerializer
def get_queryset(self): def get_queryset(self):
queryset = m.Problem.objects.all() queryset = Problem.objects.all()
ucel = self.request.query_params.get('ucel',None) ucel = self.request.query_params.get('ucel',None)
rocnik = self.request.query_params.get('rocnik',None) rocnik = self.request.query_params.get('rocnik',None)
tema = self.request.query_params.get('tema',None) tema = self.request.query_params.get('tema',None)

2
vyroci/views.py

@ -1,6 +1,6 @@
from django.views.generic import FormView, ListView from django.views.generic import FormView, ListView
from seminar.models import Osoba from seminar.models.personalni import Osoba
from seminar.views import formularOKView from seminar.views import formularOKView
from .forms import UcastnikVyrociForm from .forms import UcastnikVyrociForm
from .models import UcastnikVyroci from .models import UcastnikVyroci

77
vysledkovky/utils.py

@ -2,7 +2,10 @@ import abc
from functools import cached_property from functools import cached_property
from typing import Union, Iterable # TODO: s pythonem 3.10 přepsat na '|' from typing import Union, Iterable # TODO: s pythonem 3.10 přepsat na '|'
import seminar.models as m from seminar.models.tvorba import Rocnik, Cislo, Deadline, Problem, Clanek
from seminar.models.odevzdavatko import Hodnoceni
from seminar.models.personalni import Resitel
from seminar.models.soustredeni import Konfera
from django.db.models import Q, Sum from django.db.models import Q, Sum
from seminar.utils import resi_v_rocniku from seminar.utils import resi_v_rocniku
@ -18,11 +21,11 @@ class FixedIterator:
def body_resitelu( def body_resitelu(
za: Union[m.Cislo, m.Rocnik, None] = None, za: Union[Cislo, Rocnik, None] = None,
do: m.Deadline = None, do: Deadline = None,
od: m.Deadline = None, od: Deadline = None,
jen_verejne: bool = True, 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 null=0 # Výchozí hodnota, pokud pro daného řešitele nejsou body
) -> dict[int, int]: ) -> dict[int, int]:
filtr = Q() filtr = Q()
@ -31,9 +34,9 @@ def body_resitelu(
filtr &= Q(reseni__hodnoceni__deadline_body__verejna_vysledkovka=True) filtr &= Q(reseni__hodnoceni__deadline_body__verejna_vysledkovka=True)
# Zjistíme, typ objektu v parametru "za" # 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) 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) filtr &= Q(reseni__hodnoceni__deadline_body__cislo=za)
if do: if do:
@ -42,7 +45,7 @@ def body_resitelu(
if od: if od:
filtr &= Q(reseni__hodnoceni__deadline_body__deadline__gte=od.deadline) filtr &= Q(reseni__hodnoceni__deadline_body__deadline__gte=od.deadline)
resiteleQuery = m.Resitel.objects.all() resiteleQuery = Resitel.objects.all()
if resitele is not None: if resitele is not None:
resitele_id = [r.id for r in resitele] resitele_id = [r.id for r in resitele]
@ -63,12 +66,12 @@ def body_resitelu(
class Vysledkovka(abc.ABC): class Vysledkovka(abc.ABC):
jen_verejne: bool jen_verejne: bool
rocnik: m.Rocnik rocnik: Rocnik
do_deadlinu: m.Deadline do_deadlinu: Deadline
@property @property
@abc.abstractmethod @abc.abstractmethod
def aktivni_resitele(self) -> list[m.Resitel]: def aktivni_resitele(self) -> list[Resitel]:
... ...
@cached_property @cached_property
@ -143,20 +146,20 @@ class Vysledkovka(abc.ABC):
class VysledkovkaRocniku(Vysledkovka): 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.rocnik = rocnik
self.jen_verejne = jen_verejne self.jen_verejne = jen_verejne
deadliny = m.Deadline.objects.filter(cislo__rocnik=rocnik) deadliny = Deadline.objects.filter(cislo__rocnik=rocnik)
if jen_verejne: if jen_verejne:
deadliny = deadliny.filter(verejna_vysledkovka=True) deadliny = deadliny.filter(verejna_vysledkovka=True)
self.do_deadlinu = deadliny.order_by("deadline").last() self.do_deadlinu = deadliny.order_by("deadline").last()
@cached_property @cached_property
def aktivni_resitele(self) -> list[m.Resitel]: def aktivni_resitele(self) -> list[Resitel]:
return list(resi_v_rocniku(self.rocnik)) return list(resi_v_rocniku(self.rocnik))
@cached_property @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. """ """ Vrátí všechna čísla daného ročníku. """
if self.jen_verejne: if self.jen_verejne:
return self.rocnik.verejne_vysledkovky_cisla() return self.rocnik.verejne_vysledkovky_cisla()
@ -164,7 +167,7 @@ class VysledkovkaRocniku(Vysledkovka):
return self.rocnik.cisla.all().order_by('poradi') return self.rocnik.cisla.all().order_by('poradi')
@cached_property @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! # TODO: Body jsou decimal!
body_cisla_slovnik = dict() body_cisla_slovnik = dict()
for cislo in self.cisla_rocniku: for cislo in self.cisla_rocniku:
@ -197,7 +200,7 @@ class VysledkovkaRocniku(Vysledkovka):
radky_vysledkovky = [] radky_vysledkovky = []
setrizeni_resitele_dict = dict() setrizeni_resitele_dict = dict()
for r in m.Resitel.objects.filter( for r in Resitel.objects.filter(
id__in=self.setrizeni_resitele_id id__in=self.setrizeni_resitele_id
).select_related('osoba'): ).select_related('osoba'):
setrizeni_resitele_dict[r.id] = r setrizeni_resitele_dict[r.id] = r
@ -227,31 +230,31 @@ class VysledkovkaRocniku(Vysledkovka):
class VysledkovkaCisla(Vysledkovka): class VysledkovkaCisla(Vysledkovka):
def __init__( def __init__(
self, self,
cislo: m.Cislo, cislo: Cislo,
jen_verejne: bool = True, jen_verejne: bool = True,
do_deadlinu: m.Deadline = None do_deadlinu: Deadline = None
): ):
self.cislo = cislo self.cislo = cislo
self.rocnik = cislo.rocnik self.rocnik = cislo.rocnik
self.jen_verejne = jen_verejne self.jen_verejne = jen_verejne
if do_deadlinu is None: 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 self.do_deadlinu = do_deadlinu
@cached_property @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 # TODO možná chytřeji vybírat aktivní řešitele
return list(resi_v_rocniku(self.rocnik)) return list(resi_v_rocniku(self.rocnik))
@cached_property @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. """ """ Vrátí seznam všech problémů s body v daném čísle. """
return m.Problem.objects.filter( return Problem.objects.filter(
hodnoceni__in=m.Hodnoceni.objects.filter(deadline_body__cislo=self.cislo) hodnoceni__in=Hodnoceni.objects.filter(deadline_body__cislo=self.cislo)
).distinct().non_polymorphic().select_related('nadproblem').select_related('nadproblem__nadproblem') ).distinct().non_polymorphic().select_related('nadproblem').select_related('nadproblem__nadproblem')
@cached_property @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. """ """ Vrátí seznam všech problémů, které již nemají nadproblém. """
# hlavní problémy čísla # hlavní problémy čísla
# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém) # (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. # Není cached, protože si myslím, že queryset lze použít ve for jen jednou.
@property @property
def hodnoceni_do_cisla(self): 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: if self.jen_verejne:
hodnoceni = hodnoceni.filter(deadline_body__verejna_vysledkovka=True) hodnoceni = hodnoceni.filter(deadline_body__verejna_vysledkovka=True)
return hodnoceni.filter( return hodnoceni.filter(
@ -347,7 +350,7 @@ class VysledkovkaCisla(Vysledkovka):
return self.sectene_body[2] return self.sectene_body[2]
@cached_property @cached_property
def temata_a_spol(self) -> list[m.Problem]: def temata_a_spol(self) -> list[Problem]:
if self.rocnik.rocnik < ROCNIK_ZRUSENI_TEMAT: if self.rocnik.rocnik < ROCNIK_ZRUSENI_TEMAT:
return self.hlavni_problemy return self.hlavni_problemy
else: else:
@ -358,7 +361,7 @@ class VysledkovkaCisla(Vysledkovka):
return len(self.hlavni_problemy) - len(self.temata_a_spol) > 0 return len(self.hlavni_problemy) - len(self.temata_a_spol) > 0
@cached_property @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} podproblemy = {hp.id: [] for hp in self.temata_a_spol}
temata_a_spol = set(self.temata_a_spol) temata_a_spol = set(self.temata_a_spol)
podproblemy[-1] = [] podproblemy[-1] = []
@ -381,7 +384,7 @@ class VysledkovkaCisla(Vysledkovka):
return podproblemy return podproblemy
@cached_property @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]] return [self.podproblemy[it.id] for it in self.temata_a_spol] + [self.podproblemy[-1]]
@cached_property @cached_property
@ -411,7 +414,7 @@ class VysledkovkaCisla(Vysledkovka):
radky_vysledkovky = [] radky_vysledkovky = []
setrizeni_resitele_slovnik = {} 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: for r in setrizeni_resitele:
setrizeni_resitele_slovnik[r.id] = r setrizeni_resitele_slovnik[r.id] = r
@ -462,29 +465,29 @@ class VysledkovkaCisla(Vysledkovka):
@staticmethod @staticmethod
def ne_clanek_ne_konfera(problem): def ne_clanek_ne_konfera(problem):
inst = problem.get_real_instance() 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): class VysledkovkaDoTeXu(VysledkovkaCisla):
def __init__( def __init__(
self, self,
nejake_cislo: m.Cislo, nejake_cislo: Cislo,
od_vyjma: m.Deadline, od_vyjma: Deadline,
do_vcetne: m.Deadline do_vcetne: Deadline
): ):
super().__init__(nejake_cislo, False, do_vcetne) super().__init__(nejake_cislo, False, do_vcetne)
self.od_deadlinu = od_vyjma self.od_deadlinu = od_vyjma
@cached_property @cached_property
def problemy(self) -> list[m.Problem]: def problemy(self) -> list[Problem]:
return m.Problem.objects.filter(hodnoceni__in=m.Hodnoceni.objects.filter( return Problem.objects.filter(hodnoceni__in=Hodnoceni.objects.filter(
deadline_body__deadline__gt=self.od_deadlinu.deadline, deadline_body__deadline__gt=self.od_deadlinu.deadline,
deadline_body__deadline__lte=self.do_deadlinu.deadline, deadline_body__deadline__lte=self.do_deadlinu.deadline,
)).distinct().non_polymorphic().select_related('nadproblem').select_related('nadproblem__nadproblem') )).distinct().non_polymorphic().select_related('nadproblem').select_related('nadproblem__nadproblem')
@property @property
def hodnoceni_do_cisla(self): def hodnoceni_do_cisla(self):
hodnoceni = m.Hodnoceni.objects.prefetch_related( hodnoceni = Hodnoceni.objects.prefetch_related(
'problem', 'reseni', 'reseni__resitele') 'problem', 'reseni', 'reseni__resitele')
if self.jen_verejne: if self.jen_verejne:
hodnoceni = hodnoceni.filter(deadline_body__verejna_vysledkovka=True) hodnoceni = hodnoceni.filter(deadline_body__verejna_vysledkovka=True)

Loading…
Cancel
Save