Browse Source

Merge remote-tracking branch 'gitea/podezrele-seminare' into bez_treenodu_i_bez_podezrelych_seminaru

Zatím tak, jak se namergeovalo, změny k funkčnosti v dalších commitech
(ať neděláme zbytečně magické merge)
pull/65/head
Pavel "LEdoian" Turinsky 2 months ago
parent
commit
8283b530e9
  1. 6
      aesop/urls.py
  2. 7
      aesop/views.py
  3. 4
      api/tests/test_skola_autocomplete.py
  4. 2
      api/urls.py
  5. 18
      api/views/autocomplete.py
  6. 2
      api/views/exports.py
  7. 34
      data/sitetree.json
  8. 2
      galerie/views.py
  9. 2
      make/push_compiled_vue_to_test
  10. 4
      mamweb/admin.py
  11. 4
      mamweb/settings_common.py
  12. 2
      novinky/templates/novinky/novinky.html
  13. 2
      odevzdavatko/admin.py
  14. 25
      odevzdavatko/forms.py
  15. 16
      odevzdavatko/models.py
  16. 2
      odevzdavatko/templates/odevzdavatko/nahraj_reseni.html
  17. 2
      odevzdavatko/templates/odevzdavatko/nahraj_reseni_nadproblem.html
  18. 2
      odevzdavatko/templates/odevzdavatko/vloz_reseni.html
  19. 4
      odevzdavatko/templatetags/barvy_reseni.py
  20. 4
      odevzdavatko/templatetags/jmena.py
  21. 8
      odevzdavatko/urls.py
  22. 94
      odevzdavatko/views.py
  23. 2
      personalni/admin.py
  24. 6
      personalni/forms.py
  25. 2
      personalni/templates/personalni/profil/orgorozcestnik.html
  26. 6
      personalni/templates/personalni/profil/resitel.html
  27. 2
      personalni/templates/personalni/udaje/edit.html
  28. 2
      personalni/templates/personalni/udaje/prihlaska.html
  29. 2
      personalni/tests.py
  30. 6
      personalni/urls.py
  31. 6
      personalni/utils.py
  32. 46
      personalni/views.py
  33. 2
      prednasky/admin.py
  34. 3
      prednasky/views.py
  35. 10
      soustredeni/models.py
  36. 2
      soustredeni/templates/soustredeni/seznam_soustredeni.html
  37. 4
      soustredeni/testutils.py
  38. 6
      soustredeni/urls.py
  39. 73
      treenode/admin.py
  40. 4
      treenode/forms.py
  41. 1
      treenode/models.py
  42. 1
      treenode/models/__init__.py
  43. 1
      treenode/models/pomocne.py
  44. 132
      treenode/serializers.py
  45. 12
      treenode/templates/treenode/treenode_recursive.html
  46. 34
      treenode/templatetags.py
  47. 12
      treenode/tests.py
  48. 6
      treenode/treelib.py
  49. 10
      treenode/urls.py
  50. 82
      treenode/views.py
  51. 25
      treenode/viewsets.py
  52. 6
      tvorba/models.py
  53. 12
      tvorba/templates/tvorba/archiv/cislo.html
  54. 2
      tvorba/templates/tvorba/archiv/problem_clanek.html
  55. 2
      tvorba/templates/tvorba/archiv/problem_tema.html
  56. 2
      tvorba/templates/tvorba/archiv/problem_uloha.html
  57. 2
      tvorba/templates/tvorba/archiv/problem_uloha_tema.html
  58. 2
      tvorba/templates/tvorba/archiv/rocnik.html
  59. 2
      tvorba/templates/tvorba/clanky/organizatorske_clanky.html
  60. 2
      tvorba/templates/tvorba/zadani/AktualniVysledkovka.html
  61. 22
      tvorba/templatetags/deadliny.py
  62. 46
      tvorba/testutils.py
  63. 36
      tvorba/urls.py
  64. 2
      tvorba/utils.py
  65. 59
      tvorba/views/__init__.py
  66. 14
      tvorba/views/docasne.py
  67. 2
      various/autentizace/templates/autentizace/login.html
  68. 1
      various/autentizace/views.py
  69. 4
      various/log_filters.py
  70. 2
      various/management/commands/generate_thumbnails.py
  71. 4
      various/management/commands/pregeneruj_zmrazene_vysledkovky.py
  72. 4
      various/management/commands/testdata.py
  73. 2
      various/templates/various/pracuje_se.html
  74. 4
      various/testutils.py
  75. 30
      various/views/final.py
  76. 2
      various/views/pomocne.py
  77. 4
      vue_frontend/vue.config.js
  78. 2
      vyroci/views.py
  79. 77
      vysledkovky/utils.py

6
aesop/urls.py

@ -5,16 +5,16 @@ urlpatterns = [
path(
'aesop-export/mam-rocnik-<int:prvni_rok>.csv',
views.ExportRocnikView.as_view(),
name='seminar_export_rocnik'
name='aesop_export_rocnik'
),
path(
'aesop-export/mam-sous-<str:datum_zacatku>.csv',
views.ExportSousView.as_view(),
name='seminar_export_sous'
name='aesop_export_sous'
),
path(
'aesop-export/index.csv',
views.ExportIndexView.as_view(),
name='seminar_export_index'
name='aesop_export_index'
),
]

7
aesop/views.py

@ -6,7 +6,8 @@ from django.views import generic
from django.utils.encoding import force_str
from .utils import default_ovvpfile
from seminar.models import Rocnik, Soustredeni
from soustredeni.models import Soustredeni
from tvorba.models import Rocnik
from vysledkovky import utils
from tvorba.utils import aktivniResitele
@ -14,10 +15,10 @@ class ExportIndexView(generic.View):
def get(self, request):
ls = []
for r in Rocnik.objects.filter(exportovat = True):
url = reverse('seminar_export_rocnik', kwargs={'prvni_rok': r.prvni_rok})
url = reverse('aesop_export_rocnik', kwargs={'prvni_rok': r.prvni_rok})
ls.append(url.split('/')[-1])
for s in Soustredeni.objects.filter(exportovat = True):
url = reverse('seminar_export_sous', kwargs={'datum_zacatku': s.datum_zacatku.isoformat()})
url = reverse('aesop_export_sous', kwargs={'datum_zacatku': s.datum_zacatku.isoformat()})
ls.append(url.split('/')[-1])
return HttpResponse('\n'.join(ls) + '\n', content_type='text/plain; charset=utf-8')

4
api/tests/test_skola_autocomplete.py

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

2
api/urls.py

@ -17,5 +17,5 @@ urlpatterns = [
# Ceka na autocomplete v3
# path('autocomplete/organizatori/',
# org_member_required(views.OrganizatorAutocomplete.as_view()),
# name='seminar_autocomplete_organizator')
# name='autocomplete_organizator')
]

18
api/views/autocomplete.py

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

2
api/views/exports.py

@ -1,4 +1,4 @@
import seminar.models as m
import personalni.models as m
from django.core import serializers as ser
from django.http import HttpResponse
def exportSkolView(request):

34
data/sitetree.json

@ -73,7 +73,7 @@
"sort_order": 3,
"title": "Aktuální<br/> ročník",
"tree": 1,
"url": "seminar_aktualni_zadani",
"url": "tvorba_aktualni_zadani",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -121,7 +121,7 @@
"sort_order": 5,
"title": "Archiv",
"tree": 1,
"url": "seminar_archiv_rocniky",
"url": "tvorba_archiv_rocniky",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -289,7 +289,7 @@
"sort_order": 43,
"title": "Výsledková listina",
"tree": 1,
"url": "seminar_aktualni_vysledky",
"url": "tvorba_aktualni_vysledky",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -361,7 +361,7 @@
"sort_order": 20,
"title": "Proběhlo",
"tree": 1,
"url": "seminar_seznam_soustredeni",
"url": "soustredeni_probehlo",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -409,7 +409,7 @@
"sort_order": 23,
"title": "Osobní údaje",
"tree": 1,
"url": "seminar_resitel_edit",
"url": "personalni_resitel_edit",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -439,7 +439,7 @@
"sort_order": 36,
"title": "Nahrát řešení",
"tree": 1,
"url": "seminar_nahraj_reseni",
"url": "odevzdavatko_nahraj_reseni",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -463,7 +463,7 @@
"sort_order": 35,
"title": "Témata",
"tree": 1,
"url": "seminar_archiv_temata",
"url": "tvorba_archiv_temata",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -589,7 +589,7 @@
"sort_order": 15,
"title": "Aktuální číslo",
"tree": 1,
"url": "seminar_aktualni_zadani",
"url": "tvorba_aktualni_zadani",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -613,7 +613,7 @@
"sort_order": 24,
"title": "Čísla",
"tree": 1,
"url": "seminar_archiv_rocniky",
"url": "tvorba_archiv_rocniky",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -721,7 +721,7 @@
"sort_order": 36,
"title": "Vložit řešení",
"tree": 1,
"url": "seminar_vloz_reseni",
"url": "odevzdavatko_vloz_reseni",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -804,7 +804,7 @@
"sort_order": 37,
"title": "Moje řešení",
"tree": 1,
"url": "seminar_resitel_odevzdana_reseni",
"url": "odevzdavatko_resitel_odevzdana_reseni",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -828,7 +828,7 @@
"sort_order": 33,
"title": "Aktuální ročník",
"tree": 1,
"url": "seminar_aktualni_rocnik",
"url": "tvorba_aktualni_rocnik",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -900,7 +900,7 @@
"sort_order": 46,
"title": "Ročník {{rocnik.rocnik}}",
"tree": 1,
"url": "seminar_rocnik rocnik.rocnik",
"url": "tvorba_rocnik rocnik.rocnik",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -924,7 +924,7 @@
"sort_order": 47,
"title": "Číslo {{ cislo.rocnik.rocnik }}.{{ cislo.poradi }}",
"tree": 1,
"url": "seminar_cislo cislo.rocnik.rocnik cislo.poradi",
"url": "tvorba_cislo cislo.rocnik.rocnik cislo.poradi",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -1061,7 +1061,7 @@
"sort_order": 52,
"title": "Nahrát řešení k nadproblému {{nadproblem_id}}",
"tree": 1,
"url": "seminar_nahraj_reseni nadproblem_id",
"url": "odevzdavatko_nahraj_reseni nadproblem_id",
"urlaspattern": true
},
"model": "sitetree.treeitem",
@ -1109,10 +1109,10 @@
"sort_order": 54,
"title": "Export do abstraktů sousu {{ soustredeni.id }}",
"tree": 1,
"url": "seminar_soustredeni_abstrakty soustredeni.id",
"url": "soustredeni_abstrakty soustredeni.id",
"urlaspattern": true
},
"model": "sitetree.treeitem",
"pk": 54
}
]
]

2
galerie/views.py

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

2
make/push_compiled_vue_to_test

@ -4,7 +4,7 @@ set -exuo pipefail
. make/lib.sh
scp vue_frontend/webpack-stats.json "$GIMLI_LOGIN:$TESTWEB/vue_frontend/"
rsync -ave ssh seminar/static/seminar/vue "$GIMLI_LOGIN:$TESTWEB/seminar/static/seminar/"
rsync -ave ssh treenode/static/treenode/vue "$GIMLI_LOGIN:$TESTWEB/treenode/static/treenode/"
ssh "$GIMLI_LOGIN" "
set -euxo pipefail
cd $TESTWEB

4
mamweb/admin.py

@ -43,7 +43,7 @@ def get_app_list(self, request, app_label=None):
app_dict = self._build_app_dict(request, label=app_label)
aplikace_nahore = [
'seminar',
'tvorba',
'personalni',
'novinky',
'korektury',
@ -57,7 +57,7 @@ def get_app_list(self, request, app_label=None):
# Sort the models alphabetically within each app.
for app in app_list:
app['models'].sort(key=lambda x: locale.strxfrm('žž' + x['name'].lower()) if (x['name'].endswith("(Node)")) else locale.strxfrm(x['name'].lower()))
app['models'].sort(key=lambda x: locale.strxfrm(x['name'].lower()))
return app_list

4
mamweb/settings_common.py

@ -279,11 +279,11 @@ LOGGING = {
'filters': ['Http404AsInfo'],
},
'seminar.prihlaska.form':{
'personalni.prihlaska.form':{
'handlers': ['console','registration_logfile'],
'level': 'INFO'
},
'seminar.prihlaska.problem':{
'personalni.prihlaska.problem':{
'handlers': ['console','mail_registration','registration_error_log'],
'level': 'INFO'
},

2
novinky/templates/novinky/novinky.html

@ -4,7 +4,7 @@
{% if not novinka.zverejneno and user.je_org %}
<div class="mam-org-only">
<ul>
<li><a href="/admin/seminar/novinky/{{novinka.pk}}">Upravit novinku</a>
<li><a href="/admin/novinky/novinky/{{novinka.pk}}">Upravit novinku</a>
</ul>
{% endif %}
{% if novinka.zverejneno or user.je_org %}

2
odevzdavatko/admin.py

@ -1,6 +1,6 @@
from django.contrib import admin
from django_reverse_admin import ReverseModelAdmin
import seminar.models as m
import odevzdavatko.models as m
class PrilohaReseniInline(admin.TabularInline):

25
odevzdavatko/forms.py

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

16
odevzdavatko/models.py

@ -9,7 +9,7 @@ from django.urls import reverse_lazy
from django.utils import timezone
from django.conf import settings
import tvorba.models as am
from tvorba.models import Problem, Deadline, Cislo, Uloha, aux_generate_filename
from seminar.models import base as bm
from odevzdavatko.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet
@ -29,7 +29,7 @@ class Reseni(bm.SeminarModelBase):
id = models.AutoField(primary_key = True)
# Ke každé dvojici řešní a problém existuje nanejvýš jedno hodnocení, doplnění vazby.
problem = models.ManyToManyField(am.Problem, verbose_name='problém', help_text='Problém',
problem = models.ManyToManyField(Problem, verbose_name='problém', help_text='Problém',
through='Hodnoceni')
resitele = models.ManyToManyField(Resitel, verbose_name='autoři řešení',
@ -79,7 +79,7 @@ class Reseni(bm.SeminarModelBase):
# NOTE: Potenciální DB HOG (bez select_related)
def deadline_reseni(self):
return am.Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first()
return Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first()
## Pravdepodobne uz nebude potreba:
# def save(self, *args, **kwargs):
@ -101,16 +101,16 @@ class Hodnoceni(bm.SeminarModelBase):
body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body',
blank=True, null=True)
cislo_body = models.ForeignKey(am.Cislo, verbose_name='číslo pro body',
cislo_body = models.ForeignKey(Cislo, verbose_name='číslo pro body',
related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT)
# V ročníku < 26 nastaveno na deadline vygenerovaný pro původní cislo_body
deadline_body = models.ForeignKey(am.Deadline, verbose_name='deadline pro body',
deadline_body = models.ForeignKey(Deadline, verbose_name='deadline pro body',
related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT)
reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE)
problem = models.ForeignKey(am.Problem, verbose_name='problém',
problem = models.ForeignKey(Problem, verbose_name='problém',
related_name='hodnoceni', on_delete=models.PROTECT)
feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)')
@ -166,7 +166,7 @@ class Hodnoceni(bm.SeminarModelBase):
@property
def body_neprepocitane_max(self):
if not isinstance(self.problem.get_real_instance(), am.Uloha):
if not isinstance(self.problem.get_real_instance(), Uloha):
return None
return self.problem.uloha.max_body
@ -176,7 +176,7 @@ class Hodnoceni(bm.SeminarModelBase):
def generate_filename(self, filename):
return os.path.join(
settings.SEMINAR_RESENI_DIR,
am.aux_generate_filename(self, filename)
aux_generate_filename(self, filename)
)

2
odevzdavatko/templates/odevzdavatko/nahraj_reseni.html

@ -11,7 +11,7 @@
{% endblock %}
</h1>
<form enctype="multipart/form-data" action="{% url 'seminar_nahraj_reseni' nadproblem_id %}" method="post" onsubmit="return zkontroluj_prilohy();">
<form enctype="multipart/form-data" action="{% url 'odevzdavatko_nahraj_reseni' nadproblem_id %}" method="post" onsubmit="return zkontroluj_prilohy();">
{% csrf_token %}
<table class='form'>
<tr>

2
odevzdavatko/templates/odevzdavatko/nahraj_reseni_nadproblem.html

@ -12,7 +12,7 @@
<ul>
{% for problem in object_list %}
<li><a href="{% url 'seminar_nahraj_reseni' problem.id %}">{{ problem }}</a></li>
<li><a href="{% url 'odevzdavatko_nahraj_reseni' problem.id %}">{{ problem }}</a></li>
{% empty %}
<li>Nelze nic odevzdávat.</li>
{% endfor %}

2
odevzdavatko/templates/odevzdavatko/vloz_reseni.html

@ -12,7 +12,7 @@
Vložit řešení
{% endblock %}
</h1>
<form enctype="multipart/form-data" action="{% url 'seminar_vloz_reseni' %}" method="post" onsubmit="return zkontroluj_prilohy();">
<form enctype="multipart/form-data" action="{% url 'odevzdavatko_vloz_reseni' %}" method="post" onsubmit="return zkontroluj_prilohy();">
{% csrf_token %}
{{form.as_p}}

4
odevzdavatko/templatetags/barvy_reseni.py

@ -2,11 +2,11 @@ from django import template
register = template.Library()
from functools import cache
import seminar.models as m
from odevzdavatko.models import Reseni
@register.filter
@cache
def barva_reseni(r: m.Reseni):
def barva_reseni(r: Reseni):
"""Vrátí nějakou barvu pro daný problém, ve tvaru '#RRGGBB'
Efektivně hešujeme do barev."""

4
odevzdavatko/templatetags/jmena.py

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

8
odevzdavatko/urls.py

@ -5,10 +5,10 @@ from various.views.generic import viewMethodSwitch
from . import views
urlpatterns = [
path('org/add_solution', org_required(views.VlozReseniView.as_view()), name='seminar_vloz_reseni'),
path('resitel/nahraj_reseni', resitel_required(views.NahrajReseniRozcestnikTematekView.as_view()), name='seminar_nahraj_reseni'),
path('resitel/nahraj_reseni/<int:nadproblem_id>/', resitel_required(views.NahrajReseniView.as_view()), name='seminar_nahraj_reseni'),
path('resitel/odevzdana_reseni/', resitel_or_org_required(views.PrehledOdevzdanychReseni.as_view()), name='seminar_resitel_odevzdana_reseni'),
path('org/add_solution', org_required(views.VlozReseniView.as_view()), name='odevzdavatko_vloz_reseni'),
path('resitel/nahraj_reseni', resitel_required(views.NahrajReseniRozcestnikTematekView.as_view()), name='odevzdavatko_nahraj_reseni'),
path('resitel/nahraj_reseni/<int:nadproblem_id>/', resitel_required(views.NahrajReseniView.as_view()), name='odevzdavatko_nahraj_reseni'),
path('resitel/odevzdana_reseni/', resitel_or_org_required(views.PrehledOdevzdanychReseni.as_view()), name='odevzdavatko_resitel_odevzdana_reseni'),
path('org/reseni/', org_required(views.TabulkaOdevzdanychReseniView.as_view()), name='odevzdavatko_tabulka'),
path('org/reseni/rocnik/<int:rocnik>/', org_required(views.TabulkaOdevzdanychReseniView.as_view()), name='odevzdavatko_tabulka'),

94
odevzdavatko/views.py

@ -17,10 +17,14 @@ from decimal import Decimal
from itertools import groupby
import logging
import seminar.models as m
from . import forms as f
from .forms import OdevzdavatkoTabulkaFiltrForm as FiltrForm
from .models import Hodnoceni, Reseni
from personalni.models import Resitel, Osoba, Organizator
from tvorba.models import Problem, Deadline, Rocnik
from tvorba.utils import resi_v_rocniku
from various.models import Nastaveni
from various.views.pomocne import formularOKView
logger = logging.getLogger(__name__)
@ -40,20 +44,20 @@ logger = logging.getLogger(__name__)
class TabulkaOdevzdanychReseniView(ListView):
template_name = 'odevzdavatko/tabulka.html'
model = m.Hodnoceni
model = Hodnoceni
def inicializuj_osy_tabulky(self):
"""Vyrobí prvotní querysety pro sloupce a řádky, tj. seznam všech řešitelů a problémů"""
# FIXME: jméno metody není vypovídající...
# NOTE: Tenhle blok nemůže být přímo ve třídě, protože před vyrobením databáze neexistují ty objekty (?). TODO: Otestovat
# TODO: Prefetches, Select related, ...
self.resitele = m.Resitel.objects.all()
self.problemy = m.Problem.objects.all()
self.reseni = m.Reseni.objects.all()
self.resitele = Resitel.objects.all()
self.problemy = Problem.objects.all()
self.reseni = Reseni.objects.all()
self.aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci
self.aktualni_rocnik = Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci
if 'rocnik' in self.kwargs:
self.aktualni_rocnik = get_object_or_404(m.Rocnik, rocnik=self.kwargs['rocnik'])
self.aktualni_rocnik = get_object_or_404(Rocnik, rocnik=self.kwargs['rocnik'])
form = FiltrForm(self.request.GET, rocnik=self.aktualni_rocnik)
if form.is_valid():
@ -86,14 +90,14 @@ class TabulkaOdevzdanychReseniView(ListView):
self.resitele = self.resitele.filter(rok_maturity__gt=self.aktualni_rocnik.prvni_rok)
if problemy == FiltrForm.PROBLEMY_MOJE:
org = m.Organizator.objects.get(osoba__user=self.request.user)
org = Organizator.objects.get(osoba__user=self.request.user)
self.problemy = self.problemy.filter(
Q(autor=org)|Q(garant=org)|Q(opravovatele=org),
Q(stav=m.Problem.STAV_ZADANY)|Q(stav=m.Problem.STAV_VYRESENY),
Q(stav=Problem.STAV_ZADANY)|Q(stav=Problem.STAV_VYRESENY),
)
elif problemy == FiltrForm.PROBLEMY_LETOSNI:
self.problemy = self.problemy.filter(
Q(stav=m.Problem.STAV_ZADANY)|Q(stav=m.Problem.STAV_VYRESENY),
Q(stav=Problem.STAV_ZADANY)|Q(stav=Problem.STAV_VYRESENY),
)
#self.problemy = list(filter(lambda problem: problem.rocnik() == self.aktualni_rocnik, self.problemy)) # DB HOG? # FIXME: některé problémy nemají ročník....
# NOTE: Protože řešení odkazuje přímo na Problém a QuerySet na Hodnocení je nepolymorfní, musíme porovnávat taky s nepolymorfními Problémy.
@ -121,8 +125,8 @@ class TabulkaOdevzdanychReseniView(ListView):
ctx = super().get_context_data(*args, **kwargs)
ctx['problemy'] = self.problemy
ctx['resitele'] = self.resitele
tabulka: dict[m.Problem, dict[m.Resitel, list[tuple[m.Reseni, m.Hodnoceni]]]] = dict()
soucty: dict[m.Problem, dict[m.Resitel, Decimal]] = dict()
tabulka: dict[Problem, dict[Resitel, list[tuple[Reseni, Hodnoceni]]]] = dict()
soucty: dict[Problem, dict[Resitel, Decimal]] = dict()
def pridej_reseni(resitel, hodnoceni):
problem = hodnoceni.problem
@ -143,11 +147,11 @@ class TabulkaOdevzdanychReseniView(ListView):
for resitel in hodnoceni.reseni.resitele.all():
pridej_reseni(resitel, hodnoceni)
hodnoty: list[list[tuple[Decimal,list[tuple[m.Reseni, m.Hodnoceni]]]]] = [] # Seznam řádků výsledné tabulky podle self.resitele, v každém řádku buňky v pořadí podle self.problemy + jejich součty, v každé buňce seznam řešení k danému řešiteli a problému.
resitele_do_tabulky: list[m.Resitel] = []
hodnoty: list[list[tuple[Decimal,list[tuple[Reseni, Hodnoceni]]]]] = [] # Seznam řádků výsledné tabulky podle self.resitele, v každém řádku buňky v pořadí podle self.problemy + jejich součty, v každé buňce seznam řešení k danému řešiteli a problému.
resitele_do_tabulky: list[Resitel] = []
for resitel in self.resitele:
dostal_body = False
resiteluv_radek: list[tuple[Decimal,list[tuple[m.Reseni, m.Hodnoceni]]]] = [] # podle pořadí v self.problemy
resiteluv_radek: list[tuple[Decimal,list[tuple[Reseni, Hodnoceni]]]] = [] # podle pořadí v self.problemy
for problem in self.problemy:
if problem in tabulka and resitel in tabulka[problem]:
resiteluv_radek.append((soucty[problem][resitel], tabulka[problem][resitel]))
@ -162,7 +166,7 @@ class TabulkaOdevzdanychReseniView(ListView):
# Pro použití hacku na automatické {{form.media}} v template:
ctx['form'] = ctx['filtr']
# Pro maximum v přesměrovátku ročníků
ctx['aktualni_rocnik'] = m.Nastaveni.get_solo().aktualni_rocnik
ctx['aktualni_rocnik'] = Nastaveni.get_solo().aktualni_rocnik
ctx['barvicky'] = self.barvicky
if 'rocnik' in self.kwargs:
ctx['rocnik'] = self.kwargs['rocnik']
@ -178,7 +182,7 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi
Asi bude zastaralý v okamžiku, kdy se tenhle komentář nasadí na produkci :-)
V případě, že takové řešení existuje jen jedno, tak na něj přesměruje."""
model = m.Reseni
model = Reseni
template_name = 'odevzdavatko/seznam.html'
def get_queryset(self):
@ -190,8 +194,8 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi
if problem_id is None:
raise ValueError("Nemám problém! (To je problém!)")
resitel = m.Resitel.objects.get(id=resitel_id)
problem = m.Problem.objects.get(id=problem_id)
resitel = Resitel.objects.get(id=resitel_id)
problem = Problem.objects.get(id=problem_id)
qs = qs.filter(
problem__in=[problem],
resitele__in=[resitel],
@ -221,13 +225,13 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi
## XXX: https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#avoid-anything-more-complex
class DetailReseniView(DetailView):
""" Náhled na řešení. Editace je v :py:class:`EditReseniView`. """
model = m.Reseni
model = Reseni
template_name = 'odevzdavatko/detail.html'
def aktualni_hodnoceni(self):
self.reseni = get_object_or_404(m.Reseni, id=self.kwargs['pk'])
self.reseni = get_object_or_404(Reseni, id=self.kwargs['pk'])
result = [] # Slovníky s klíči problem, body, deadline_body -- initial data pro f.OhodnoceniReseniFormSet
for hodn in m.Hodnoceni.objects.filter(reseni=self.reseni):
for hodn in Hodnoceni.objects.filter(reseni=self.reseni):
seznam_atributu = [
"problem",
"body",
@ -284,7 +288,7 @@ class EditReseniView(DetailReseniView):
def hodnoceniReseniView(request, pk, *args, **kwargs):
reseni = get_object_or_404(m.Reseni, pk=pk)
reseni = get_object_or_404(Reseni, pk=pk)
success_url = reverse('odevzdavatko_detail_reseni', kwargs={'pk': pk})
# FIXME: Použit initial i tady a nebastlit hodnocení tak nízkoúrovňově
@ -300,7 +304,7 @@ def hodnoceniReseniView(request, pk, *args, **kwargs):
poznamka_form.save()
# Smažeme všechna dosavadní hodnocení tohoto řešení
qs = m.Hodnoceni.objects.filter(reseni=reseni)
qs = Hodnoceni.objects.filter(reseni=reseni)
logger.info(f"Will delete {qs.count()} objects: {qs}")
qs.delete()
@ -311,7 +315,7 @@ def hodnoceniReseniView(request, pk, *args, **kwargs):
del(data_for_hodnoceni["body_celkem"])
del(data_for_hodnoceni["body_neprepocitane"])
del(data_for_hodnoceni["body_neprepocitane_celkem"])
hodnoceni = m.Hodnoceni(
hodnoceni = Hodnoceni(
reseni=reseni,
**form.cleaned_data,
)
@ -332,14 +336,14 @@ def hodnoceniReseniView(request, pk, *args, **kwargs):
class PrehledOdevzdanychReseni(ListView):
model = m.Hodnoceni
model = Hodnoceni
template_name = 'odevzdavatko/prehled_reseni.html'
def get_queryset(self):
if not self.request.user.is_authenticated:
raise RuntimeError("Uživatel měl být přihlášený!")
# get_or_none, aby neexistence řešitele (např. u orgů) neházela chybu
resitel = m.Resitel.objects.filter(osoba__user=self.request.user).first()
resitel = Resitel.objects.filter(osoba__user=self.request.user).first()
qs = super().get_queryset()
qs = qs.filter(reseni__resitele__in=[resitel])
# Setřídíme podle času doručení řešení, aby se netřídily podle okamžiku vyrobení Hodnocení
@ -360,13 +364,13 @@ class PrehledOdevzdanychReseni(ListView):
# Přehled všech řešení kvůli debugování
class SeznamReseniView(ListView):
model = m.Reseni
model = Reseni
template_name = 'odevzdavatko/seznam.html'
class SeznamAktualnichReseniView(SeznamReseniView):
def get_queryset(self):
qs = super().get_queryset()
akt_rocnik = m.Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci, asi...
akt_rocnik = Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci, asi...
resitele = resi_v_rocniku(akt_rocnik)
qs = qs.filter(resitele__in=resitele) # FIXME: Najde řešení i ze starých ročníků, která odevzdal alespoň jeden aktuální řešitel
return qs
@ -378,7 +382,7 @@ class VlozReseniView(LoginRequiredMixin, FormView):
def form_valid(self, form):
data = form.cleaned_data
nove_reseni = m.Reseni.objects.create(
nove_reseni = Reseni.objects.create(
cas_doruceni=data['cas_doruceni'],
forma=data['forma'],
poznamka=data['poznamka'],
@ -405,35 +409,35 @@ class VlozReseniView(LoginRequiredMixin, FormView):
class NahrajReseniRozcestnikTematekView(LoginRequiredMixin, ListView):
model = m.Problem
model = Problem
template_name = 'odevzdavatko/nahraj_reseni_nadproblem.html'
def get_queryset(self):
return super().get_queryset().filter(stav=m.Problem.STAV_ZADANY, nadproblem__isnull=True)
return super().get_queryset().filter(stav=Problem.STAV_ZADANY, nadproblem__isnull=True)
class NahrajReseniView(LoginRequiredMixin, CreateView):
model = m.Reseni
model = Reseni
template_name = 'odevzdavatko/nahraj_reseni.html'
form_class = f.NahrajReseniForm
nadproblem: m.Problem
nadproblem: Problem
def setup(self, request, *args, **kwargs):
super().setup(request, *args, **kwargs)
nadproblem_id = self.kwargs["nadproblem_id"]
self.nadproblem = get_object_or_404(m.Problem, id=nadproblem_id)
self.nadproblem = get_object_or_404(Problem, id=nadproblem_id)
def get(self, request, *args, **kwargs):
# Zaříznutí nezadaných problémů
if self.nadproblem.stav != m.Problem.STAV_ZADANY:
if self.nadproblem.stav != Problem.STAV_ZADANY:
raise PermissionDenied()
# Zaříznutí starých řešitelů:
# FIXME: Je to tady dost naprasené, mělo by to asi být jinde…
osoba = m.Osoba.objects.get(user=self.request.user)
osoba = Osoba.objects.get(user=self.request.user)
resitel = osoba.resitel
if resitel.rok_maturity <= m.Nastaveni.get_solo().aktualni_rocnik.prvni_rok:
if resitel.rok_maturity <= Nastaveni.get_solo().aktualni_rocnik.prvni_rok:
return render(request, 'universal.html', {
'title': 'Nelze odevzdat',
'error': 'Zdá se, že jsi již odmaturoval/a, a tedy nemůžeš odevzdat do našeho semináře řešení.',
@ -445,7 +449,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
nadproblem_id = self.nadproblem.id
return {
"nadproblem_id": nadproblem_id,
"problem": [] if self.nadproblem.podproblem.filter(stav=m.Problem.STAV_ZADANY).exists() else nadproblem_id
"problem": [] if self.nadproblem.podproblem.filter(stav=Problem.STAV_ZADANY).exists() else nadproblem_id
}
@ -457,7 +461,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
data['prilohy'] = f.ReseniSPrilohamiFormSet()
data["nadproblem_id"] = self.nadproblem.id
data["nadproblem"] = get_object_or_404(m.Problem, id=self.nadproblem.id)
data["nadproblem"] = get_object_or_404(Problem, id=self.nadproblem.id)
return data
# FIXME prepsat tak, aby form_valid se volalo jen tehdy, kdyz je form i formset validni
@ -469,17 +473,17 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
return super().form_invalid(form)
with transaction.atomic():
self.object = form.save()
self.object.resitele.add(m.Resitel.objects.get(osoba__user = self.request.user))
self.object.resitele.add(Resitel.objects.get(osoba__user = self.request.user))
self.object.resitele.add(*form.cleaned_data["resitele"])
self.object.cas_doruceni = timezone.now()
self.object.forma = m.Reseni.FORMA_UPLOAD
self.object.forma = Reseni.FORMA_UPLOAD
self.object.save()
prilohy.instance = self.object
prilohy.save()
for hodnoceni in self.object.hodnoceni_set.all():
hodnoceni.deadline_body = m.Deadline.objects.filter(deadline__gte=self.object.cas_doruceni).first()
hodnoceni.deadline_body = Deadline.objects.filter(deadline__gte=self.object.cas_doruceni).first()
hodnoceni.save()
# Pošleme mail opravovatelům a garantovi
@ -497,7 +501,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
# FIXME: Víc informativní obsah mailů, možná vč. příloh?
prijemci = map(lambda it: it.osoba.email, prijemci)
resitel = m.Osoba.objects.get(user = self.request.user)
resitel = Osoba.objects.get(user = self.request.user)
seznam = "problému " + str(problemy[0]) if len(problemy) == 1 else 'následujícím problémům:\n' + ', \n'.join(map(str, problemy))
seznam_do_subjectu = "problému " + str(problemy[0]) + ("" if len(problemy) == 1 else f" (a dalším { len(problemy) - 1 })")
@ -512,5 +516,5 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
return formularOKView(
self.request,
text='Řešení úspěšně odevzdáno',
dalsi_odkazy=[("Odevzdat další řešení", reverse("seminar_nahraj_reseni"))],
dalsi_odkazy=[("Odevzdat další řešení", reverse("odevzdavatko_nahraj_reseni"))],
)

2
personalni/admin.py

@ -2,7 +2,7 @@ from django.contrib import admin
from django.contrib.auth.models import Group
from django_reverse_admin import ReverseModelAdmin
from django.contrib.messages import WARNING, ERROR, SUCCESS
import seminar.models as m
import personalni.models as m
from datetime import datetime
@admin.action(description="Sjednoť telefony")

6
personalni/forms.py

@ -4,7 +4,7 @@ from django.contrib.auth.forms import PasswordResetForm
from django.core.exceptions import ObjectDoesNotExist
from django.contrib.auth.models import User
from seminar.models import Skola, Resitel, Osoba
from personalni.models import Skola, Resitel, Osoba
from datetime import date
import logging
@ -27,7 +27,7 @@ class TelInput(forms.TextInput):
class UdajeForm(forms.Form):
username = None
err_logger = logging.getLogger('seminar.prihlaska.problem')
err_logger = logging.getLogger('personalni.prihlaska.problem')
jmeno = forms.CharField(label='Jméno', max_length=256, required=True)
prezdivka_resitele = forms.CharField(label='Přezdívka (veřejná)', max_length=256, required=False)
@ -147,7 +147,7 @@ class PrihlaskaForm(PasswordResetForm, UdajeForm):
class ProfileEditForm(UdajeForm):
err_logger = logging.getLogger('seminar.edit.problem')
err_logger = logging.getLogger('personalni.edit.problem')
username = forms.CharField(
label='Přihlašovací jméno',
max_length=256,

2
personalni/templates/personalni/profil/orgorozcestnik.html

@ -78,7 +78,7 @@
<li>hlasování o přednáškách</li>
</ul>
</li>
<li><a href="{% url 'seminar_seznam_soustredeni' %}">proběhlá soustředění</a>
<li><a href="{% url 'soustredeni_probehlo' %}">proběhlá soustředění</a>
<ul>
<li>vytvoření galerie</li>
<li>stažení seznamu účastníků</li>

6
personalni/templates/personalni/profil/resitel.html

@ -10,9 +10,9 @@
</h1>
<a href="{% url 'logout' %}">Odhlásit se</a><br>
<a href="{% url 'seminar_resitel_edit' %}">Upravit údaje</a><br>
<a href="{% url 'seminar_nahraj_reseni' %}">Nahrát řešení</a><br>
<a href="{% url 'seminar_resitel_odevzdana_reseni' %}">Již odevzdaná řešení</a><br>
<a href="{% url 'personalni_resitel_edit' %}">Upravit údaje</a><br>
<a href="{% url 'odevzdavatko_nahraj_reseni' %}">Nahrát řešení</a><br>
<a href="{% url 'odevzdavatko_resitel_odevzdana_reseni' %}">Již odevzdaná řešení</a><br>
{% endblock %}

2
personalni/templates/personalni/udaje/edit.html

@ -11,7 +11,7 @@
<hr>
<p><a href="{% url 'reset_password' %}">Změnit heslo</a></p>
<form action="{% url 'seminar_resitel_edit' %}" method="post">
<form action="{% url 'personalni_resitel_edit' %}" method="post">
{% include "personalni/udaje/udaje.html"%}
<input type="submit" value="Změnit">
</form>

2
personalni/templates/personalni/udaje/prihlaska.html

@ -14,7 +14,7 @@
<p><b>Tučně</b> popsaná pole jsou povinná.</p>
<form action="{% url 'seminar_prihlaska' %}" method="post">
<form action="{% url 'personalni_prihlaska' %}" method="post">
{% include "personalni/udaje/udaje.html" %}
<h4>
GDPR

2
personalni/tests.py

@ -4,7 +4,7 @@ from django.contrib.auth.models import User, Group
from django.contrib.admin.sites import AdminSite
from personalni.admin import OsobaAdmin
# Tohle bude peklo, až jednou ty modely fakt rozstřelíme… Možná vyrobit various.all_models, které půjdou importovat jako m? :-)
import seminar.models as m
import personalni.models as m
import logging
logger = logging.getLogger(__name__)

6
personalni/urls.py

@ -7,15 +7,15 @@ urlpatterns = [
path(
'org/rozcestnik/',
org_required(views.OrgoRozcestnikView.as_view()),
name='seminar_org_rozcestnik'
name='personalni_org_rozcestnik'
),
path('prihlaska/', views.prihlaskaView, name='seminar_prihlaska'),
path('prihlaska/', views.prihlaskaView, name='personalni_prihlaska'),
path(
'resitel/osobni-udaje/',
login_required(views.resitelEditView),
name='seminar_resitel_edit'
name='personalni_resitel_edit'
),
# Obecný view na profil -- orgům dá rozcestník, řešitelům jejich stránku

6
personalni/utils.py

@ -1,4 +1,3 @@
import seminar.models as m
from various.utils import bez_diakritiky_translate
import re
@ -7,9 +6,10 @@ from django.contrib.auth.decorators import permission_required, user_passes_test
from django.contrib.auth.models import AnonymousUser
from django.db import transaction
import seminar.models as m
import soustredeni.models
from odevzdavatko.models import Reseni_Resitele
from .models import Osoba, Organizator, Skola, Resitel, Prijemce
@ -97,7 +97,7 @@ def merge_resitele(cilovy, zdrojovy):
# Přepojit všechny vazby ze zdrojového na cílového
print('Přepojuji vazby')
# Vazby: Škola (hotovo), Řešení_Řešitelé, Konfery_Účastníci, Soustředění_Účastníci, Osoba (vyřeší se později, nejde přepojit)
ct = m.Reseni_Resitele.objects.filter(resitele=zdrojovy).update(resitele=cilovy)
ct = Reseni_Resitele.objects.filter(resitele=zdrojovy).update(resitele=cilovy)
print(f' Přepojeno {ct} řešení')
ct = soustredeni.models.Konfery_Ucastnici.objects.filter(resitel=zdrojovy).update(resitel=cilovy)
print(f' Přepojeno {ct} konfer')

46
personalni/views.py

@ -16,8 +16,12 @@ from django.db import transaction
from django.http import HttpResponse
from django.utils import timezone
import seminar.models as s
import seminar.models as m
import personalni.models as m
from soustredeni.models import Soustredeni
from odevzdavatko.models import Hodnoceni
from tvorba.models import Clanek, Uloha, Tema
from various.models import Nastaveni
from .forms import PrihlaskaForm, ProfileEditForm, PoMaturiteProfileEditForm
from datetime import date
@ -91,22 +95,22 @@ class OrgoRozcestnikView(TemplateView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['posledni_soustredeni'] = s.Soustredeni.objects.order_by('-datum_konce').first()
nastaveni = s.Nastaveni.objects.first()
context['posledni_soustredeni'] = Soustredeni.objects.order_by('-datum_konce').first()
nastaveni = Nastaveni.objects.first()
aktualni_rocnik = nastaveni.aktualni_rocnik
context['posledni_cislo_url'] = nastaveni.aktualni_cislo.verejne_url()
# TODO možná chceme odkazovat na právě rozpracované číslo, a ne to poslední vydané
# pokud nechceme haluzit kód (= poradi) dalšího čísla, bude asi potřeba jít
# přes treenody (a dát si přitom pozor na MezicisloNode)
neobodovana_reseni = s.Hodnoceni.objects.filter(body__isnull=True)
reseni_mimo_cislo = s.Hodnoceni.objects.filter(deadline_body__isnull=True)
neobodovana_reseni = Hodnoceni.objects.filter(body__isnull=True)
reseni_mimo_cislo = Hodnoceni.objects.filter(deadline_body__isnull=True)
context['pocet_neobodovanych_reseni'] = neobodovana_reseni.count()
context['pocet_reseni_mimo_cislo'] = reseni_mimo_cislo.count()
u = self.request.user
os = s.Osoba.objects.get(user=u)
organizator = s.Organizator.objects.get(osoba=os)
os = m.Osoba.objects.get(user=u)
organizator = m.Organizator.objects.get(osoba=os)
context['muj_pocet_neobodovanych_reseni'] = neobodovana_reseni.filter(Q(problem__garant=organizator) | Q(problem__autor=organizator) | Q(problem__opravovatele__in=[organizator])).distinct().count()
context['muj_pocet_reseni_mimo_cislo'] = reseni_mimo_cislo.filter(Q(problem__garant=organizator) | Q(problem__autor=organizator) | Q(problem__opravovatele__in=[organizator])).count()
@ -116,11 +120,11 @@ class OrgoRozcestnikView(TemplateView):
context["pocty_neopravenych_reseni"] = [(it['problem__nazev'], it['cas'].date) for it in pocty_neopravenych_reseni.all()]
#FIXME: přidat stav='STAV_ZADANY'
temata = s.Tema.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]),
temata = Tema.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]),
rocnik=aktualni_rocnik).distinct()
ulohy = s.Uloha.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]),
ulohy = Uloha.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]),
cislo_zadani__rocnik=aktualni_rocnik).distinct()
clanky = s.Clanek.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]),
clanky = Clanek.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]),
cislo__rocnik=aktualni_rocnik).distinct()
context['temata'] = temata
@ -134,12 +138,12 @@ class OrgoRozcestnikView(TemplateView):
class ResitelView(LoginRequiredMixin,generic.DetailView):
model = s.Resitel
model = m.Resitel
template_name = 'personalni/profil/resitel.html'
def get_object(self, queryset=None):
print(self.request.user)
return s.Resitel.objects.get(osoba__user=self.request.user)
return m.Resitel.objects.get(osoba__user=self.request.user)
### Formulare
@ -157,10 +161,10 @@ def prihlaska_log_gdpr_safe(logger, gdpr_logger, msg, form_data):
@sensitive_post_parameters('jmeno', 'prijmeni', 'email', 'telefon', 'datum_narozeni', 'ulice', 'mesto', 'psc', 'skola')
def resitelEditView(request):
err_logger = logging.getLogger('seminar.prihlaska.problem')
err_logger = logging.getLogger('personalni.prihlaska.problem')
## Načtení objektů Osoba a Resitel patřících k aktuálně přihlášenému uživateli
u = request.user
osoba_edit = s.Osoba.objects.get(user=u)
osoba_edit = m.Osoba.objects.get(user=u)
if hasattr(osoba_edit,'resitel'):
resitel_edit = osoba_edit.resitel
else:
@ -195,7 +199,7 @@ def resitelEditView(request):
## Změny v osobě
fcd = form.cleaned_data
form_hash = hash(frozenset(fcd.items()))
form_logger = logging.getLogger('seminar.prihlaska.form')
form_logger = logging.getLogger('personalni.prihlaska.form')
form_logger.info("EDIT:" + str(fcd) + str(form_hash)) # TODO možná logovat jinak
osoba_edit.jmeno = fcd['jmeno']
osoba_edit.prijmeni = fcd['prijmeni']
@ -244,9 +248,9 @@ def resitelEditView(request):
@sensitive_post_parameters('jmeno', 'prijmeni', 'email', 'telefon', 'datum_narozeni', 'ulice', 'mesto', 'psc', 'skola', 'jak_se_dozvedeli')
def prihlaskaView(request):
generic_logger = logging.getLogger('seminar.prihlaska')
err_logger = logging.getLogger('seminar.prihlaska.problem')
form_logger = logging.getLogger('seminar.prihlaska.form')
generic_logger = logging.getLogger('personalni.prihlaska')
err_logger = logging.getLogger('personalni.prihlaska.problem')
form_logger = logging.getLogger('personalni.prihlaska.form')
if request.method == 'POST':
form = PrihlaskaForm(request.POST)
# TODO vyresit, co se bude v jakych situacich zobrazovat
@ -266,7 +270,7 @@ def prihlaskaView(request):
resitel_grp = Group.objects.filter(name__exact='resitel').first()
u.groups.add(resitel_grp)
o = s.Osoba(
o = m.Osoba(
jmeno = fcd['jmeno'],
prijmeni = fcd['prijmeni'],
osloveni = fcd['osloveni'],
@ -328,7 +332,7 @@ def prihlaskaView(request):
if kolize.count() > 1: # Jednu z nich jsme právě uložili
err_logger.warning(f'Zaregistrovala se osoba s kolizním jménem. ID osob: {[o.id for o in kolize]}')
r = s.Resitel(
r = m.Resitel(
prezdivka_resitele=fcd['prezdivka_resitele'] if fcd['prezdivka_resitele'] != "" else None,
rok_maturity = fcd['rok_maturity'],
zasilat = fcd['zasilat'],

2
prednasky/admin.py

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

3
prednasky/views.py

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

10
soustredeni/models.py

@ -10,7 +10,7 @@ from django.conf import settings
from personalni.models import Resitel, Organizator
from seminar.models.base import SeminarModelBase
import tvorba.models as am
from tvorba.models import Rocnik, Problem, aux_generate_filename
logger = logging.getLogger(__name__)
@ -27,7 +27,7 @@ class Soustredeni(SeminarModelBase):
# Interní ID
id = models.AutoField(primary_key = True)
rocnik = models.ForeignKey(am.Rocnik, verbose_name='ročník', related_name='soustredeni',
rocnik = models.ForeignKey(Rocnik, verbose_name='ročník', related_name='soustredeni',
on_delete=models.PROTECT)
datum_zacatku = models.DateField('datum začátku', blank=True, null=True,
@ -75,7 +75,7 @@ class Soustredeni(SeminarModelBase):
def verejne_url(self):
#return reverse('seminar_soustredeni', kwargs={'pk': self.id})
return reverse('seminar_seznam_soustredeni')
return reverse('soustredeni_probehlo')
@reversion.register(ignore_duplicates=True)
@ -143,13 +143,13 @@ class Soustredeni_Organizatori(SeminarModelBase):
def generate_filename_konfera(self, filename):
return os.path.join(
settings.SEMINAR_KONFERY_DIR,
am.aux_generate_filename(self, filename)
aux_generate_filename(self, filename)
)
##
@reversion.register(ignore_duplicates=True)
class Konfera(am.Problem):
class Konfera(Problem):
class Meta:
db_table = 'seminar_konfera'
verbose_name = 'Konfera'

2
soustredeni/templates/soustredeni/seznam_soustredeni.html

@ -40,7 +40,7 @@
<div class="mam-org-only">
<a href="../{{soustredeni.pk}}/fotogalerie/0/new/">Vytvořit novou fotogalerii</a><br>
<a href="../{{soustredeni.pk}}/obalky.pdf">Vygenerovat obálky pro účastníky</a><br>
<a href={% url 'seminar_soustredeni_abstrakty' soustredeni.pk %}>Vygenerovat účastníky a vedoucí do abstraktů</a><br>
<a href={% url 'soustredeni_abstrakty' soustredeni.pk %}>Vygenerovat účastníky a vedoucí do abstraktů</a><br>
Seznam účastníků -
<a href="../{{soustredeni.pk}}/seznam_ucastniku">HTML tabulka pro tisk</a>,
<a href="../{{soustredeni.pk}}/export_ucastniku">CSV</a>,

4
soustredeni/testutils.py

@ -6,7 +6,7 @@ from typing import Sequence
import lorem
from .models import Soustredeni, Konfera
import seminar.models as am # tvorba
from tvorba.models import Rocnik
import personalni.models as pm
logger = logging.getLogger(__name__)
@ -25,7 +25,7 @@ def gen_soustredeni(
for _ in range(1, 10): # FIXME Tu range si změňte jak chcete, nevím, co přesně znamená size (asi Anet?)
datum_zacatku = datetime.date(rnd.randint(2000, 2020), rnd.randint(1, 12), rnd.randint(1, 28))
working_sous = Soustredeni.objects.create(
rocnik=am.Rocnik.objects.order_by('?').first(),
rocnik=Rocnik.objects.order_by('?').first(),
verejne_db=rnd.choice([True, False]),
misto=rnd.choice(['Kremrolovice', 'Indiánov', 'U zmzliny', 'Vafláreň', 'Větrník', 'Horní Rakvička', 'Dolní cheesecake']),
typ=rnd.choice(['jarni', 'podzimni', 'vikend']),

6
soustredeni/urls.py

@ -7,7 +7,7 @@ urlpatterns = [
path(
'probehlo/',
views.SoustredeniListView.as_view(),
name='seminar_seznam_soustredeni'
name='soustredeni_probehlo'
),
path(
'<int:soustredeni>/',
@ -37,12 +37,12 @@ urlpatterns = [
path(
'obalky.pdf',
org_required(views.soustredeniObalkyView),
name='seminar_soustredeni_obalky'
name='soustredeni_obalky'
),
path(
'abstrakty',
org_required(views.SoustredeniAbstraktyView.as_view()),
name='seminar_soustredeni_abstrakty'
name='soustredeni_abstrakty'
),
path(
'fotogalerie/',

73
treenode/admin.py

@ -4,25 +4,26 @@ from django.forms import widgets
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter
import seminar.models as m
from .models import TreeNode, RocnikNode, CisloNode, MezicisloNode, TemaVCisleNode, UlohaZadaniNode, PohadkaNode, UlohaVzorakNode, TextNode, CastNode, OrgTextNode
from .models.pomocne import Text, Obrazek
# Polymorfismus pro stromy
# TODO: Inlines podle https://django-polymorphic.readthedocs.io/en/stable/admin.html
@admin.register(m.TreeNode)
@admin.register(TreeNode)
class TreeNodeAdmin(PolymorphicParentModelAdmin):
base_model = m.TreeNode
base_model = TreeNode
child_models = [
m.RocnikNode,
m.CisloNode,
m.MezicisloNode,
m.TemaVCisleNode,
m.UlohaZadaniNode,
m.PohadkaNode,
m.UlohaVzorakNode,
m.TextNode,
m.CastNode,
m.OrgTextNode,
RocnikNode,
CisloNode,
MezicisloNode,
TemaVCisleNode,
UlohaZadaniNode,
PohadkaNode,
UlohaVzorakNode,
TextNode,
CastNode,
OrgTextNode,
]
actions = ['aktualizuj_nazvy']
@ -36,64 +37,64 @@ class TreeNodeAdmin(PolymorphicParentModelAdmin):
self.message_user(request, "Názvy aktualizovány.")
aktualizuj_nazvy.short_description = "Aktualizuj vybraným TreeNodům názvy"
@admin.register(m.RocnikNode)
@admin.register(RocnikNode)
class RocnikNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.RocnikNode
base_model = RocnikNode
show_in_index = True
@admin.register(m.CisloNode)
@admin.register(CisloNode)
class CisloNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.CisloNode
base_model = CisloNode
show_in_index = True
@admin.register(m.MezicisloNode)
@admin.register(MezicisloNode)
class MezicisloNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.MezicisloNode
base_model = MezicisloNode
show_in_index = True
@admin.register(m.TemaVCisleNode)
@admin.register(TemaVCisleNode)
class TemaVCisleNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.TemaVCisleNode
base_model = TemaVCisleNode
show_in_index = True
@admin.register(m.UlohaZadaniNode)
@admin.register(UlohaZadaniNode)
class UlohaZadaniNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.UlohaZadaniNode
base_model = UlohaZadaniNode
show_in_index = True
@admin.register(m.PohadkaNode)
@admin.register(PohadkaNode)
class PohadkaNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.PohadkaNode
base_model = PohadkaNode
show_in_index = True
@admin.register(m.UlohaVzorakNode)
@admin.register(UlohaVzorakNode)
class UlohaVzorakNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.UlohaVzorakNode
base_model = UlohaVzorakNode
show_in_index = True
@admin.register(m.TextNode)
@admin.register(TextNode)
class TextNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.TextNode
base_model = TextNode
show_in_index = True
@admin.register(m.CastNode)
@admin.register(CastNode)
class TextNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.CastNode
base_model = CastNode
show_in_index = True
fields = ('nadpis',)
@admin.register(m.OrgTextNode)
@admin.register(OrgTextNode)
class TextNodeAdmin(PolymorphicChildModelAdmin):
base_model = m.OrgTextNode
base_model = OrgTextNode
show_in_index = True
class TextAdminInline(admin.TabularInline):
model = m.Text
model = Text
formfield_overrides = {
models.TextField: {'widget': widgets.TextInput}
}
exclude = ['text_zkraceny_set', 'text_zkraceny']
admin.site.register(m.Text)
admin.site.register(m.Obrazek)
admin.site.register(Text)
admin.site.register(Obrazek)

4
treenode/forms.py

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

1
treenode/models.py

@ -337,4 +337,3 @@ class Obrazek(SeminarModelBase):
# TODO placement hint - chci ho tady / pred textem / za textem

1
treenode/models/__init__.py

@ -0,0 +1 @@
from seminar.models.treenode import *

1
treenode/models/pomocne.py

@ -0,0 +1 @@
from seminar.models.pomocne import *

132
treenode/serializers.py

@ -1,7 +1,11 @@
from rest_framework import serializers
from rest_polymorphic.serializers import PolymorphicSerializer
import seminar.models as m
from odevzdavatko.models import Reseni
from tvorba.models import Problem, Uloha
from .models import RocnikNode, CisloNode, MezicisloNode, TemaVCisleNode, OrgTextNode, PohadkaNode, TextNode, TreeNode, CastNode, UlohaZadaniNode, UlohaVzorakNode, ReseniNode
from .models.pomocne import Text
from treenode import treelib
DEFAULT_NODE_DEPTH = 2
@ -9,57 +13,57 @@ DEFAULT_NODE_DEPTH = 2
class TextSerializer(serializers.ModelSerializer):
class Meta:
model = m.Text
model = Text
fields = '__all__'
class ProblemSerializer(serializers.ModelSerializer):
class Meta:
model = m.Problem
model = Problem
fields = '__all__'
class UlohaSerializer(serializers.ModelSerializer):
class Meta:
model = m.Uloha
model = Uloha
fields = '__all__'
class ReseniSerializer(serializers.ModelSerializer):
class Meta:
model = m.Reseni
model = Reseni
fields = '__all__'
class RocnikNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.RocnikNode
model = RocnikNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
class CisloNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.CisloNode
model = CisloNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
class MezicisloNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.MezicisloNode
model = MezicisloNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
class TemaVCisleNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.TemaVCisleNode
model = TemaVCisleNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
class OrgTextNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.OrgTextNode
model = OrgTextNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
class PohadkaNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.PohadkaNode
model = PohadkaNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
@ -67,7 +71,7 @@ class TextNodeSerializer(serializers.ModelSerializer):
text = TextSerializer()
class Meta:
model = m.TextNode
model = TextNode
fields = ('id','text','polymorphic_ctype')
depth = DEFAULT_NODE_DEPTH
@ -80,7 +84,7 @@ class TextNodeWriteSerializer(serializers.ModelSerializer):
return node
class Meta:
model = m.TextNode
model = TextNode
fields = ('id','text')
depth = DEFAULT_NODE_DEPTH
@ -93,26 +97,26 @@ class TextNodeCreateSerializer(serializers.ModelSerializer):
temp_text = validated_data.pop('text')
where = validated_data.pop('where')
refnode_id = validated_data.pop('refnode')
refnode = m.TreeNode.objects.get(pk=refnode_id)
text = m.Text.objects.create(**temp_text)
refnode = TreeNode.objects.get(pk=refnode_id)
text = Text.objects.create(**temp_text)
if where == 'syn':
node = treelib.create_child(refnode,m.TextNode,text=text)
node = treelib.create_child(refnode,TextNode,text=text)
elif where == 'za':
node = treelib.create_node_after(refnode,m.TextNode,text=text)
node = treelib.create_node_after(refnode,TextNode,text=text)
elif where == 'pred':
node = treelib.create_node_before(refnode,m.TextNode,text=text)
node = treelib.create_node_before(refnode,TextNode,text=text)
node.where = None
node.refnode = None
return node
class Meta:
model = m.TextNode
model = TextNode
fields = ('text','where','refnode')
depth = DEFAULT_NODE_DEPTH
class CastNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.CastNode
model = CastNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
@ -124,25 +128,25 @@ class CastNodeCreateSerializer(serializers.ModelSerializer):
temp_nadpis = validated_data.pop('nadpis')
where = validated_data.pop('where')
refnode_id = validated_data.pop('refnode')
refnode = m.TreeNode.objects.get(pk=refnode_id)
refnode = TreeNode.objects.get(pk=refnode_id)
if where == 'syn':
node = treelib.create_child(refnode,m.CastNode,nadpis=temp_nadpis)
node = treelib.create_child(refnode,CastNode,nadpis=temp_nadpis)
elif where == 'za':
node = treelib.create_node_after(refnode,m.CastNode,nadpis=temp_nadpis)
node = treelib.create_node_after(refnode,CastNode,nadpis=temp_nadpis)
elif where == 'pred':
node = treelib.create_node_before(refnode,m.CastNode,nadpis=temp_nadpis)
node = treelib.create_node_before(refnode,CastNode,nadpis=temp_nadpis)
node.where = None
node.refnode = None
return node
class Meta:
model = m.CastNode
model = CastNode
fields = ('nadpis','where','refnode')
depth = DEFAULT_NODE_DEPTH
class UlohaZadaniNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.UlohaZadaniNode
model = UlohaZadaniNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
@ -157,7 +161,7 @@ class UlohaZadaniNodeWriteSerializer(serializers.ModelSerializer):
return node
class Meta:
model = m.TextNode
model = TextNode
fields = ('id','uloha')
depth = DEFAULT_NODE_DEPTH
@ -171,28 +175,28 @@ class UlohaZadaniNodeCreateSerializer(serializers.ModelSerializer):
temp_uloha = validated_data.pop('uloha')
where = validated_data.pop('where')
refnode_id = validated_data.pop('refnode')
refnode = m.TreeNode.objects.get(pk=refnode_id)
refnode = TreeNode.objects.get(pk=refnode_id)
# Z cesty ke koreni stromu zjistime, v jakem jsme tematu a v jakem cisle
cislo = None
tema = None
travelnode = refnode
while travelnode is not None:
if isinstance(travelnode, m.TemaVCisleNode):
if isinstance(travelnode, TemaVCisleNode):
tema = travelnode.tema
if isinstance(travelnode, m.CisloNode):
if isinstance(travelnode, CisloNode):
cislo = travelnode.cislo
travelnode = treelib.get_parent(travelnode)
# Vyrobime ulohu
uloha = m.Uloha.objects.create(cislo_zadani=cislo, nadproblem = tema, **temp_uloha)
uloha = Uloha.objects.create(cislo_zadani=cislo, nadproblem = tema, **temp_uloha)
# A vyrobime UlohaZadaniNode
if where == 'syn':
node = treelib.create_child(refnode,m.UlohaZadaniNode,uloha = uloha)
node = treelib.create_child(refnode,UlohaZadaniNode,uloha = uloha)
elif where == 'za':
node = treelib.create_node_after(refnode,m.UlohaZadaniNode,uloha = uloha)
node = treelib.create_node_after(refnode,UlohaZadaniNode,uloha = uloha)
elif where == 'pred':
node = treelib.create_node_before(refnode,m.UlohaZadaniNode,uloha = uloha)
node = treelib.create_node_before(refnode,UlohaZadaniNode,uloha = uloha)
node.where = None
node.refnode = None
node.max_body = None
@ -200,21 +204,21 @@ class UlohaZadaniNodeCreateSerializer(serializers.ModelSerializer):
return node
class Meta:
model = m.UlohaZadaniNode
model = UlohaZadaniNode
fields = ('uloha','where','refnode')
depth = DEFAULT_NODE_DEPTH
class UlohaVzorakNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.UlohaVzorakNode
model = UlohaVzorakNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
class UlohaVzorakNodeWriteSerializer(serializers.ModelSerializer):
uloha = serializers.PrimaryKeyRelatedField(queryset=m.Uloha.objects.all(), many=False, read_only=False)
uloha = serializers.PrimaryKeyRelatedField(queryset=Uloha.objects.all(), many=False, read_only=False)
class Meta:
model = m.UlohaVzorakNode
model = UlohaVzorakNode
fields = ('id','uloha')
depth = DEFAULT_NODE_DEPTH
@ -226,17 +230,17 @@ class UlohaVzorakNodeCreateSerializer(serializers.ModelSerializer):
def create(self, validated_data):
uloha_id = validated_data.pop('uloha_id')
uloha = m.Uloha.objects.get(pk=uloha_id)
uloha = Uloha.objects.get(pk=uloha_id)
where = validated_data.pop('where')
refnode_id = validated_data.pop('refnode')
refnode = m.TreeNode.objects.get(pk=refnode_id)
refnode = TreeNode.objects.get(pk=refnode_id)
if where == 'syn':
node = treelib.create_child(refnode,m.UlohaVzorakNode,uloha = uloha)
node = treelib.create_child(refnode,UlohaVzorakNode,uloha = uloha)
elif where == 'za':
node = treelib.create_node_after(refnode,m.UlohaVzorakNode,uloha = uloha)
node = treelib.create_node_after(refnode,UlohaVzorakNode,uloha = uloha)
elif where == 'pred':
node = treelib.create_node_before(refnode,m.UlohaVzorakNode,uloha = uloha)
node = treelib.create_node_before(refnode,UlohaVzorakNode,uloha = uloha)
node.refnode = None
node.where = None
node.uloha_id = None
@ -244,7 +248,7 @@ class UlohaVzorakNodeCreateSerializer(serializers.ModelSerializer):
return node
class Meta:
model = m.UlohaVzorakNode
model = UlohaVzorakNode
fields = ('refnode', 'uloha_id', 'where')
depth = DEFAULT_NODE_DEPTH
@ -253,15 +257,15 @@ class UlohaVzorakNodeCreateSerializer(serializers.ModelSerializer):
class ReseniNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.ReseniNode
model = ReseniNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
class ReseniNodeWriteSerializer(serializers.ModelSerializer):
reseni = serializers.PrimaryKeyRelatedField(queryset=m.Reseni.objects.all(), many=False, read_only=False)
reseni = serializers.PrimaryKeyRelatedField(queryset=Reseni.objects.all(), many=False, read_only=False)
class Meta:
model = m.ReseniNode
model = ReseniNode
fields = ('id','reseni')
depth = DEFAULT_NODE_DEPTH
@ -273,41 +277,41 @@ class ReseniNodeCreateSerializer(serializers.ModelSerializer):
def create(self,validated_data):
# text_zadani = validated_data.pop('text_zadani')
reseni_id = validated_data.pop('reseni_id')
reseni = m.Reseni.objects.get(pk=reseni_id)
reseni = Reseni.objects.get(pk=reseni_id)
where = validated_data.pop('where')
refnode_id = validated_data.pop('refnode')
refnode = m.TreeNode.objects.get(pk=refnode_id)
refnode = TreeNode.objects.get(pk=refnode_id)
# A vyrobime UlohaZadaniNode
if where == 'syn':
node = treelib.create_child(refnode,m.ReseniNode,reseni = reseni)
node = treelib.create_child(refnode,ReseniNode,reseni = reseni)
elif where == 'za':
node = treelib.create_node_after(refnode,m.ReseniNode,reseni = reseni)
node = treelib.create_node_after(refnode,ReseniNode,reseni = reseni)
elif where == 'pred':
node = treelib.create_node_before(refnode,m.ReseniNode,reseni = reseni)
node = treelib.create_node_before(refnode,ReseniNode,reseni = reseni)
node.where = None
node.refnode = None
node.reseni_id = None
return node
class Meta:
model = m.ReseniNode
model = ReseniNode
fields = ('reseni_id','where','refnode')
depth = DEFAULT_NODE_DEPTH
class TreeNodeSerializer(PolymorphicSerializer):
model_serializer_mapping = {
m.RocnikNode: RocnikNodeSerializer,
m.CisloNode: CisloNodeSerializer,
m.MezicisloNode: MezicisloNodeSerializer,
m.TemaVCisleNode: TemaVCisleNodeSerializer,
m.OrgTextNode: OrgTextNodeSerializer,
m.UlohaZadaniNode: UlohaZadaniNodeSerializer,
m.UlohaVzorakNode: UlohaVzorakNodeSerializer,
m.PohadkaNode: PohadkaNodeSerializer,
m.TextNode: TextNodeSerializer,
m.CastNode: CastNodeSerializer,
m.ReseniNode: ReseniNodeSerializer,
RocnikNode: RocnikNodeSerializer,
CisloNode: CisloNodeSerializer,
MezicisloNode: MezicisloNodeSerializer,
TemaVCisleNode: TemaVCisleNodeSerializer,
OrgTextNode: OrgTextNodeSerializer,
UlohaZadaniNode: UlohaZadaniNodeSerializer,
UlohaVzorakNode: UlohaVzorakNodeSerializer,
PohadkaNode: PohadkaNodeSerializer,
TextNode: TextNodeSerializer,
CastNode: CastNodeSerializer,
ReseniNode: ReseniNodeSerializer,
}

12
treenode/templates/treenode/treenode_recursive.html

@ -25,20 +25,20 @@
<button>Zvyš úroveň nadpisu</button> - nejsou testovací data
</div>
{% endif %}
{% include "seminar/treenode_name.html" %}
{% include "treenode/treenode_name.html" %}
{%if obj.children %}
<div class="borderized children">
{% with kam="před" kam_slug="syn" %} {% include "seminar/treenode_add_stub.html" %} {% endwith %}
{% with kam="před" kam_slug="syn" %} {% include "treenode/treenode_add_stub.html" %} {% endwith %}
{%for ch in obj.children %}
{# ----------- Vypisujeme podstrom ----------#}
{%with obj=ch depth=depth|add:"1" %} {%include "seminar/treenode_recursive.html" %} {%endwith%}
{%with obj=ch depth=depth|add:"1" %} {%include "treenode/treenode_recursive.html" %} {%endwith%}
{# ----------- Přidáváme mezi syny / za posledního -------- #}
{% if forloop.last %}
{% with kam="za" kam_slug="za" obj=ch %} {% include "seminar/treenode_add_stub.html" %} {% endwith %}
{% with kam="za" kam_slug="za" obj=ch %} {% include "treenode/treenode_add_stub.html" %} {% endwith %}
{% else %}
{% with kam="mezi" obj=ch kam_slug="za" %} {% include "seminar/treenode_add_stub.html" %} {% endwith %}
{% with kam="mezi" obj=ch kam_slug="za" %} {% include "treenode/treenode_add_stub.html" %} {% endwith %}
{% endif %}
{# ----------- Prohazujeme sousedy ----------#}
<div class="pink">
@ -50,6 +50,6 @@
</div>
{% else %}
{# ----------- Přidáváme prvního syna ----------#}
{% with kam="jako syna" kam_slug="syn" %} {% include "seminar/treenode_add_stub.html" %} {% endwith %}
{% with kam="jako syna" kam_slug="syn" %} {% include "treenode/treenode_add_stub.html" %} {% endwith %}
{%endif%}
</div>

34
treenode/templatetags.py

@ -1,6 +1,6 @@
from django import template
from enum import Enum
import seminar.models as m
from .models import RocnikNode, CisloNode, CastNode, TextNode, TemaVCisleNode, UlohaVzorakNode, UlohaZadaniNode, PohadkaNode
register = template.Library()
@ -11,8 +11,8 @@ def nodeType(value):
if isinstance(value,CastNode): return "Část"
if isinstance(value,TextNode): return "Text"
if isinstance(value,TemaVCisleNode): return "Téma v čísle"
if isinstance(value,KonferaNode): return "Konfera"
if isinstance(value,ClanekNode): return "Článek"
# if isinstance(value,KonferaNode): return "Konfera" # FIXME neexistuje
# if isinstance(value,ClanekNode): return "Článek" # FIXME neexistuje
if isinstance(value,UlohaVzorakNode): return "Vzorák"
if isinstance(value,UlohaZadaniNode): return "Zadání úlohy"
if isinstance(value,PohadkaNode): return "Pohádka"
@ -22,53 +22,57 @@ def nodeType(value):
@register.filter
def isRocnik(value):
return isinstance(value, m.RocnikNode)
return isinstance(value, RocnikNode)
@register.filter
def isCislo(value):
return isinstance(value, m.CisloNode)
return isinstance(value, CisloNode)
@register.filter
def isCast(value):
return isinstance(value, m.CastNode)
return isinstance(value, CastNode)
@register.filter
def isText(value):
return isinstance(value, m.TextNode)
return isinstance(value, TextNode)
@register.filter
def isTemaVCisle(value):
return isinstance(value, m.TemaVCisleNode)
return isinstance(value, TemaVCisleNode)
@register.filter
def isKonfera(value):
return isinstance(value, m.KonferaNode)
# FIXME neexistuje
# return isinstance(value, KonferaNode)
return False
@register.filter
def isClanek(value):
return isinstance(value, m.ClanekNode)
# FIXME neexistuje
# return isinstance(value, seminar.models.ClanekNode)
return False
@register.filter
def isUlohaVzorak(value):
return isinstance(value, m.UlohaVzorakNode)
return isinstance(value, UlohaVzorakNode)
@register.filter
def isUlohaZadani(value):
return isinstance(value, m.UlohaZadaniNode)
return isinstance(value, UlohaZadaniNode)
@register.filter
def isPohadka(value):
return isinstance(value, m.PohadkaNode)
return isinstance(value, PohadkaNode)
@register.filter
def isReseni(value):
return False
# return isinstance(value, m.OtisteneReseniNode)
# return isinstance(value, OtisteneReseniNode)
@register.filter
def isOrgText(value):
return False
# return isinstance(value, m.OrgTextNode)
# return isinstance(value, OrgTextNode)
###

12
treenode/tests.py

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

6
treenode/treelib.py

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

10
treenode/urls.py

@ -2,16 +2,16 @@ from django.urls import path, re_path
from . import views
urlpatterns = [
#path('treenode/<int:pk>/', views.TreeNodeView.as_view(), name='seminar_treenode'),
#path('treenode/<int:pk>/json/', views.TreeNodeJSONView.as_view(), name='seminar_treenode_json'),
#path('treenode/text/<int:pk>/', views.TextWebView.as_view(), name='seminar_textnode_web'),
#path('treenode/<int:pk>/', views.TreeNodeView.as_view(), name='treenode'),
#path('treenode/<int:pk>/json/', views.TreeNodeJSONView.as_view(), name='treenode_json'),
#path('treenode/text/<int:pk>/', views.TextWebView.as_view(), name='treenode_textnode_web'),
#path('treenode/editor/pridat/<str:co>/<int:pk>/<str:kam>/', views.TreeNodePridatView.as_view(), name='treenode_pridat'),
#path('treenode/editor/smazat/<int:pk>/', views.TreeNodeSmazatView.as_view(), name='treenode_smazat'),
#path('treenode/editor/odvesitpryc/<int:pk>/', views.TreeNodeOdvesitPrycView.as_view(), name='treenode_odvesitpryc'),
#path('treenode/editor/podvesit/<int:pk>/<str:kam>/', views.TreeNodePodvesitView.as_view(), name='treenode_podvesit'),
#path('treenode/editor/prohodit/<int:pk>/', views.TreeNodeProhoditView.as_view(), name='treenode_prohodit'),
#path('treenode/sirotcinec/', views.SirotcinecView.as_view(), name='seminar_treenode_sirotcinec'),
#path('problem/(?P<pk>\d+)/(?P<prispevek>\d+)/', views.PrispevekView.as_view(), name='seminar_problem_prispevek'),
#path('treenode/sirotcinec/', views.SirotcinecView.as_view(), name='treenode_sirotcinec'),
#path('problem/(?P<pk>\d+)/(?P<prispevek>\d+)/', views.PrispevekView.as_view(), name='treenode_problem_prispevek'),
re_path(r'^temp/vue/.*$',views.VueTestView.as_view(),name='vue_test_view'),
path('temp/image_upload/', views.NahrajObrazekKTreeNoduView.as_view()),

82
treenode/views.py

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

25
treenode/viewsets.py

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

6
tvorba/models.py

@ -96,7 +96,7 @@ class Rocnik(SeminarModelBase):
return self.prvni_rok + 1
def verejne_url(self):
return reverse('seminar_rocnik', kwargs={'rocnik': self.rocnik})
return reverse('tvorba_rocnik', kwargs={'rocnik': self.rocnik})
@classmethod
def cached_rocnik(cls, r_id):
@ -171,7 +171,7 @@ class Cislo(SeminarModelBase):
verejne.boolean = True
def verejne_url(self):
return reverse('seminar_cislo', kwargs={'rocnik': self.rocnik.rocnik, 'cislo': self.poradi})
return reverse('tvorba_cislo', kwargs={'rocnik': self.rocnik.rocnik, 'cislo': self.poradi})
def absolute_url(self):
return "https://" + str(get_current_site(None)) + self.verejne_url()
@ -508,7 +508,7 @@ class Problem(SeminarModelBase,PolymorphicModel):
# verejne.boolean = True
def verejne_url(self):
return reverse('seminar_problem', kwargs={'pk': self.id})
return reverse('tvorba_problem', kwargs={'pk': self.id})
def admin_url(self):
return reverse('admin:tvorba_problem_change', args=(self.id, ))

12
tvorba/templates/tvorba/archiv/cislo.html

@ -35,12 +35,12 @@
<div class="mam-org-only">
<h2> Orgovské odkazy </h2>
<ul>
<li><a href="obalky.pdf">Obálky (PDF)</a></li>
<li><a href="tituly.tex" download>Tituly (TeX, 2. deadline předchozího čísla a 1.deadline tohoto)</a></li>
<li><a href="vysledkovka.tex" download>Výsledkovka (TeX, 2. deadline předchozího čísla a 1.deadline tohoto)</a></li>
<li><a href="odmeny/{{prevcislo.rocnik.rocnik}}.{{prevcislo.poradi}}/">Odměny</a></li>
<li><a href="{% url "seminar_rocnik_titul" rocnik=cislo.rocnik.rocnik %}" download="posledni_tituly.tex">Tituly do závěrečného čísla (TeX, 2. deadline předchozího čísla a oba tohoto)</a></li>
<li><a href="{% url "seminar_rocnik_posledni_vysledkovka" rocnik=cislo.rocnik.rocnik %}" download>Výsledkovka závěrečného čísla ročníku (TeX, 2. deadline předchozího čísla a oba tohoto)</a></li>
<li><a href="{% url "tvorba_cislo_obalky" rocnik=cislo.rocnik.rocnik cislo=cislo.poradi %}">Obálky (PDF)</a></li>
<li><a href="{% url "tvorba_cislo_titul" rocnik=cislo.rocnik.rocnik cislo=cislo.poradi %}" download>Tituly (TeX, 2. deadline předchozího čísla a 1.deadline tohoto)</a></li>
<li><a href="{% url "tvorba_cislo_vysledkovka" rocnik=cislo.rocnik.rocnik cislo=cislo.poradi %}" download>Výsledkovka (TeX, 2. deadline předchozího čísla a 1.deadline tohoto)</a></li>
<li>{% if prevcislo %}<a href="{% url "tvorba_archiv_odmeny" trocnik=cislo.rocnik.rocnik tcislo=cislo.poradi frocnik=prevcislo.rocnik.rocnik fcislo=prevcislo.poradi %}">Odměny</a>{% else %}Pro toto číslo neumíme spočítat odměny.{% endif %}{# FIXME (Jediné číslo, kde toto neumíme je to úplně první.) #}</li>
<li><a href="{% url "tvorba_rocnik_titul" rocnik=cislo.rocnik.rocnik %}" download="posledni_tituly.tex">Tituly do závěrečného čísla (TeX, 2. deadline předchozího čísla a oba tohoto)</a></li>
<li><a href="{% url "tvorba_rocnik_posledni_vysledkovka" rocnik=cislo.rocnik.rocnik %}" download>Výsledkovka závěrečného čísla ročníku (TeX, 2. deadline předchozího čísla a oba tohoto)</a></li>
</ul>
</div>
{% endif %}

2
tvorba/templates/tvorba/archiv/problem_clanek.html

@ -1,4 +1,4 @@
{% extends "seminar/archiv/problem.html" %}
{% extends "tvorba/archiv/problem.html" %}
{% block problem %}
{% if problem.cislo_zadani %}

2
tvorba/templates/tvorba/archiv/problem_tema.html

@ -1,4 +1,4 @@
{% extends "seminar/archiv/problem.html" %}
{% extends "tvorba/archiv/problem.html" %}
{% block problem %}
<h1>

2
tvorba/templates/tvorba/archiv/problem_uloha.html

@ -1,4 +1,4 @@
{% extends "seminar/archiv/problem.html" %}
{% extends "tvorba/archiv/problem.html" %}
{% block problem %}
<h1>

2
tvorba/templates/tvorba/archiv/problem_uloha_tema.html

@ -1,4 +1,4 @@
{% extends "seminar/archiv/problem.html" %}
{% extends "tvorba/archiv/problem.html" %}
{% block problem %}
<h1>

2
tvorba/templates/tvorba/archiv/rocnik.html

@ -121,7 +121,7 @@
<p><a href='vysledkovka.tex' download>Výsledkovka ročníku (LaTeX, včetně neveřejných)</a></p>
<p><a href="tituly.tex" download>Tituly (TeX, včetně neveřejných, všechny, nevhodné do mamtexu)</a></p>
{# FIXME: Sice to sem asi nepatří sémanticky, ale bylo to nejjednodušší… #}
<p><a href='{% url 'seminar_rocnik_resitele_csv' rocnik=rocnik.rocnik %}' download>CSV export řešitelů</a></p>
<p><a href='{% url 'tvorba_rocnik_resitele_csv' rocnik=rocnik.rocnik %}' download>CSV export řešitelů</a></p>
<h2>Výsledková listina včetně neveřejných bodů</h2>
{% include "vysledkovky/vysledkovka_rocnik.html" with vysledkovka=vysledkovka_neverejna %}
</div>

2
tvorba/templates/tvorba/clanky/organizatorske_clanky.html

@ -1,4 +1,4 @@
{% extends 'seminar/clanky/resitelske_clanky.html' %}
{% extends 'tvorba/clanky/resitelske_clanky.html' %}
{% block nadpis1a %}
Organizátorské články

2
tvorba/templates/tvorba/zadani/AktualniVysledkovka.html

@ -17,7 +17,7 @@
<p>
Staré výsledkové listiny najdete
v&nbsp;<a href="{% url 'seminar_archiv_rocniky' %}">archivu</a>.
v&nbsp;<a href="{% url 'tvorba_archiv_rocniky' %}">archivu</a>.
</p>
{% if user.je_org and vysledkovka_s_neverejnymi %}

22
tvorba/templatetags/deadliny.py

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

46
tvorba/testutils.py

@ -6,8 +6,12 @@ import lorem
import django.contrib.auth
import logging
from seminar.models import Rocnik, Cislo, Deadline, Problem, Tema, Uloha, TextNode, UlohaVzorakNode, RocnikNode, CisloNode, TemaVCisleNode, Text, UlohaZadaniNode
import seminar.models as m
from .models import Rocnik, Cislo, Deadline, Problem, Tema, Uloha, Clanek
from odevzdavatko.models import Reseni, Hodnoceni
# TODO zbavit se treenodů do treenode.testutils (ty pak klidně volat odtud)
from treenode.models import TextNode, UlohaZadaniNode, UlohaVzorakNode, RocnikNode, CisloNode, TemaVCisleNode, CastNode, MezicisloNode, ReseniNode
from treenode.models.pomocne import Text
from treenode.treelib import all_children, insert_last_child, all_children_of_type, create_node_after
@ -195,7 +199,7 @@ def add_first_child(node, child):
def get_text():
odstavec = lorem.paragraph()
return Text.objects.create(na_web = odstavec, do_cisla = odstavec)
return Text.objects.create(na_web = odstavec, do_cisla = odstavec)
def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod):
tema = Tema.objects.create(
@ -219,31 +223,31 @@ def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod):
insert_last_child(cislo_node, tema_cislo_node)
# Přidávání obsahu do čísla
cast_node = m.CastNode.objects.create(nadpis = "Příspěvek k číslu {}".format(cislo.kod), root=cislo_node.root)
cast_node = CastNode.objects.create(nadpis = "Příspěvek k číslu {}".format(cislo.kod), root=cislo_node.root)
add_first_child(tema_cislo_node, cast_node)
text_node = TextNode.objects.create(text = get_text(), root=cislo_node.root)
add_first_child(cast_node, text_node)
cast_node2 = m.CastNode.objects.create(nadpis = "První podproblém", root=cislo_node.root)
cast_node2 = CastNode.objects.create(nadpis = "První podproblém", root=cislo_node.root)
add_first_child(text_node, cast_node2)
text_node2 = TextNode.objects.create(text = get_text(), root=cislo_node.root)
add_first_child(cast_node2, text_node2)
cast_node3 = m.CastNode.objects.create(nadpis = "Druhý podproblém", root=cislo_node.root)
cast_node3 = CastNode.objects.create(nadpis = "Druhý podproblém", root=cislo_node.root)
add_first_child(text_node2, cast_node3)
text_node3 = TextNode.objects.create(text = get_text(), root=cislo_node.root)
add_first_child(cast_node3, text_node3)
cast_node4 = m.CastNode.objects.create(nadpis = "Třetí podproblém", root=cislo_node.root)
cast_node4 = CastNode.objects.create(nadpis = "Třetí podproblém", root=cislo_node.root)
add_first_child(text_node3, cast_node4)
text_node4 = TextNode.objects.create(text = get_text(), root=cislo_node.root)
add_first_child(cast_node3, text_node4)
cast_node3a = m.CastNode.objects.create(nadpis = "Podproblém paralelní s "
cast_node3a = CastNode.objects.create(nadpis = "Podproblém paralelní s "
"druhým podproblémem", root=cislo_node.root)
cast_node3.succ = cast_node3a
cast_node3.save()
@ -253,10 +257,10 @@ def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod):
# Občas přidáme mezičíslo
if rnd.randint(1, 3) == 1:
create_node_after(cislo_node, m.MezicisloNode, root=cislo_node.root)
create_node_after(cislo_node, MezicisloNode, root=cislo_node.root)
mezicislo_node = cislo_node.succ
cast_node_mezicislo = m.CastNode.objects.create(
cast_node_mezicislo = CastNode.objects.create(
nadpis = "Příspěvek k mezičíslu".format(cislo.kod), root=cislo_node.root)
add_first_child(mezicislo_node, cast_node_mezicislo)
@ -419,7 +423,7 @@ def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori,
# Najdeme správný TemaVCisleNode pro vložení vzoráku
res_tema_node = None;
for node in all_children(cislo_se_vzorakem.cislonode):
if isinstance(node, TemaVCisleNode):
if isinstance(node, TemaVCisleNode):
if node.tema == tema:
res_tema_node = node
if res_tema_node is None:
@ -438,7 +442,7 @@ def otec_syn(otec, syn):
def gen_clanek(rnd, organizatori, resitele):
logger.info("Generuji článek do čísla 22.2")
clanek = m.Clanek.objects.create(
clanek = Clanek.objects.create(
nazev="Článek o Lorem ipsum",
nadproblem=None,
stav='vyreseny',
@ -448,16 +452,16 @@ def gen_clanek(rnd, organizatori, resitele):
)
clanek.save()
reseni = m.Reseni.objects.create(
reseni = Reseni.objects.create(
zverejneno=True,
)
reseni.resitele.add(rnd.choice(resitele))
reseni.save()
cislo = m.Cislo.objects.get(rocnik__rocnik=22, poradi=2)
cislo = Cislo.objects.get(rocnik__rocnik=22, poradi=2)
cislonode = cislo.cislonode
hodnoceni = m.Hodnoceni.objects.create(
hodnoceni = Hodnoceni.objects.create(
body=15.0,
cislo_body=cislo,
reseni=reseni,
@ -465,7 +469,7 @@ def gen_clanek(rnd, organizatori, resitele):
)
hodnoceni.save()
reseninode = m.ReseniNode.objects.create(
reseninode = ReseniNode.objects.create(
reseni=reseni
)
reseninode.save()
@ -481,26 +485,26 @@ def gen_clanek(rnd, organizatori, resitele):
# FIXME: Ten, kdo vymyslel TreeLib (mj. týž, kdo psal tenhle kód),
# nevyrobil vhodnou funkci, takže to postavíme pozpátku pomocí create_child
# (které vyrábí _prvního_ syna)
create_child(reseninode, m.CastNode, nadpis="Lorem ipsum")
create_child(reseninode, CastNode, nadpis="Lorem ipsum")
# Taky ten člověk nevyrobil vracení nových věcí...
castnode = reseninode.first_child
# Úvodní odstaveček
obsah = "Tohle je zamyšlení o textu lorem ipsum. Začneme a skončíme ukázkou."
text = m.Text.objects.create(
text = Text.objects.create(
na_web=obsah,
do_cisla=obsah,
)
text.save()
create_child(reseninode, m.TextNode, text=text)
create_child(reseninode, TextNode, text=text)
# Několik odstavců lorem ipsum
for _ in range(rnd.randint(3, 7)):
lipsum = lorem.paragraph()
text = m.Text.objects.create(
text = Text.objects.create(
na_web=lipsum,
do_cisla=lipsum,
)
text.save()
create_child(castnode, m.TextNode, text=text)
create_child(castnode, TextNode, text=text)
logger.info(f"Článek vygenerován (reseni={reseni.id}, treenode={reseninode.id})")

36
tvorba/urls.py

@ -7,19 +7,19 @@ urlpatterns = [
# path('<int:rocnik>/t<int:tematko>/', views.TematkoView),
# Archiv
path('archiv/rocniky/', views.ArchivView.as_view(), name="seminar_archiv_rocniky"),
path('archiv/temata/', views.ArchivTemataView.as_view(), name="seminar_archiv_temata"),
path('archiv/rocniky/', views.ArchivView.as_view(), name="tvorba_archiv_rocniky"),
path('archiv/temata/', views.ArchivTemataView.as_view(), name="tvorba_archiv_temata"),
path('rocnik/<int:rocnik>/', views.RocnikView.as_view(), name='seminar_rocnik'),
path('cislo/<int:rocnik>.<str:cislo>/', views.CisloView.as_view(), name='seminar_cislo'),
path('problem/<int:pk>/', views.problemView, name='seminar_problem'),
path('rocnik/<int:rocnik>/', views.RocnikView.as_view(), name='tvorba_rocnik'),
path('cislo/<int:rocnik>.<str:cislo>/', views.CisloView.as_view(), name='tvorba_cislo'),
path('problem/<int:pk>/', views.problemView, name='tvorba_problem'),
# Zadani
# path('aktualni/zadani/', views.AktualniZadaniView.as_view(), name='seminar_aktualni_zadani'), # Dočasně ad-hoc jednoduchá věc.
path('aktualni/zadani/', views.AktualniZadaniView, name='seminar_aktualni_zadani'),
#path('aktualni/temata/', views.ZadaniTemataView, name='seminar_temata'),
path('aktualni/vysledkova-listina/', views.ZadaniAktualniVysledkovkaView, name='seminar_aktualni_vysledky'),
path('aktualni/rocnik/', views.AktualniRocnikRedirectView.as_view(), name='seminar_aktualni_rocnik'),
# path('aktualni/zadani/', views.AktualniZadaniView.as_view(), name='tvorba_aktualni_zadani'), # Dočasně ad-hoc jednoduchá věc.
path('aktualni/zadani/', views.AktualniZadaniView, name='tvorba_aktualni_zadani'),
#path('aktualni/temata/', views.ZadaniTemataView, name='tvorba_temata'),
path('aktualni/vysledkova-listina/', views.ZadaniAktualniVysledkovkaView, name='tvorba_aktualni_vysledky'),
path('aktualni/rocnik/', views.AktualniRocnikRedirectView.as_view(), name='tvorba_aktualni_rocnik'),
# Clanky
path('archiv/clanky/', views.ClankyResitelView.as_view(), name='clanky_resitel'),
@ -29,42 +29,42 @@ urlpatterns = [
path(
'rocnik/<int:rocnik>/vysledkovka.tex',
org_required(views.RocnikVysledkovkaView.as_view()),
name='seminar_rocnik_vysledkovka'
name='tvorba_rocnik_vysledkovka'
),
path(
'rocnik/<int:rocnik>/resitele.csv',
org_required(views.resiteleRocnikuCsvExportView),
name='seminar_rocnik_resitele_csv'
name='tvorba_rocnik_resitele_csv'
),
path(
'rocnik/<int:rocnik>/tituly.tex',
org_required(views.TitulyViewRocnik),
name='seminar_rocnik_titul'
name='tvorba_rocnik_titul'
),
path(
'rocnik/<int:rocnik>/posledni_vysledkovka.tex',
org_required(views.PosledniCisloVysledkovkaView.as_view()),
name='seminar_rocnik_posledni_vysledkovka'
name='tvorba_rocnik_posledni_vysledkovka'
),
path(
'cislo/<int:rocnik>.<str:cislo>/vysledkovka.tex',
org_required(views.CisloVysledkovkaView.as_view()),
name='seminar_cislo_vysledkovka'
name='tvorba_cislo_vysledkovka'
),
path(
'cislo/<int:rocnik>.<str:cislo>/obalky.pdf',
org_required(views.cisloObalkyView),
name='seminar_cislo_obalky'
name='tvorba_cislo_obalky'
),
path(
'cislo/<int:rocnik>.<str:cislo>/tituly.tex',
org_required(views.TitulyView),
name='seminar_cislo_titul'
name='tvorba_cislo_titul'
),
path(
'cislo/<int:trocnik>.<str:tcislo>/odmeny/<int:frocnik>.<str:fcislo>/',
org_required(views.OdmenyView.as_view()),
name="seminar_archiv_odmeny"),
name="tvorba_archiv_odmeny"),
# Dočasné & neodladěné:
path(

2
tvorba/utils.py

@ -2,7 +2,7 @@ from django.core.exceptions import ObjectDoesNotExist
import personalni.models
import seminar.models as m
import tvorba.models as m
def resi_v_rocniku(rocnik, cislo=None):

59
tvorba/views/__init__.py

@ -14,12 +14,11 @@ from django.db.models import Q, Sum, Count
from django.views.generic.base import RedirectView
from django.core.exceptions import PermissionDenied
import seminar.models as s
import seminar.models as m
from seminar.models import Problem, Cislo, Reseni, Nastaveni, Rocnik, \
Resitel, Novinky, Tema, Clanek, \
Deadline # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci
#from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva
from personalni.models import Resitel
from soustredeni.models import Konfera
from tvorba.models import Problem, Cislo, Rocnik, Tema, Clanek, Deadline, Uloha
from treenode.models import TemaVCisleNode, PohadkaNode
from various.models import Nastaveni
from treenode import treelib
import treenode.templatetags as tnltt
import treenode.serializers as vr
@ -58,7 +57,7 @@ def get_problemy_k_tematu(tema):
# FIXME: Pozor, níž je ještě jeden ProblemView!
#class ProblemView(generic.DetailView):
# model = s.Problem
# model = Problem
# # Zkopírujeme template_name od TreeNodeView, protože jsme prakticky jen trošku upravený TreeNodeView
# template_name = TreeNodeView.template_name
#
@ -70,17 +69,17 @@ def get_problemy_k_tematu(tema):
# if False:
# # Hezčí formátování zbytku :-P
# pass
# elif isinstance(self.object, s.Clanek) or isinstance(self.object, s.Konfera):
# elif isinstance(self.object, Clanek) or isinstance(self.object, Konfera):
# # Tyhle Problémy mají ŘešeníNode
# context['tnldata'] = TNLData.from_treenode(self.object.reseninode,user)
# elif isinstance(self.object, s.Uloha):
# elif isinstance(self.object, Uloha):
# # FIXME: Teď vždycky zobrazujeme i vzorák! Možná by bylo hezčí/lepší mít to stejně jako pro Téma: procházet jen dosažitelné z Ročníku / čísla / whatever
# tnl_zadani = TNLData.from_treenode(self.object.ulohazadaninode,user)
# tnl_vzorak = TNLData.from_treenode(self.object.ulohavzoraknode,user)
# context['tnldata'] = TNLData.from_tnldata_list([tnl_zadani, tnl_vzorak])
# elif isinstance(self.object, s.Tema):
# elif isinstance(self.object, Tema):
# rocniknode = self.object.rocnik.rocniknode
# context['tnldata'] = TNLData.filter_treenode(rocniknode, lambda x: isinstance(x, s.TemaVCisleNode))
# context['tnldata'] = TNLData.filter_treenode(rocniknode, lambda x: isinstance(x, TemaVCisleNode))
# else:
# raise ValueError("Obecný problém nejde zobrazit.")
# return context
@ -115,7 +114,7 @@ def ZadaniTemataView(request):
nastaveni = get_object_or_404(Nastaveni)
verejne = nastaveni.aktualni_cislo.verejne()
akt_rocnik = nastaveni.aktualni_cislo.rocnik
temata = s.Tema.objects.filter(rocnik=akt_rocnik, stav='zadany')
temata = Tema.objects.filter(rocnik=akt_rocnik, stav='zadany')
return render(request, 'tvorba/tematka/rozcestnik.html',
{
'tematka': temata,
@ -140,14 +139,14 @@ def ZadaniTemataView(request):
#
#
#def TematkoView(request, rocnik, tematko):
# nastaveni = s.Nastaveni.objects.first()
# rocnik_object = s.Rocnik.objects.filter(rocnik=rocnik)
# tematko_object = s.Tema.objects.filter(rocnik=rocnik_object[0], kod=tematko)
# nastaveni = Nastaveni.objects.first()
# rocnik_object = Rocnik.objects.filter(rocnik=rocnik)
# tematko_object = Tema.objects.filter(rocnik=rocnik_object[0], kod=tematko)
# seznam = vytahniZLesaSeznam(tematko_object[0], nastaveni.aktualni_rocnik().rocniknode)
# for node, depth in seznam:
# if node.isinstance(node, s.KonferaNode):
# if node.isinstance(node, KonferaNode): # FIXME neexistuje
# 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
#
# return render(request, 'tvorba/tematka/toaletak.html', {})
@ -155,8 +154,8 @@ def ZadaniTemataView(request):
#
#def TemataRozcestnikView(request):
# print("=============================================")
# nastaveni = s.Nastaveni.objects.first()
# tematka_objects = s.Tema.objects.filter(rocnik=nastaveni.aktualni_rocnik())
# nastaveni = Nastaveni.objects.first()
# tematka_objects = Tema.objects.filter(rocnik=nastaveni.aktualni_rocnik())
# tematka = [] #List tematka obsahuje pro kazde tematko object a list vsech TemaVCisleNodu - implementované pomocí slovníku
# for tematko_object in tematka_objects:
# print("AKTUALNI TEMATKO")
@ -278,7 +277,7 @@ def resiteleRocnikuCsvExportView(request, rocnik):
assert request.method in ('GET', 'HEAD')
return dataResiteluCsvResponse(
utils.resi_v_rocniku(
get_object_or_404(m.Rocnik, rocnik=rocnik)
get_object_or_404(Rocnik, rocnik=rocnik)
)
)
@ -291,10 +290,10 @@ def resiteleRocnikuCsvExportView(request, rocnik):
# def get_template_names(self, **kwargs):
# # FIXME: Switch podle typu není hezký, ale nechtělo se mi to přepisovat celé. Správně by se tohle mělo řešit polymorfismem.
# spravne_templaty = {
# s.Uloha: "uloha",
# s.Tema: "tema",
# s.Konfera: "konfera",
# s.Clanek: "clanek",
# Uloha: "uloha",
# Tema: "tema",
# Konfera: "konfera",
# Clanek: "clanek",
# }
# context = super().get_context_data(**kwargs)
# return ['tvorba/archiv/problem_' + spravne_templaty[context['object'].__class__] + '.html']
@ -340,10 +339,10 @@ class CisloView(generic.DetailView):
deadliny_s_vysledkovkami = []
nadpisy = {
m.Deadline.TYP_CISLA: "Výsledkovka",
m.Deadline.TYP_PRVNI: "Výsledkovka do prvního deadlinu",
m.Deadline.TYP_PRVNI_A_SOUS: "Výsledkovka do prvního deadlinu a deadlinu pro účast na soustředění",
m.Deadline.TYP_SOUS: "Výsledkovka do deadlinu pro účast na soustředění",
Deadline.TYP_CISLA: "Výsledkovka",
Deadline.TYP_PRVNI: "Výsledkovka do prvního deadlinu",
Deadline.TYP_PRVNI_A_SOUS: "Výsledkovka do prvního deadlinu a deadlinu pro účast na soustředění",
Deadline.TYP_SOUS: "Výsledkovka do deadlinu pro účast na soustředění",
}
for deadline in deadliny:
@ -577,8 +576,8 @@ class ClankyResitelView(generic.ListView):
class AktualniRocnikRedirectView(RedirectView):
permanent=False
pattern_name = 'seminar_rocnik'
pattern_name = 'tvorba_rocnik'
def get_redirect_url(self, *args, **kwargs):
aktualni_rocnik = 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)

14
tvorba/views/docasne.py

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

2
various/autentizace/templates/autentizace/login.html

@ -33,7 +33,7 @@
Ješte nejsi zaregistrován?
</h2>
<form action="{% url 'seminar_prihlaska' %}">
<form action="{% url 'personalni_prihlaska' %}">
<input type="submit" value="Registrovat" />
</form>

1
various/autentizace/views.py

@ -55,5 +55,4 @@ class PasswordResetCompleteView(auth_views.PasswordResetCompleteView):
class PasswordChangeView(auth_views.PasswordChangeView):
# template_name = 'seminar/password_change.html'
success_url = reverse_lazy('titulni_strana')

4
various/log_filters.py

@ -12,8 +12,8 @@ class StripSensitiveFormDataFilter(Filter):
if hasattr(record, 'request') and record.request.path in [
reverse('login'),
reverse('logout'),
reverse('seminar_prihlaska'),
reverse('seminar_resitel_edit'),
reverse('personalni_prihlaska'),
reverse('personalni_resitel_edit'),
reverse('reset_password'),
reverse('reset_password_done'),
# FIXME

2
various/management/commands/generate_thumbnails.py

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

4
various/management/commands/pregeneruj_zmrazene_vysledkovky.py

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

4
various/management/commands/testdata.py

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

2
various/templates/various/pracuje_se.html

@ -11,7 +11,7 @@
Na této stránce velmi intenzivně pracujeme.
Za dočasnou nedostupnost se omlouváme.
Zkuste přejít na <a href="/">titulní stránku</a>
nebo se podívat na <a href="{% url 'seminar_aktualni_zadani' %}">aktuální zadání</a>.
nebo se podívat na <a href="{% url 'tvorba_aktualni_zadani' %}">aktuální zadání</a>.
</p>
<img src="{% static '404.png' %}">
{% endblock %}

4
various/testutils.py

@ -7,7 +7,9 @@ from django.contrib.flatpages.models import FlatPage
from django.contrib.sites.models import Site
from django.db import transaction
from seminar.models import Rocnik, Cislo, Nastaveni, Osoba, Organizator
from personalni.models import Osoba, Organizator
from tvorba.models import Rocnik, Cislo
from various.models import Nastaveni
from korektury.testutils import create_test_pdf
from novinky.testutils import gen_novinky

30
various/views/final.py

@ -14,8 +14,10 @@ from django.views import generic
import novinky.views
import treenode.treelib as t
import tvorba.views
from personalni.models import Resitel
from seminar import models as m
from treenode.models import CisloNode
from personalni.models import Resitel, Osoba
from tvorba.models import Clanek, Deadline
from ..models import Nastaveni
@ -30,7 +32,7 @@ class TitulniStranaView(generic.ListView):
context = super(TitulniStranaView, self).get_context_data(**kwargs)
nastaveni = get_object_or_404(Nastaveni)
deadline = m.Deadline.objects.filter(
deadline = Deadline.objects.filter(
deadline__gte=timezone.now()).order_by("deadline").first()
context['nejblizsi_deadline'] = deadline
@ -93,31 +95,31 @@ def seznam_problemu():
# Duplicita jmen
jmena = {}
for r in m.Resitel.objects.all():
for r in Resitel.objects.all():
j = r.osoba.plne_jmeno()
if j not in jmena:
jmena[j] = []
jmena[j].append(r)
for j in jmena:
if len(jmena[j]) > 1:
prb(m.Resitel, 'Duplicitní jméno "%s"' % (j,), jmena[j])
prb(Resitel, 'Duplicitní jméno "%s"' % (j,), jmena[j])
# Data maturity a narození
for r in m.Resitel.objects.all():
for r in Resitel.objects.all():
if not r.rok_maturity:
prb(m.Resitel, 'Neznámý rok maturity', [r])
prb(Resitel, 'Neznámý rok maturity', [r])
if r.rok_maturity and (r.rok_maturity < 1990 or r.rok_maturity > datetime.date.today().year + 10):
prb(m.Resitel, 'Podezřelé datum maturity', [r])
prb(Resitel, 'Podezřelé datum maturity', [r])
if r.osoba.datum_narozeni and (
r.osoba.datum_narozeni.year < 1970 or r.osoba.datum_narozeni.year > datetime.date.today().year - 12):
prb(m.Resitel, 'Podezřelé datum narození', [r])
prb(Resitel, 'Podezřelé datum narození', [r])
# if not r.email:
# prb(Resitel, u'Neznámý email', [r])
## Kontroly konzistence databáze a TreeNodů
# Články
for clanek in m.Clanek.objects.all():
for clanek in Clanek.objects.all():
# získáme řešení svázané se článkem a z něj node ve stromě
reseni = clanek.reseni_set
if (reseni.count() != 1):
@ -127,7 +129,7 @@ def seznam_problemu():
# content type je věc pomáhající rozeznávat různé typy objektů v django-polymorphic
# protože isinstance vrátí vždy jen TreeNode
# https://django-polymorphic.readthedocs.io/en/stable/migrating.html
cislonode_ct = ContentType.objects.get_for_model(m.CisloNode)
cislonode_ct = ContentType.objects.get_for_model(CisloNode)
node = clanek_node
while node is not None:
node_ct = node.polymorphic_ctype
@ -136,7 +138,7 @@ def seznam_problemu():
# .cislonode je opačná vazba k treenode_ptr, abychom z TreeNode dostali
# CisloNode
if clanek.cislo != node.cislonode.cislo:
prb(m.Clanek, "Číslo otištění uložené u článku nesedí s "
prb(Clanek, "Číslo otištění uložené u článku nesedí s "
"číslem otištění podle struktury treenodů.", [clanek])
break
node = t.get_parent(node)
@ -146,8 +148,8 @@ def seznam_problemu():
def StavDatabazeView(request):
# nastaveni = Nastaveni.objects.get()
problemy = seznam_problemu()
muzi = Resitel.objects.filter(osoba__osloveni=m.Osoba.OSLOVENI_MUZSKE)
zeny = Resitel.objects.filter(osoba__osloveni=m.Osoba.OSLOVENI_ZENSKE)
muzi = Resitel.objects.filter(osoba__osloveni=Osoba.OSLOVENI_MUZSKE)
zeny = Resitel.objects.filter(osoba__osloveni=Osoba.OSLOVENI_ZENSKE)
return render(request, 'various/stav_databaze.html', {
# 'nastaveni': nastaveni,
'problemy': problemy,

2
various/views/pomocne.py

@ -17,7 +17,7 @@ def formularOKView(request, text='', dalsi_odkazy: Sequence[tuple[str, str]] = (
odkazy = list(dalsi_odkazy) + [
# (Text, odkaz)
('Vrátit se na titulní stránku', reverse('titulni_strana')),
('Zobrazit aktuální zadání', reverse('seminar_aktualni_zadani')),
('Zobrazit aktuální zadání', reverse('tvorba_aktualni_zadani')),
]
context = {
'odkazy': odkazy,

4
vue_frontend/vue.config.js

@ -17,9 +17,9 @@ module.exports = {
filenameHashing: false,
productionSourceMap: true,
publicPath: process.env.NODE_ENV === 'production'
? '/static/seminar/vue/'
? '/static/treenode/vue/'
: 'http://localhost:8080/',
outputDir: '../seminar/static/seminar/vue/',
outputDir: '../treenode/static/treenode/vue/',
chainWebpack: config => {

2
vyroci/views.py

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

77
vysledkovky/utils.py

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

Loading…
Cancel
Save