Browse Source

Merge branch 'master' into predelani_sousovych_view

pull/57/head
Jonas Havelka 1 week ago
parent
commit
854c902322
  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. 47
      data/sitetree.json
  8. 62
      deploy_v2/admin_org_prava.json
  9. 9
      docs/struktura.rst
  10. 2
      docs/zapisy/2021-12-06-testovani_dokumentace_codereview.md
  11. 2
      galerie/views.py
  12. 2
      make/push_compiled_vue_to_test
  13. 1
      make/schema
  14. 4
      mamweb/admin.py
  15. 4
      mamweb/settings_common.py
  16. 88
      mamweb/static/css/layout.css
  17. 731
      mamweb/static/css/mamweb_legacy.css
  18. 43
      mamweb/static/css/modules.css
  19. 11
      mamweb/templates/base.html
  20. 2
      novinky/templates/novinky/novinky.html
  21. 2
      odevzdavatko/admin.py
  22. 25
      odevzdavatko/forms.py
  23. 99
      odevzdavatko/migrations/0001_create.py
  24. 30
      odevzdavatko/migrations/0002_manage.py
  25. 13
      odevzdavatko/migrations/0003_odstrel_odevzdavatka_post.py
  26. 13
      odevzdavatko/migrations/0004_tvorba_pre.py
  27. 35
      odevzdavatko/migrations/0005_tvorba_relink.py
  28. 14
      odevzdavatko/migrations/0006_tvorba_post.py
  29. 13
      odevzdavatko/migrations/0007_odstrel_treenode_pre.py
  30. 20
      odevzdavatko/migrations/0008_odstrel_treenode_relink.py
  31. 14
      odevzdavatko/migrations/0009_odstrel_treenode_post.py
  32. 44
      odevzdavatko/models.py
  33. 2
      odevzdavatko/templates/odevzdavatko/nahraj_reseni.html
  34. 2
      odevzdavatko/templates/odevzdavatko/nahraj_reseni_nadproblem.html
  35. 2
      odevzdavatko/templates/odevzdavatko/vloz_reseni.html
  36. 4
      odevzdavatko/templatetags/barvy_reseni.py
  37. 4
      odevzdavatko/templatetags/jmena.py
  38. 2
      odevzdavatko/testutils.py
  39. 8
      odevzdavatko/urls.py
  40. 94
      odevzdavatko/views.py
  41. 2
      personalni/admin.py
  42. 8
      personalni/forms.py
  43. 13
      personalni/migrations/0012_odstrel_odevzdavatka_pre.py
  44. 14
      personalni/migrations/0013_odstrel_odevzdavatka_post.py
  45. 13
      personalni/migrations/0014_tvorba_pre.py
  46. 14
      personalni/migrations/0015_tvorba_post.py
  47. 13
      personalni/migrations/0016_odstrel_treenode_pre.py
  48. 14
      personalni/migrations/0017_odstrel_treenode_post.py
  49. 8
      personalni/models.py
  50. 4
      personalni/templates/personalni/profil/orgorozcestnik.html
  51. 6
      personalni/templates/personalni/profil/resitel.html
  52. 2
      personalni/templates/personalni/udaje/edit.html
  53. 2
      personalni/templates/personalni/udaje/prihlaska.html
  54. 8
      personalni/tests.py
  55. 6
      personalni/urls.py
  56. 6
      personalni/utils.py
  57. 46
      personalni/views.py
  58. 2
      prednasky/admin.py
  59. 3
      prednasky/views.py
  60. 6
      seminar/__init__.py
  61. 5
      seminar/migrations/0001_initial.py
  62. 21
      seminar/migrations/0001_squashed_0098_auto_20210906_0305.py
  63. 6
      seminar/migrations/0002_add_body_views.py
  64. 6
      seminar/migrations/0028_add_body_celkem_views.py
  65. 6
      seminar/migrations/0029_fix_body_celkem_views.py
  66. 4
      seminar/migrations/0031_cislo_pdf.py
  67. 5
      seminar/migrations/0032_cislo_pdf_blank_typos.py
  68. 7
      seminar/migrations/0041_konfery.py
  69. 6
      seminar/migrations/0042_auto_20161005_0847.py
  70. 5
      seminar/migrations/0081_auto_20200408_2221.py
  71. 4
      seminar/migrations/0082_auto_20200506_1951.py
  72. 5
      seminar/migrations/0100_auto_20211129_2354.py
  73. 12
      seminar/migrations/0103_deadline.py
  74. 2
      seminar/migrations/0105_odstraneni_deadlinu_cisla.py
  75. 2
      seminar/migrations/0106_remove_cislo_verejna_vysledkovka.py
  76. 14
      seminar/migrations/0131_odstrel_odevzdavatka_pre.py
  77. 29
      seminar/migrations/0132_unmanage_odevzdavatko.py
  78. 20
      seminar/migrations/0133_relink_odevzdavatko.py
  79. 50
      seminar/migrations/0134_delete_odevzdavatko.py
  80. 14
      seminar/migrations/0135_odstrel_odevzdavatka_post.py
  81. 21
      seminar/migrations/0136_tvorba_pre.py
  82. 59
      seminar/migrations/0137_tvorba_unmanage.py
  83. 150
      seminar/migrations/0138_tvorba_delete.py
  84. 14
      seminar/migrations/0139_tvorba_post.py
  85. 17
      seminar/migrations/0140_odstrel_treenode_pre.py
  86. 69
      seminar/migrations/0141_odstrel_treenode_unmanage.py
  87. 153
      seminar/migrations/0142_odstrel_treenode_delete.py
  88. 14
      seminar/migrations/0143_odstrel_treenode_post.py
  89. 43
      seminar/migrations/0144_post_odstrel_vseho.py
  90. 15
      seminar/models.py
  91. 13
      seminar/models/__init__.py
  92. 22
      seminar/models/base.py
  93. 69
      seminar/models/pomocne.py
  94. 13
      soustredeni/migrations/0004_tvorba_pre.py
  95. 28
      soustredeni/migrations/0005_tvorba_relink.py
  96. 17
      soustredeni/migrations/0006_tvorba_relink2.py
  97. 15
      soustredeni/migrations/0007_tvorba_relink3.py
  98. 34
      soustredeni/migrations/0008_tvorba_relink4.py
  99. 17
      soustredeni/migrations/0009_tvorba_relink5.py
  100. 14
      soustredeni/migrations/0010_tvorba_post.py

6
aesop/urls.py

@ -5,16 +5,16 @@ urlpatterns = [
path( path(
'aesop-export/mam-rocnik-<int:prvni_rok>.csv', 'aesop-export/mam-rocnik-<int:prvni_rok>.csv',
views.ExportRocnikView.as_view(), views.ExportRocnikView.as_view(),
name='seminar_export_rocnik' name='aesop_export_rocnik'
), ),
path( path(
'aesop-export/mam-sous-<str:datum_zacatku>.csv', 'aesop-export/mam-sous-<str:datum_zacatku>.csv',
views.ExportSousView.as_view(), views.ExportSousView.as_view(),
name='seminar_export_sous' name='aesop_export_sous'
), ),
path( path(
'aesop-export/index.csv', 'aesop-export/index.csv',
views.ExportIndexView.as_view(), 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 django.utils.encoding import force_str
from .utils import default_ovvpfile 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 vysledkovky import utils
from tvorba.utils import aktivniResitele from tvorba.utils import aktivniResitele
@ -14,10 +15,10 @@ class ExportIndexView(generic.View):
def get(self, request): def get(self, request):
ls = [] ls = []
for r in Rocnik.objects.filter(exportovat = True): 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]) ls.append(url.split('/')[-1])
for s in Soustredeni.objects.filter(exportovat = True): 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]) ls.append(url.split('/')[-1])
return HttpResponse('\n'.join(ls) + '\n', content_type='text/plain; charset=utf-8') 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.test import TestCase, tag
from django.urls import reverse from django.urls import reverse
import seminar.models as m from personalni.models import Skola
from personalni.utils import sync_skoly from personalni.utils import sync_skoly
@tag('stejny-model-na-produkci') @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""" """Testuje, že pro každého orga je jeho škola ve výsledném QuerySetu"""
for pfx, id in self.spravna_data: for pfx, id in self.spravna_data:
with self.subTest(prefix=pfx, spravne_id=id): with self.subTest(prefix=pfx, spravne_id=id):
spravna_skola = m.Skola.objects.get(id=id) spravna_skola = Skola.objects.get(id=id)
# Zeptáme se view, co si myslí # Zeptáme se view, co si myslí
resp = self.client.get(reverse('autocomplete_skola')+'?q='+pfx).json() resp = self.client.get(reverse('autocomplete_skola')+'?q='+pfx).json()
ids = [int(x['id']) for x in resp['results']] ids = [int(x['id']) for x in resp['results']]

2
api/urls.py

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

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.core import serializers as ser
from django.http import HttpResponse from django.http import HttpResponse
def exportSkolView(request): def exportSkolView(request):

47
data/sitetree.json

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

62
deploy_v2/admin_org_prava.json

@ -216,57 +216,57 @@
}, },
{ {
"codename": "add_cislo", "codename": "add_cislo",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "cislo" "ct_model": "cislo"
}, },
{ {
"codename": "change_cislo", "codename": "change_cislo",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "cislo" "ct_model": "cislo"
}, },
{ {
"codename": "delete_cislo", "codename": "delete_cislo",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "cislo" "ct_model": "cislo"
}, },
{ {
"codename": "view_cislo", "codename": "view_cislo",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "cislo" "ct_model": "cislo"
}, },
{ {
"codename": "add_clanek", "codename": "add_clanek",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "clanek" "ct_model": "clanek"
}, },
{ {
"codename": "change_clanek", "codename": "change_clanek",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "clanek" "ct_model": "clanek"
}, },
{ {
"codename": "delete_clanek", "codename": "delete_clanek",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "clanek" "ct_model": "clanek"
}, },
{ {
"codename": "view_clanek", "codename": "view_clanek",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "clanek" "ct_model": "clanek"
}, },
{ {
"codename": "add_deadline", "codename": "add_deadline",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "deadline" "ct_model": "deadline"
}, },
{ {
"codename": "change_deadline", "codename": "change_deadline",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "deadline" "ct_model": "deadline"
}, },
{ {
"codename": "view_deadline", "codename": "view_deadline",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "deadline" "ct_model": "deadline"
}, },
{ {
@ -371,22 +371,22 @@
}, },
{ {
"codename": "add_pohadka", "codename": "add_pohadka",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "pohadka" "ct_model": "pohadka"
}, },
{ {
"codename": "change_pohadka", "codename": "change_pohadka",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "pohadka" "ct_model": "pohadka"
}, },
{ {
"codename": "delete_pohadka", "codename": "delete_pohadka",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "pohadka" "ct_model": "pohadka"
}, },
{ {
"codename": "view_pohadka", "codename": "view_pohadka",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "pohadka" "ct_model": "pohadka"
}, },
{ {
@ -411,22 +411,22 @@
}, },
{ {
"codename": "add_problem", "codename": "add_problem",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "problem" "ct_model": "problem"
}, },
{ {
"codename": "change_problem", "codename": "change_problem",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "problem" "ct_model": "problem"
}, },
{ {
"codename": "delete_problem", "codename": "delete_problem",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "problem" "ct_model": "problem"
}, },
{ {
"codename": "view_problem", "codename": "view_problem",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "problem" "ct_model": "problem"
}, },
{ {
@ -441,22 +441,22 @@
}, },
{ {
"codename": "add_rocnik", "codename": "add_rocnik",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "rocnik" "ct_model": "rocnik"
}, },
{ {
"codename": "change_rocnik", "codename": "change_rocnik",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "rocnik" "ct_model": "rocnik"
}, },
{ {
"codename": "delete_rocnik", "codename": "delete_rocnik",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "rocnik" "ct_model": "rocnik"
}, },
{ {
"codename": "view_rocnik", "codename": "view_rocnik",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "rocnik" "ct_model": "rocnik"
}, },
{ {
@ -541,42 +541,42 @@
}, },
{ {
"codename": "add_tema", "codename": "add_tema",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "tema" "ct_model": "tema"
}, },
{ {
"codename": "change_tema", "codename": "change_tema",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "tema" "ct_model": "tema"
}, },
{ {
"codename": "delete_tema", "codename": "delete_tema",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "tema" "ct_model": "tema"
}, },
{ {
"codename": "view_tema", "codename": "view_tema",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "tema" "ct_model": "tema"
}, },
{ {
"codename": "add_uloha", "codename": "add_uloha",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "uloha" "ct_model": "uloha"
}, },
{ {
"codename": "change_uloha", "codename": "change_uloha",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "uloha" "ct_model": "uloha"
}, },
{ {
"codename": "delete_uloha", "codename": "delete_uloha",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "uloha" "ct_model": "uloha"
}, },
{ {
"codename": "view_uloha", "codename": "view_uloha",
"ct_app_label": "seminar", "ct_app_label": "tvorba",
"ct_model": "uloha" "ct_model": "uloha"
}, },
{ {

9
docs/struktura.rst

@ -10,11 +10,9 @@ věci jako chybové hlášky a vzhled M&M stránek (menu, patička, atd.). Aktu
i veškeré csv. i veškeré csv.
Další jsou pak jednotlivé aplikace (pokud něco hledáte, tak zřejmě chcete najít Další jsou pak jednotlivé aplikace (pokud něco hledáte, tak zřejmě chcete najít
tu aplikaci, která tomu odpovídá, respektive se k ní dostat přes url), za tu aplikaci, která tomu odpovídá, respektive se k ní dostat přes url).
zmínku stojí seminar, kde jsou takové ty věci, co zbyly. Plus jsou tam aktuálně
téměř všechny modely, protože je těžké je přesunout jinam.
**TLDR: Nevšímejte si složky data/ a souborů přímo v kořenové složce.** **TLDR: Nevšímejte si složek data/ seminar/ a souborů přímo v kořenové složce.**
Kromě věcí potřebných ke gitu, :doc:`ke spuštění <vyvoj>` a fukci djanga, Kromě věcí potřebných ke gitu, :doc:`ke spuštění <vyvoj>` a fukci djanga,
dalších drobností, lokální databáze a již zmíněných aplikací jsou tu ``data``, dalších drobností, lokální databáze a již zmíněných aplikací jsou tu ``data``,
kde je takový ten obsah webu, co by se měl dát snadno měnit (tudíž musí být v kde je takový ten obsah webu, co by se měl dát snadno měnit (tudíž musí být v
@ -22,6 +20,9 @@ databázi), tj. statické stránky, menu a obrázky v pozadí menu. Ten je třeb
měnit hlavně na produkci a sekundárně tady (může to dělat i newebař a nechcete měnit hlavně na produkci a sekundárně tady (může to dělat i newebař a nechcete
přepsat jeho práci). Vše, co nejsou aplikace je popsáno :doc:`tady <dalsi_soubory>`. přepsat jeho práci). Vše, co nejsou aplikace je popsáno :doc:`tady <dalsi_soubory>`.
Ještě je tu aplikace ``seminar/``, kde bylo původně skoro všechno, a tak nám
tam zbývá spoustu historických migrací (čehož se jen tak nezbavíme).
Základy djanga Základy djanga
-------------- --------------

2
docs/zapisy/2021-12-06-testovani_dokumentace_codereview.md

@ -116,7 +116,7 @@ Aktuálně: Jakýsi coding style zhruba existuje, není popsaný, šíří se li
- Nesmí být striktně vynucovaný - Nesmí být striktně vynucovaný
- Musel by být hodně nastavitelný - Musel by být hodně nastavitelný
- Nechceme mít kód plný `#NOQA: WTF42` - Nechceme mít kód plný `#NOQA: WTF42`
- Nejspíš vždycky bude mít false positives (`tvorba.utils.roman_numerals`) i false negatives (`seminar.models.tvorba.Cislo.posli_cislo_mailem`) - Nejspíš vždycky bude mít false positives (`tvorba.utils.roman_numerals`) i false negatives (`tvorba.models.Cislo.posli_cislo_mailem`)
- Možná dobrý sluha, ale určitě špatný pán (also: špatná zkušenost ☺) - Možná dobrý sluha, ale určitě špatný pán (also: špatná zkušenost ☺)
- __Důsledek:__ Hrozí, že těch falešných varování bude moc, čímž to ztratí smysl úplně - __Důsledek:__ Hrozí, že těch falešných varování bude moc, čímž to ztratí smysl úplně
- Potenciálně by šlo aplikovat jen lokálně na změny? - Potenciálně by šlo aplikovat jen lokálně na změny?

2
galerie/views.py

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

2
make/push_compiled_vue_to_test

@ -4,7 +4,7 @@ set -exuo pipefail
. make/lib.sh . make/lib.sh
scp vue_frontend/webpack-stats.json "$GIMLI_LOGIN:$TESTWEB/vue_frontend/" 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" " ssh "$GIMLI_LOGIN" "
set -euxo pipefail set -euxo pipefail
cd $TESTWEB cd $TESTWEB

1
make/schema

@ -5,5 +5,4 @@ set -exuo pipefail
ensure_web_installed ensure_web_installed
./manage.py graph_models seminar | dot -Tpdf > schema_seminar.pdf
./manage.py graph_models -a -g | dot -Tpdf > schema_all.pdf ./manage.py graph_models -a -g | dot -Tpdf > schema_all.pdf

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) app_dict = self._build_app_dict(request, label=app_label)
aplikace_nahore = [ aplikace_nahore = [
'seminar', 'tvorba',
'personalni', 'personalni',
'novinky', 'novinky',
'korektury', 'korektury',
@ -57,7 +57,7 @@ def get_app_list(self, request, app_label=None):
# Sort the models alphabetically within each app. # Sort the models alphabetically within each app.
for app in app_list: 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 return app_list

4
mamweb/settings_common.py

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

88
mamweb/static/css/layout.css

@ -19,10 +19,10 @@ div.kontejner {/* Ne container, aby se to netlouklo s bootstrapem. */
margin-top: var(--login-bar-height); margin-top: var(--login-bar-height);
} }
div.kontent-wrapper { & div.kontent-wrapper {
padding-bottom: var(--footer-height); padding-bottom: var(--footer-height);
div.kontent { & div.kontent {
padding: 15px 30px; padding: 15px 30px;
} }
} }
@ -67,16 +67,16 @@ div.kontejner {/* Ne container, aby se to netlouklo s bootstrapem. */
background-size: 100%; background-size: 100%;
top: 58px; top: 58px;
img.logo { & img.logo {
width: 100%; width: 100%;
filter: drop-shadow(0px 5px 5px rgba(0, 0, 0, 0.4)); filter: drop-shadow(0px 5px 5px rgba(0, 0, 0, 0.4));
} }
img.logo-mobile { & img.logo-mobile {
display: none; display: none;
} }
.no-mobile { & .no-mobile {
background-size: contain; background-size: contain;
} }
} }
@ -94,12 +94,12 @@ div.kontejner {/* Ne container, aby se to netlouklo s bootstrapem. */
filter: drop-shadow(5px 0px 5px rgba(0, 0, 0, 0.4)); filter: drop-shadow(5px 0px 5px rgba(0, 0, 0, 0.4));
padding-top: 3.5%; padding-top: 3.5%;
p.license { & p.license {
text-align: center; text-align: center;
font-weight: 400; font-weight: 400;
bottom: 0; bottom: 0;
a { & a {
color: #333; color: #333;
} }
} }
@ -139,21 +139,21 @@ div.login-bar {
padding-left: 5px; padding-left: 5px;
padding-right: 5px; padding-right: 5px;
div { & div {
display: inline; display: inline;
} }
a.LOGIN-ref-admin { & a.LOGIN-ref-admin {
display: inline; display: inline;
color: var(--barva-pozadi); color: var(--barva-pozadi);
} }
.LOGIN_napis-webarum { & .LOGIN_napis-webarum {
display: inline; display: inline;
color: var(--barva-pozadi); color: var(--barva-pozadi);
float: right; float: right;
a { & a {
color: var(--svetla-oranzova); color: var(--svetla-oranzova);
text-decoration: underline; text-decoration: underline;
} }
@ -176,7 +176,7 @@ div.login-bar {
#header { #header {
background-size: 100%; background-size: 100%;
img.logo { & img.logo {
width: 100%; width: 100%;
} }
} }
@ -202,11 +202,11 @@ div.login-bar {
top: 0; top: 0;
background-image: none; background-image: none;
img.logo { & img.logo {
display: none; display: none;
} }
img.logo-mobile { & img.logo-mobile {
display: block; display: block;
top: 0; top: 0;
left: 0; left: 0;
@ -215,7 +215,7 @@ div.login-bar {
margin-bottom: 3px; margin-bottom: 3px;
} }
.no-mobile{ & .no-mobile{
display: none; display: none;
} }
} }
@ -241,13 +241,13 @@ ul.menu {
font-variant: small-caps; font-variant: small-caps;
a { & a {
text-decoration: none; text-decoration: none;
font-weight: bold; font-weight: bold;
font-size: 105%; font-size: 105%;
} }
li { & li {
margin: 0; margin: 0;
display: inline-block; display: inline-block;
width: 16.666667%; width: 16.666667%;
@ -256,7 +256,7 @@ ul.menu {
font-size: 140%; font-size: 140%;
font-weight: 400; font-weight: 400;
>a:hover, >a:active { &>a:hover, &>a:active {
color: black; color: black;
} }
@ -265,7 +265,7 @@ ul.menu {
} }
} }
ul.submenu { & ul.submenu {
background-color: var(--hlavni-oranzova); background-color: var(--hlavni-oranzova);
margin-top: 10px; /* mezera mezi hlavním menu a submenu */ margin-top: 10px; /* mezera mezi hlavním menu a submenu */
@ -277,24 +277,24 @@ ul.menu {
z-index: 50; z-index: 50;
font-weight: 400; font-weight: 400;
li { & li {
width: auto; width: auto;
padding: 0 20px 0 20px; padding: 0 20px 0 20px;
display: inline-block; display: inline-block;
>a { &>a {
color: var(--svetla-oranzova); color: var(--svetla-oranzova);
text-decoration: none; text-decoration: none;
text-shadow: none; text-shadow: none;
:hover { &:hover {
color: black; color: black;
} }
} }
} }
} }
ul.submenu li.active>a, .parentactive ul li:first-child>a { & ul.submenu li.active>a, & .parentactive ul li:first-child>a {
color: black; color: black;
} }
} }
@ -304,7 +304,7 @@ ul.menu {
font-size: 90%; font-size: 90%;
margin-top: -7px; margin-top: -7px;
li { & li {
margin-top: 10px; /* posunutí textu hlavního menu níže */ margin-top: 10px; /* posunutí textu hlavního menu níže */
} }
} }
@ -312,7 +312,7 @@ ul.menu {
ul.submenu { ul.submenu {
margin-top: 8px; /* mezera mezi hlavním menu a submenu */ margin-top: 8px; /* mezera mezi hlavním menu a submenu */
li { & li {
margin-top: 0; /* aby se spolu s textem hlavního menu neposunoval níže i text submenu */ margin-top: 0; /* aby se spolu s textem hlavního menu neposunoval níže i text submenu */
} }
} }
@ -323,7 +323,7 @@ ul.menu {
font-size: 80%; font-size: 80%;
margin-top: -2px; margin-top: -2px;
li { & li {
margin-top: 10px; /* posunutí textu hlavního menu níže */ margin-top: 10px; /* posunutí textu hlavního menu níže */
} }
} }
@ -331,7 +331,7 @@ ul.menu {
ul.submenu { ul.submenu {
margin-top: 8px; /* mezera mezi hlavním menu a submenu */ margin-top: 8px; /* mezera mezi hlavním menu a submenu */
li { & li {
margin-top: 0; /* aby se spolu s textem hlavního menu neposunoval níže i text submenu */ margin-top: 0; /* aby se spolu s textem hlavního menu neposunoval níže i text submenu */
} }
} }
@ -354,20 +354,20 @@ ul.menu {
padding-bottom: 3px; padding-bottom: 3px;
padding-left: 12px; padding-left: 12px;
a { & a {
&:active, &:hover, &:focus { &:active, &:hover, &:focus {
text-decoration: none; text-decoration: none;
color: black; color: black;
} }
} }
ul { & ul {
list-style-type: none; list-style-type: none;
font-size: 90%; font-size: 90%;
color: black; /*černé šipky submenu*/ color: black; /*černé šipky submenu*/
li { & li {
> a { &>a {
color: black; color: black;
} }
@ -377,7 +377,7 @@ ul.menu {
} }
} }
br { & br {
display: none; display: none;
} }
} }
@ -459,13 +459,13 @@ body.suprodweb { &:before, &:after { background: red; } }
display: block; display: block;
} }
h1 { text-align: center; } & h1 { text-align: center; }
.TITULNI_STRANA_zjistit_vic{ & .TITULNI_STRANA_zjistit_vic{
text-align: center; text-align: center;
margin-bottom: 30px; margin-bottom: 30px;
hr { & hr {
display: none; display: none;
@media(max-width: 800px){ @media(max-width: 800px){
@ -474,15 +474,15 @@ body.suprodweb { &:before, &:after { background: red; } }
} }
} }
.TITULNI_STRANA_graf { & .TITULNI_STRANA_graf {
@media(max-width: 800px) { @media(max-width: 800px) {
padding-top: 40px; padding-top: 40px;
} }
.TITULNI_STRANA_graf-svg { & .TITULNI_STRANA_graf-svg {
display: flex; display: flex;
#svg-graf { & #svg-graf {
width: 100%; width: 100%;
height: auto; height: auto;
margin: 30px; margin: 30px;
@ -496,7 +496,7 @@ body.suprodweb { &:before, &:after { background: red; } }
} }
} }
.TITULNI_STRANA_obsah { & .TITULNI_STRANA_obsah {
width: 66%; width: 66%;
@media(max-width: 800px){ @media(max-width: 800px){
@ -504,7 +504,7 @@ body.suprodweb { &:before, &:after { background: red; } }
} }
} }
.TITULNI_STRANA_vitej_titulka, .TITULNI_STRANA_temata_titulka { & .TITULNI_STRANA_vitej_titulka, & .TITULNI_STRANA_temata_titulka {
width: 49%; width: 49%;
padding: 10px; padding: 10px;
display: table-cell; display: table-cell;
@ -515,7 +515,7 @@ body.suprodweb { &:before, &:after { background: red; } }
} }
} }
.TITULNI_STRANA_novinky { & .TITULNI_STRANA_novinky {
width: 33%; width: 33%;
padding: 10px; padding: 10px;
@ -540,11 +540,11 @@ div.odpocet {
.stranka_aktualni_zadani { .stranka_aktualni_zadani {
text-align: center; text-align: center;
#AKTUALNI_ZADADNI_obrazek { & #AKTUALNI_ZADADNI_obrazek {
margin-top: 15px; margin-top: 15px;
} }
div.AKTUALNI_ZADANI_termin { & div.AKTUALNI_ZADANI_termin {
text-align: center; text-align: center;
font-size: large; font-size: large;
font-weight: bold; font-weight: bold;
@ -553,7 +553,7 @@ div.odpocet {
font-size: small; font-size: small;
} }
.AKTUALNI_ZADANI_datum { & .AKTUALNI_ZADANI_datum {
color: var(--hlavni-oranzova); color: var(--hlavni-oranzova);
margin: 0; margin: 0;
} }

731
mamweb/static/css/mamweb_legacy.css

@ -0,0 +1,731 @@
@charset "utf-8"; /* vynuť utf-8 */
@supports (-webkit-touch-callout: none) and (not (offset-position: normal)) {
.button {
margin: 10px 0 10px 0;
padding: 4px 0; /*vertikální centování textu*/
text-align: center;
background-color: var(--hlavni-oranzova);
color: var(--barva-pozadi);
font-size: 150%;
font-weight: bold;
font-variant: small-caps;
filter: drop-shadow(0px 5px 5px rgba(0, 0, 0, 0.4));
}
.button:hover {
position: relative;
top: 2px;
left: 2px;
background-color: #df490e;
}
/******************/
/* Rozložení webu a jeho prvky (hlavička, menu, footer) */
/**** KONTEJNER ****/
div.kontejner { /* Ne container, aby se to netlouklo s bootstrapem. */
width: 970px;
margin: auto;
min-height: 100vh;
position: relative;
padding: 0;
}
.org-logged-in div.kontejner {
margin-top: var(--login-bar-height);
}
div.kontejner div.kontent-wrapper {
padding-bottom: var(--footer-height);
}
div.kontejner div.kontent-wrapper div.kontent {
padding: 15px 30px;
}
/* Roztáhne obsah z containeru na celou šířku obrazovky: */
.full_width {
width: 100vw;
margin-left: calc(-50vw + 485px);
}
/* Na úzkém displeji nechceme nic dělat. */
@media(max-width: 860px) {
.full_width {
margin-left: 0;
width: unset;
}
}
/*******************/
/**** HLAVIČKA ****/
#header {
position: relative;
background: url("../images/header/vikendovka.jpg") no-repeat center top; /* poměr 350:970, TODO: aby to nemuselo být přesně na pixely */
background-size: 100%;
top: 58px;
}
#header img.logo {
width: 100%;
filter: drop-shadow(0px 5px 5px rgba(0, 0, 0, 0.4));
}
#header img.logo-mobile {
display: none;
}
#header .no-mobile {
background-size: contain;
}
/******************/
/**** Footer ****/
#footer {
position: absolute;
bottom: 0;
width: 100%;
background: url("../images/mozaika-footer.svg") no-repeat top center;
height: var(--footer-height);
background-size: 100%;
filter: drop-shadow(5px 0px 5px rgba(0, 0, 0, 0.4));
padding-top: 3.5%;
}
#footer p.license {
text-align: center;
font-weight: 400;
bottom: 0;
}
#footer p.license a {
color: #333;
}
@media (max-width: 650px) {
#footer {
display: none;
}
}
p.license-mobile {
display: none;
margin-bottom: 0;
}
@media (max-width: 650px) {
p.license-mobile {
position: relative;
display: block;
font-size: 90%;
background-color: var(--hlavni-oranzova);
padding: 5%;
text-align: justify;
}
}
/****************/
/**** LOGIN BAR ****/
div.login-bar {
background: var(--orgovska-fialova);
color: var(--svetla-oranzova);
width: 100%;
position: fixed;
margin-top: calc(-1 * var(--login-bar-height));
min-height: var(--login-bar-height);
z-index: 4086;
padding-left: 5px;
padding-right: 5px;
}
div.login-bar div {
display: inline;
}
div.login-bar a.LOGIN-ref-admin {
display: inline;
color: var(--barva-pozadi);
}
div.login-bar .LOGIN_napis-webarum {
display: inline;
color: var(--barva-pozadi);
float: right;
}
div.login-bar .LOGIN_napis-webarum a {
color: var(--svetla-oranzova);
text-decoration: underline;
}
/*******************/
/* stránka přes celý displej */
@media (max-width: 970px) {
#header {
background-size: 100%;
}
#header img.logo {
width: 100%;
}
}
/* malý tablet, mobil */
@media (max-width: 650px) {
#header {
width: 100%;
top: 0;
background-image: none;
}
#header img.logo {
display: none;
}
#header img.logo-mobile {
display: block;
top: 0;
left: 0;
width: 100%;
filter: drop-shadow(0px 0 5px rgba(0, 0, 0, 0.4));
margin-bottom: 3px;
}
#header .no-mobile{
display: none;
}
}
/**** MENU ****/
ul.menu {
width: 100%;
padding: 0;
margin-top: -5px; /* posune celé menu nahoru (pak potřeba zvětšit mezeru mezi menu a submenu) */
font-variant: small-caps;
}
ul.menu a {
text-decoration: none;
font-weight: bold;
font-size: 105%;
}
ul.menu li {
margin: 0;
display: inline-block;
width: 16.666667%;
text-align: center;
font-size: 140%;
font-weight: 400;
}
ul.menu li >a:hover, >a:active {
color: black;
}
ul.menu li.active>a {
color: var(--svetla-oranzova);
}
ul.menu ul.submenu {
background-color: var(--hlavni-oranzova);
margin-top: 10px; /* mezera mezi hlavním menu a submenu */
margin-bottom: 10px;
padding-top: 10px;
padding-bottom: 5px;
filter: drop-shadow(0px 5px 5px rgba(0, 0, 0, 0.4));
z-index: 50;
font-weight: 400;
}
ul.menu ul.submenu li {
width: auto;
padding: 0 20px 0 20px;
display: inline-block;
}
ul.menu ul.submenu li >a {
color: var(--svetla-oranzova);
text-decoration: none;
text-shadow: none;
}
ul.menu ul.submenu li >a :hover {
color: black;
}
ul.menu ul.submenu li.active>a, .parentactive ul li:first-child>a {
color: black;
}
@media (max-width: 970px) {
ul.menu {
font-size: 90%;
margin-top: -7px;
}
ul.menu li {
margin-top: 10px; /* posunutí textu hlavního menu níže */
}
ul.submenu {
margin-top: 8px; /* mezera mezi hlavním menu a submenu */
}
ul.submenu li {
margin-top: 0; /* aby se spolu s textem hlavního menu neposunoval níže i text submenu */
}
}
@media(max-width: 800px) {
ul.menu {
font-size: 80%;
margin-top: -2px;
}
ul.menu li {
margin-top: 10px; /* posunutí textu hlavního menu níže */
}
ul.submenu {
margin-top: 8px; /* mezera mezi hlavním menu a submenu */
}
ul.submenu li {
margin-top: 0; /* aby se spolu s textem hlavního menu neposunoval níže i text submenu */
}
}
@media (max-width: 650px) {
ul.menu {
display: none;
}
ul.menu_mobile {
display: block;
z-index: 10;
position: sticky;
font-variant: small-caps;
font-size: 150%;
font-weight: bold;
list-style-type: none;
padding-top: 3px;
padding-bottom: 3px;
padding-left: 12px;
}
ul.menu_mobile a:active, ul.menu_mobile a:hover, ul.menu_mobile a:focus {
text-decoration: none;
color: black;
}
ul.menu_mobile ul {
list-style-type: none;
font-size: 90%;
color: black; /*černé šipky submenu*/
}
ul.menu_mobile ul li > a {
color: black;
}
ul.menu_mobile ul li::before {
content: ' \276D '; /*https://www.w3schools.com/cssref/css_entities.asp*/
}
ul.menu_mobile br {
display: none;
}
} /* konec @media */
/**************/
/**** ZBYTEK ****/
/* (konkrétní stránky) */
/* Titulní stránka */
.titulnistrana {
display: flex;
text-align: justify;
@media(max-width: 800px){
.titulnistrana {
display: block;
}
}
.titulnistrana h1 { text-align: center; }
.titulnistrana .TITULNI_STRANA_zjistit_vic {
text-align: center;
margin-bottom: 30px;
}
.titulnistrana .TITULNI_STRANA_zjistit_vic hr {
display: none;
}
@media(max-width: 800px){
.titulnistrana .TITULNI_STRANA_zjistit_vic hr {
display: flex;
}
}
@media(max-width: 800px) {
.titulnistrana .TITULNI_STRANA_graf {
padding-top: 40px;
}
}
.titulnistrana .TITULNI_STRANA_graf .TITULNI_STRANA_graf-svg {
display: flex;
}
.titulnistrana .TITULNI_STRANA_graf .TITULNI_STRANA_graf-svg #svg-graf {
width: 100%;
height: auto;
margin: 30px;
}
@media(max-width: 800px){
.titulnistrana .TITULNI_STRANA_graf .TITULNI_STRANA_graf-svg #svg-graf {
max-width: 500px;
padding: 10px;
margin: auto;
}
}
.titulnistrana .TITULNI_STRANA_obsah {
width: 66%;
}
@media(max-width: 800px){
.titulnistrana .TITULNI_STRANA_obsah {
width: 100%;
}
}
.titulnistrana .TITULNI_STRANA_vitej_titulka, .TITULNI_STRANA_temata_titulka {
width: 49%;
padding: 10px;
display: table-cell;
}
@media (max-width: 650px) {
.titulnistrana .TITULNI_STRANA_vitej_titulka, .TITULNI_STRANA_temata_titulka {
width: 100%;
display: block;
}
}
.titulnistrana .TITULNI_STRANA_novinky {
width: 33%;
padding: 10px;
}
@media(max-width: 800px){
.titulnistrana .TITULNI_STRANA_novinky {
width: 100%;
max-width: 500px;
margin: auto;
}
}
/* Stránky Aktuální ročník */
.stranka_aktualni_zadani {
text-align: center;
}
.stranka_aktualni_zadani #AKTUALNI_ZADADNI_obrazek {
margin-top: 15px;
}
.stranka_aktualni_zadani div.AKTUALNI_ZADANI_termin {
text-align: center;
font-size: large;
font-weight: bold;
}
@media (max-width: 420px) {
.stranka_aktualni_zadani div.AKTUALNI_ZADANI_termin {
font-size: small;
}
}
.stranka_aktualni_zadani .AKTUALNI_ZADANI_datum {
color: var(--hlavni-oranzova);
margin: 0;
}
}
/* Stránka Jak řešit */
.jakresit svg {
width: 33%;
padding: 10px;
filter: none;
}
@media(max-width: 860px) {
.jakresit svg {
margin: auto;
display: grid;
width: 100%;
max-width: 360px;
}
}
/**** OZNAČENÍ NE-PUBLIC ČÁSTÍ ****/
.mam-org-only {
background: var(--orgovska-svetla-fialova);
padding: 10px;
margin: 10px -10px;
border: var(--orgovska-fialova) 2px dashed;
}
.mam-org-only .mam-org-only {
border: 0;
}
.mam-org-only li {
padding: 3px 0;
margin: -2px 0;
}
/**********************************/
/**** OTÁČECÍ KARTY ****/
/* (orgové, archiv) */
.flip-card {
perspective: 1000px; /* Remove this if you don't want the 3D effect */
margin-left: auto;
margin-right: auto;
}
/* This container is needed to position the front and back side */
.flip-card .flip-card-inner {
position: relative;
width: 100%;
height: 100%;
transition: transform 0.8s;
transform-style: preserve-3d;
}
/* Do an horizontal flip when you move the mouse over the flip box container */
.flip-card:hover .flip-card-inner {
transform: rotateY(180deg);
}
/* Position the front and back side */
.flip-card .flip-card-front, .flip-card-back {
position: absolute;
width: 100%;
height: 100%;
-webkit-backface-visibility: hidden; /* Safari */
backface-visibility: hidden;
}
.flip-card div.flip-card-foto, div.flip-card-foto img {
width: 100%;
height: 100%;
/* Pokud je na přední straně něco proklikávacího (třeba celá fotka), tak na dotykových zařízeních nemůže proklikávat, aby se dalo otáčet */
@media(hover: none) {
.flip-card div.flip-card-foto, div.flip-card-foto img a { pointer-events: none; }
}
/* Style the back side */
.flip-card-back {
transform: rotateY(180deg);
padding: 10px;
padding-top: 20px;
}
}
/***********************/
/**** TABULKY ****/
/** Tabulka s čárami mezi sloupci **/
/* Např. výsledkovky */
.tabulka_oramovane_sloupce {
border: solid 2px;
}
.tabulka_oramovane_sloupce td:first-child, .tabulka_oramovane_sloupce th:first-child {
border-left: none;
border-right: solid 1px;
}
.tabulka_oramovane_sloupce td:nth-child(2), .tabulka_oramovane_sloupce th:nth-child(2) {
border-left: none;
}
.tabulka_oramovane_sloupce td, .tabulka_oramovane_sloupce th {
padding: 0.1em 0.3em;
border-left: solid 1px;
}
.tabulka_oramovane_sloupce thead th, .tabulka_oramovane_sloupce thead td {
border-bottom: solid 1px;
}
/***********************************/
/** Tabulka se střídajícími se barvami řádků **/
/* Skoro jakákoliv tabulka kromě výsledkovek */
.barevna_tabulka td th {
padding: 1px 10px 1px 10px;
}
.barevna_tabulka tbody tr:nth-child(even), thead tr {
background: var(--svetlounka-oranzova);
}
.barevna_tabulka tbody tr:nth-child(odd) {
background: var(--barva-pozadi);
}
/**********************************************/
/** Tabulka, kde první řádek a sloupec je pořád vidět **/
/* Např. tabulka odevzdaných řešení, nebo výsledkovky */
.tabulka_s_uchycenym_radkem_a_sloupcem {
/* Omezí výšku a šířku, aby bylo příjemné na scrollování a zapne scrollování */
display: block;
width: fit-content; /* display: block; roztahuje na celou šířku */
max-height: 80vh;
overflow: auto;
max-width: 90%; /* (FIXME asi není potřeba u tabulek, co nejsou na celou obrazovku) */
margin-left: 5%; /* Vystředování (FIXME není potřeba u tabulek, co nejsou na celou obrazovku) */
border-collapse: separate; /* Pokud má tabulka orámování, je potřeba ho separovat, aby dodrželo position: sticky; */
border-spacing: 0;
}
/* Uchytí první řádek */
.tabulka_s_uchycenym_radkem_a_sloupcem thead tr {
position: sticky;
top: 0;
z-index: 2;
}
/* Uchytí první sloupec */
.tabulka_s_uchycenym_radkem_a_sloupcem td:first-child, .tabulka_s_uchycenym_radkem_a_sloupcem th:first-child {
position: sticky;
left: 0;
background: inherit; /* (Snad) zneprůhlední první sloupec */
z-index: 1;
}
/*******************************************************/
/** Tabulka mající všechna ohraničení **/
.plne_ohranicena_tabulka {
border-collapse: collapse;
}
.plne_ohranicena_tabulka tr th, .plne_ohranicena_tabulka tr td {
border: 1px solid black;
padding: 1px 10px 1px 10px;
}
/***************************************/
/** Výsledkovky **/
.vysledkovka, .tabulka_oramovane_sloupce td:first-child, .vysledkovka, .tabulka_oramovane_sloupce th:first-child { position: unset; }
.vysledkovka, .tabulka_oramovane_sloupce td:nth-child(2), .vysledkovka, .tabulka_oramovane_sloupce th:nth-child(2) {
border-right: solid 1px;
position: sticky;
left: 0;
background: inherit; /* (Snad) zneprůhlední druhý sloupec */
z-index: 1;
}
.vysledkovka, .tabulka_oramovane_sloupce td:nth-child(3), .vysledkovka, .tabulka_oramovane_sloupce th:nth-child(3) {
border-left: none;
}
/*****************/
/** Tabulka mých (řešitelových) řešení **/
.moje_reseni tr th, .moje_reseni tr td {
text-align: center;
}
.moje_reseni tr td.problem { text-align: left; }
/****************************************/
/** Detail řešení **/
.bodovani>input {
width: 4em;
}
.bodovani>input::placeholder {
color: lightgray;
opacity: 1;
}
.bodovani>input::-webkit-input-placeholder { /* Edge */
color: lightgray;
}
/*******************/
/*****************/
.novinka .novinka_obrazek {
margin: 10px 0 10px 0;
width: 100%;
}
.novinka .novinka_datum {
font-weight: bold;
}
.novinka .novinka_autor {
text-align: right;
font-style: italic;
}
/**** FORMULÁŘE ****/
div.gdpr {
font-size: 6pt;
}
div.gdpr p {
font-size: 6pt;
margin-bottom: .66em;
}
/*******************/
}

43
mamweb/static/css/modules.css

@ -20,7 +20,7 @@
border: 0; border: 0;
} }
&li { & li {
padding: 3px 0; padding: 3px 0;
margin: -2px 0; margin: -2px 0;
} }
@ -57,7 +57,7 @@
margin-right: auto; margin-right: auto;
/* This container is needed to position the front and back side */ /* This container is needed to position the front and back side */
.flip-card-inner { & .flip-card-inner {
position: relative; position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -71,7 +71,7 @@
} }
/* Position the front and back side */ /* Position the front and back side */
.flip-card-front, .flip-card-back { & .flip-card-front, & .flip-card-back {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -79,18 +79,18 @@
backface-visibility: hidden; backface-visibility: hidden;
} }
div.flip-card-foto, div.flip-card-foto img { & div.flip-card-foto, & div.flip-card-foto img {
width: 100%; width: 100%;
height: 100%; height: 100%;
/* Pokud je na přední straně něco proklikávacího (třeba celá fotka), tak na dotykových zařízeních nemůže proklikávat, aby se dalo otáčet */ /* Pokud je na přední straně něco proklikávacího (třeba celá fotka), tak na dotykových zařízeních nemůže proklikávat, aby se dalo otáčet */
@media(hover: none) { @media(hover: none) {
a { pointer-events: none; } & a { pointer-events: none; }
} }
} }
/* Style the back side */ /* Style the back side */
.flip-card-back { & .flip-card-back {
transform: rotateY(180deg); transform: rotateY(180deg);
padding: 10px; padding: 10px;
padding-top: 20px; padding-top: 20px;
@ -203,7 +203,7 @@ div.org_email {
.tabulka_oramovane_sloupce { .tabulka_oramovane_sloupce {
border: solid 2px; border: solid 2px;
td, th { & td, & th {
&:first-child, &:first-child { &:first-child, &:first-child {
border-left: none; border-left: none;
border-right: solid 1px; border-right: solid 1px;
@ -217,7 +217,7 @@ div.org_email {
border-left: solid 1px; border-left: solid 1px;
} }
thead { th, td { & thead { & th, & td {
border-bottom: solid 1px; border-bottom: solid 1px;
} } } }
} }
@ -228,15 +228,15 @@ div.org_email {
/* Skoro jakákoliv tabulka kromě výsledkovek */ /* Skoro jakákoliv tabulka kromě výsledkovek */
.barevna_tabulka { .barevna_tabulka {
td th { & td th {
padding: 1px 10px 1px 10px; padding: 1px 10px 1px 10px;
} }
tbody tr:nth-child(even), thead tr { & tbody tr:nth-child(even), & thead tr {
background: var(--svetlounka-oranzova); background: var(--svetlounka-oranzova);
} }
tbody tr:nth-child(odd) { & tbody tr:nth-child(odd) {
background: var(--barva-pozadi); background: var(--barva-pozadi);
} }
} }
@ -258,14 +258,14 @@ div.org_email {
border-spacing: 0; border-spacing: 0;
/* Uchytí první řádek */ /* Uchytí první řádek */
thead tr { & thead tr {
position: sticky; position: sticky;
top: 0; top: 0;
z-index: 2; z-index: 2;
} }
/* Uchytí první sloupec */ /* Uchytí první sloupec */
td, th { &:first-child { & td, & th { &:first-child {
position: sticky; position: sticky;
left: 0; left: 0;
background: inherit; /* (Snad) zneprůhlední první sloupec */ background: inherit; /* (Snad) zneprůhlední první sloupec */
@ -290,7 +290,7 @@ div.org_email {
.plne_ohranicena_tabulka { .plne_ohranicena_tabulka {
border-collapse: collapse; border-collapse: collapse;
tr { th, td { & tr { & th, & td {
border: 1px solid black; border: 1px solid black;
padding: 1px 10px 1px 10px; padding: 1px 10px 1px 10px;
} } } }
@ -305,7 +305,7 @@ div.org_email {
/** Výsledkovky **/ /** Výsledkovky **/
.vysledkovka, .tabulka_oramovane_sloupce { .vysledkovka, .tabulka_oramovane_sloupce {
td, th { & td, & th {
&:first-child { position: unset; } &:first-child { position: unset; }
&:nth-child(2) { &:nth-child(2) {
@ -327,11 +327,11 @@ div.org_email {
/** Tabulka mých (řešitelových) řešení **/ /** Tabulka mých (řešitelových) řešení **/
.moje_reseni tr { .moje_reseni tr {
th, td { & th, & td {
text-align: center; text-align: center;
} }
td.problem { text-align: left; } & td.problem { text-align: left; }
} }
/* Různá šířka problému */ /* Různá šířka problému */
@ -378,16 +378,16 @@ div.org_email {
.novinka { .novinka {
.novinka_obrazek { & .novinka_obrazek {
margin: 10px 0 10px 0; margin: 10px 0 10px 0;
width: 100%; width: 100%;
} }
.novinka_datum { & .novinka_datum {
font-weight: bold; font-weight: bold;
} }
.novinka_autor { & .novinka_autor {
text-align: right; text-align: right;
font-style: italic; font-style: italic;
} }
@ -413,7 +413,6 @@ table#reseni.form td, table#reseni.form tr {
} }
@media(max-width: 800px) { @media(max-width: 800px) {
table#reseni.form td, table#reseni.form tr { table#reseni.form td, table#reseni.form tr {
display: inline-grid; display: inline-grid;
max-width: 300px; max-width: 300px;
@ -473,7 +472,7 @@ ul.form li{
div.gdpr { div.gdpr {
font-size: 6pt; font-size: 6pt;
p { & p {
font-size: 6pt; font-size: 6pt;
margin-bottom: .66em; margin-bottom: .66em;
} }

11
mamweb/templates/base.html

@ -8,11 +8,12 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="{% static 'images/MATFYZ_MM_barevne.svg' %}" type="image/x-icon"> <link rel="shortcut icon" href="{% static 'images/MATFYZ_MM_barevne.svg' %}" type="image/x-icon">
{% block custom_css %}{% endblock %} {% block custom_css %}{% endblock %}
<link href="{% static 'bootstrap/css/bootstrap.css' %}?version=2" rel="stylesheet"> <link href="{% static 'bootstrap/css/bootstrap.css' %}?version=3" rel="stylesheet">
<link href="{% static 'css/constants.css' %}?version=2" rel="stylesheet"> <link href="{% static 'css/constants.css' %}?version=3" rel="stylesheet">
<link href="{% static 'css/base.css' %}?version=2" rel="stylesheet"> <link href="{% static 'css/mamweb_legacy.css' %}?version=3" rel="stylesheet">
<link href="{% static 'css/layout.css' %}?version=2" rel="stylesheet"> <link href="{% static 'css/base.css' %}?version=3" rel="stylesheet">
<link href="{% static 'css/modules.css' %}?version=2" rel="stylesheet"> <link href="{% static 'css/layout.css' %}?version=3" rel="stylesheet">
<link href="{% static 'css/modules.css' %}?version=3" rel="stylesheet">
<script src="{% static 'js/jquery-1.11.1.js' %}"></script> <script src="{% static 'js/jquery-1.11.1.js' %}"></script>
<script src="{% static 'js/jquery-3.4.1.js' %}"></script> <script src="{% static 'js/jquery-3.4.1.js' %}"></script>

2
novinky/templates/novinky/novinky.html

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

2
odevzdavatko/admin.py

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

99
odevzdavatko/migrations/0001_create.py

@ -0,0 +1,99 @@
# Generated by Django 4.2.13 on 2024-10-22 22:51
from django.db import migrations, models
import django.utils.timezone
import odevzdavatko.models
def nastav_nove_contenttypes(apps, schema_editor):
ContentType = apps.get_model('contenttypes', 'ContentType')
for m in ('reseni', 'hodnoceni', 'reseni_resitele', 'prilohareseni'):
ContentType.objects.filter(app_label='seminar', model=m).update(app_label='odevzdavatko')
def nastav_stare_contenttypes(apps, schema_editor):
ContentType = apps.get_model('contenttypes', 'ContentType')
for m in ('reseni', 'hodnoceni', 'reseni_resitele', 'prilohareseni'):
ContentType.objects.filter(app_label='odevzdavatko', model=m).update(app_label='seminar')
class Migration(migrations.Migration):
initial = True
dependencies = [
('seminar', '0132_unmanage_odevzdavatko'),
]
operations = [
migrations.CreateModel(
name='Hodnoceni',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('body', models.DecimalField(blank=True, decimal_places=1, max_digits=8, null=True, verbose_name='body')),
('feedback', models.TextField(blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)', verbose_name='zpětná vazba')),
('cislo_body', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='seminar.cislo', verbose_name='číslo pro body')),
('deadline_body', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='seminar.deadline', verbose_name='deadline pro body')),
('problem', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='seminar.problem', verbose_name='problém')),
('reseni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='odevzdavatko.reseni', verbose_name='řešení')),
],
options={
'verbose_name': 'Hodnocení',
'verbose_name_plural': 'Hodnocení',
'db_table': 'seminar_hodnoceni',
'managed': False,
},
),
migrations.CreateModel(
name='PrilohaReseni',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('vytvoreno', models.DateTimeField(blank=True, default=django.utils.timezone.now, editable=False, verbose_name='vytvořeno')),
('soubor', models.FileField(upload_to=odevzdavatko.models.generate_filename, verbose_name='soubor')),
('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu', verbose_name='neveřejná poznámka')),
('res_poznamka', models.TextField(blank=True, help_text='Poznámka k příloze řešení, např. co daný soubor obsahuje', verbose_name='poznámka řešitele')),
('reseni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='prilohy', to='odevzdavatko.reseni', verbose_name='řešení')),
],
options={
'verbose_name': 'Příloha řešení',
'verbose_name_plural': 'Přílohy řešení',
'db_table': 'seminar_priloha_reseni',
'ordering': ['reseni', 'vytvoreno'],
'managed': False,
},
),
migrations.CreateModel(
name='Reseni',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('cas_doruceni', models.DateTimeField(blank=True, default=django.utils.timezone.now, verbose_name='čas_doručení')),
('forma', models.CharField(choices=[('papir', 'Papírové řešení'), ('email', 'Emailem'), ('upload', 'Upload přes web')], default='email', max_length=16, verbose_name='forma řešení')),
('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k řešení (plain text)', verbose_name='neveřejná poznámka')),
('zverejneno', models.BooleanField(default=False, help_text='Udává, zda je řešení zveřejněno', verbose_name='řešení zveřejněno')),
('problem', models.ManyToManyField(help_text='Problém', through='odevzdavatko.Hodnoceni', to='seminar.problem', verbose_name='problém')),
('resitele', models.ManyToManyField(help_text='Seznam autorů řešení', through='odevzdavatko.Reseni_Resitele', to='personalni.resitel', verbose_name='autoři řešení')),
('text_cely', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='reseni_cely_set', to='seminar.reseninode', verbose_name='Plná verze textu řešení')),
],
options={
'verbose_name': 'Řešení',
'verbose_name_plural': 'Řešení',
'db_table': 'seminar_reseni',
'ordering': ['-cas_doruceni'],
'managed': False,
},
),
migrations.CreateModel(
name='Reseni_Resitele',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('reseni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='odevzdavatko.reseni', verbose_name='řešení')),
('resitele', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='personalni.resitel', verbose_name='řešitel')),
],
options={
'verbose_name': 'Řešení řešitelů',
'verbose_name_plural': 'Řešení řešitelů',
'db_table': 'seminar_reseni_resitele',
'ordering': ['reseni', 'resitele'],
'managed': False,
},
),
migrations.RunPython(nastav_nove_contenttypes, nastav_stare_contenttypes),
]

30
odevzdavatko/migrations/0002_manage.py

@ -0,0 +1,30 @@
# Generated by Django 4.2.13 on 2024-10-23 21:07
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('odevzdavatko', '0001_create'),
('seminar', '0134_delete_odevzdavatko'),
]
operations = [
migrations.AlterModelOptions(
name='hodnoceni',
options={'verbose_name': 'Hodnocení', 'verbose_name_plural': 'Hodnocení'},
),
migrations.AlterModelOptions(
name='prilohareseni',
options={'ordering': ['reseni', 'vytvoreno'], 'verbose_name': 'Příloha řešení', 'verbose_name_plural': 'Přílohy řešení'},
),
migrations.AlterModelOptions(
name='reseni',
options={'ordering': ['-cas_doruceni'], 'verbose_name': 'Řešení', 'verbose_name_plural': 'Řešení'},
),
migrations.AlterModelOptions(
name='reseni_resitele',
options={'ordering': ['reseni', 'resitele'], 'verbose_name': 'Řešení řešitelů', 'verbose_name_plural': 'Řešení řešitelů'},
),
]

13
odevzdavatko/migrations/0003_odstrel_odevzdavatka_post.py

@ -0,0 +1,13 @@
# Generated by Django 4.2.13 on 2024-10-23 21:10
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('odevzdavatko', '0002_manage'),
]
operations = [
]

13
odevzdavatko/migrations/0004_tvorba_pre.py

@ -0,0 +1,13 @@
# Generated by Django 4.2.16 on 2024-10-30 01:06
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('odevzdavatko', '0003_odstrel_odevzdavatka_post'),
]
operations = [
]

35
odevzdavatko/migrations/0005_tvorba_relink.py

@ -0,0 +1,35 @@
# Generated by Django 4.2.16 on 2024-10-30 13:18
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('tvorba', '0001_tvorba_create'),
('odevzdavatko', '0004_tvorba_pre'),
]
operations = [
migrations.AlterField(
model_name='hodnoceni',
name='cislo_body',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='tvorba.cislo', verbose_name='číslo pro body'),
),
migrations.AlterField(
model_name='hodnoceni',
name='deadline_body',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='tvorba.deadline', verbose_name='deadline pro body'),
),
migrations.AlterField(
model_name='hodnoceni',
name='problem',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='tvorba.problem', verbose_name='problém'),
),
migrations.AlterField(
model_name='reseni',
name='problem',
field=models.ManyToManyField(help_text='Problém', through='odevzdavatko.Hodnoceni', to='tvorba.problem', verbose_name='problém'),
),
]

14
odevzdavatko/migrations/0006_tvorba_post.py

@ -0,0 +1,14 @@
# Generated by Django 4.2.16 on 2024-10-30 21:34
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('odevzdavatko', '0005_tvorba_relink'),
('tvorba', '0003_tvorba_post'),
]
operations = [
]

13
odevzdavatko/migrations/0007_odstrel_treenode_pre.py

@ -0,0 +1,13 @@
# Generated by Django 4.2.16 on 2024-11-02 19:45
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('odevzdavatko', '0006_tvorba_post'),
]
operations = [
]

20
odevzdavatko/migrations/0008_odstrel_treenode_relink.py

@ -0,0 +1,20 @@
# Generated by Django 4.2.16 on 2024-11-02 20:44
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('treenode', '0001_odstrel_treenode_create'),
('odevzdavatko', '0007_odstrel_treenode_pre'),
]
operations = [
migrations.AlterField(
model_name='reseni',
name='text_cely',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='reseni_cely_set', to='treenode.reseninode', verbose_name='Plná verze textu řešení'),
),
]

14
odevzdavatko/migrations/0009_odstrel_treenode_post.py

@ -0,0 +1,14 @@
# Generated by Django 4.2.16 on 2024-11-02 20:52
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('odevzdavatko', '0008_odstrel_treenode_relink'),
('treenode', '0003_odstrel_treenode_post'),
]
operations = [
]

44
seminar/models/odevzdavatko.py → odevzdavatko/models.py

@ -9,16 +9,14 @@ from django.urls import reverse_lazy
from django.utils import timezone from django.utils import timezone
from django.conf import settings from django.conf import settings
from seminar.models import tvorba as am from tvorba.models import Problem, Deadline, Cislo, Uloha, aux_generate_filename
from seminar.models import treenode as tm from various.models import SeminarModelBase
from seminar.models import base as bm
from odevzdavatko.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet from odevzdavatko.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet
from personalni.models import Resitel from personalni.models import Resitel
@reversion.register(ignore_duplicates=True) @reversion.register(ignore_duplicates=True)
class Reseni(bm.SeminarModelBase): class Reseni(SeminarModelBase):
class Meta: class Meta:
db_table = 'seminar_reseni' db_table = 'seminar_reseni'
@ -31,7 +29,7 @@ class Reseni(bm.SeminarModelBase):
id = models.AutoField(primary_key = True) id = models.AutoField(primary_key = True)
# Ke každé dvojici řešní a problém existuje nanejvýš jedno hodnocení, doplnění vazby. # Ke každé dvojici řešní a problém existuje nanejvýš jedno hodnocení, doplnění vazby.
problem = models.ManyToManyField(am.Problem, verbose_name='problém', help_text='Problém', problem = models.ManyToManyField(Problem, verbose_name='problém', help_text='Problém',
through='Hodnoceni') through='Hodnoceni')
resitele = models.ManyToManyField(Resitel, verbose_name='autoři řešení', resitele = models.ManyToManyField(Resitel, verbose_name='autoři řešení',
@ -51,7 +49,7 @@ class Reseni(bm.SeminarModelBase):
forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False, forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False,
default=FORMA_EMAIL) default=FORMA_EMAIL)
text_cely = models.OneToOneField('ReseniNode', verbose_name='Plná verze textu řešení', text_cely = models.OneToOneField('treenode.ReseniNode', verbose_name='Plná verze textu řešení',
blank=True, null=True, related_name="reseni_cely_set", blank=True, null=True, related_name="reseni_cely_set",
on_delete=models.PROTECT) on_delete=models.PROTECT)
@ -81,7 +79,7 @@ class Reseni(bm.SeminarModelBase):
# NOTE: Potenciální DB HOG (bez select_related) # NOTE: Potenciální DB HOG (bez select_related)
def deadline_reseni(self): def deadline_reseni(self):
return am.Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first() return Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first()
## Pravdepodobne uz nebude potreba: ## Pravdepodobne uz nebude potreba:
# def save(self, *args, **kwargs): # def save(self, *args, **kwargs):
@ -90,7 +88,7 @@ class Reseni(bm.SeminarModelBase):
# self.cislo_body = self.problem.cislo_reseni # self.cislo_body = self.problem.cislo_reseni
# super(Reseni, self).save(*args, **kwargs) # super(Reseni, self).save(*args, **kwargs)
class Hodnoceni(bm.SeminarModelBase): class Hodnoceni(SeminarModelBase):
class Meta: class Meta:
db_table = 'seminar_hodnoceni' db_table = 'seminar_hodnoceni'
verbose_name = 'Hodnocení' verbose_name = 'Hodnocení'
@ -103,16 +101,16 @@ class Hodnoceni(bm.SeminarModelBase):
body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body', body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body',
blank=True, null=True) blank=True, null=True)
cislo_body = models.ForeignKey(am.Cislo, verbose_name='číslo pro body', cislo_body = models.ForeignKey(Cislo, verbose_name='číslo pro body',
related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT) related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT)
# V ročníku < 26 nastaveno na deadline vygenerovaný pro původní cislo_body # V ročníku < 26 nastaveno na deadline vygenerovaný pro původní cislo_body
deadline_body = models.ForeignKey(am.Deadline, verbose_name='deadline pro body', deadline_body = models.ForeignKey(Deadline, verbose_name='deadline pro body',
related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT) related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT)
reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE)
problem = models.ForeignKey(am.Problem, verbose_name='problém', problem = models.ForeignKey(Problem, verbose_name='problém',
related_name='hodnoceni', on_delete=models.PROTECT) related_name='hodnoceni', on_delete=models.PROTECT)
feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)') feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)')
@ -168,7 +166,7 @@ class Hodnoceni(bm.SeminarModelBase):
@property @property
def body_neprepocitane_max(self): def body_neprepocitane_max(self):
if not isinstance(self.problem.get_real_instance(), am.Uloha): if not isinstance(self.problem.get_real_instance(), Uloha):
return None return None
return self.problem.uloha.max_body return self.problem.uloha.max_body
@ -178,12 +176,12 @@ class Hodnoceni(bm.SeminarModelBase):
def generate_filename(self, filename): def generate_filename(self, filename):
return os.path.join( return os.path.join(
settings.SEMINAR_RESENI_DIR, settings.SEMINAR_RESENI_DIR,
am.aux_generate_filename(self, filename) aux_generate_filename(self, filename)
) )
@reversion.register(ignore_duplicates=True) @reversion.register(ignore_duplicates=True)
class PrilohaReseni(bm.SeminarModelBase): class PrilohaReseni(SeminarModelBase):
class Meta: class Meta:
db_table = 'seminar_priloha_reseni' db_table = 'seminar_priloha_reseni'
@ -239,19 +237,3 @@ class Reseni_Resitele(models.Model):
def __str__(self): def __str__(self):
return '{} od {}'.format(self.reseni, self.resitel) return '{} od {}'.format(self.reseni, self.resitel)
# NOTE: Poteciální DB HOG bez select_related # NOTE: Poteciální DB HOG bez select_related
class ReseniNode(tm.TreeNode):
class Meta:
db_table = 'seminar_nodes_otistene_reseni'
verbose_name = 'Otištěné řešení (Node)'
verbose_name_plural = 'Otištěná řešení (Node)'
reseni = models.ForeignKey(Reseni,
on_delete=models.PROTECT,
verbose_name = 'reseni')
def aktualizuj_nazev(self):
self.nazev = "ReseniNode: "+str(self.reseni)
def getOdkazStr(self):
return str(self.reseni)

2
odevzdavatko/templates/odevzdavatko/nahraj_reseni.html

@ -11,7 +11,7 @@
{% endblock %} {% endblock %}
</h1> </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 %} {% csrf_token %}
<table class='form'> <table class='form'>
<tr> <tr>

2
odevzdavatko/templates/odevzdavatko/nahraj_reseni_nadproblem.html

@ -12,7 +12,7 @@
<ul> <ul>
{% for problem in object_list %} {% 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 %} {% empty %}
<li>Nelze nic odevzdávat.</li> <li>Nelze nic odevzdávat.</li>
{% endfor %} {% endfor %}

2
odevzdavatko/templates/odevzdavatko/vloz_reseni.html

@ -12,7 +12,7 @@
Vložit řešení Vložit řešení
{% endblock %} {% endblock %}
</h1> </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 %} {% csrf_token %}
{{form.as_p}} {{form.as_p}}

4
odevzdavatko/templatetags/barvy_reseni.py

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

4
odevzdavatko/templatetags/jmena.py

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

2
odevzdavatko/testutils.py

@ -1,7 +1,7 @@
import datetime import datetime
import random import random
from seminar.models.odevzdavatko import Reseni, Hodnoceni from odevzdavatko.models import Reseni, Hodnoceni
def gen_reseni_ulohy(rnd, cisla, uloha, pocet_resitelu, poradi_cisla, resitele_cisla, resitele): def gen_reseni_ulohy(rnd, cisla, uloha, pocet_resitelu, poradi_cisla, resitele_cisla, resitele):

8
odevzdavatko/urls.py

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

8
personalni/forms.py

@ -4,7 +4,7 @@ from django.contrib.auth.forms import PasswordResetForm
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.contrib.auth.models import User from django.contrib.auth.models import User
from seminar.models import Skola, Resitel, Osoba from personalni.models import Skola, Resitel, Osoba
from datetime import date from datetime import date
import logging import logging
@ -27,7 +27,7 @@ class TelInput(forms.TextInput):
class UdajeForm(forms.Form): class UdajeForm(forms.Form):
username = None 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) 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) prezdivka_resitele = forms.CharField(label='Přezdívka (veřejná)', max_length=256, required=False)
@ -62,7 +62,7 @@ class UdajeForm(forms.Form):
rok_maturity = forms.IntegerField( rok_maturity = forms.IntegerField(
label='Rok maturity', label='Rok maturity',
min_value=date.today().year, min_value=date.today().year,
max_value=date.today().year+8, max_value=date.today().year+13,
required=True, required=True,
) )
@ -147,7 +147,7 @@ class PrihlaskaForm(PasswordResetForm, UdajeForm):
class ProfileEditForm(UdajeForm): class ProfileEditForm(UdajeForm):
err_logger = logging.getLogger('seminar.edit.problem') err_logger = logging.getLogger('personalni.prihlaska.problem.edit')
username = forms.CharField( username = forms.CharField(
label='Přihlašovací jméno', label='Přihlašovací jméno',
max_length=256, max_length=256,

13
personalni/migrations/0012_odstrel_odevzdavatka_pre.py

@ -0,0 +1,13 @@
# Generated by Django 4.2.13 on 2024-10-22 22:17
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('personalni', '0011_osloveni_vsechny_choices'),
]
operations = [
]

14
personalni/migrations/0013_odstrel_odevzdavatka_post.py

@ -0,0 +1,14 @@
# Generated by Django 4.2.13 on 2024-10-23 21:10
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('personalni', '0012_odstrel_odevzdavatka_pre'),
('odevzdavatko', '0003_odstrel_odevzdavatka_post'),
]
operations = [
]

13
personalni/migrations/0014_tvorba_pre.py

@ -0,0 +1,13 @@
# Generated by Django 4.2.16 on 2024-10-30 01:07
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('personalni', '0013_odstrel_odevzdavatka_post'),
]
operations = [
]

14
personalni/migrations/0015_tvorba_post.py

@ -0,0 +1,14 @@
# Generated by Django 4.2.16 on 2024-10-30 21:35
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('personalni', '0014_tvorba_pre'),
('tvorba', '0003_tvorba_post'),
]
operations = [
]

13
personalni/migrations/0016_odstrel_treenode_pre.py

@ -0,0 +1,13 @@
# Generated by Django 4.2.16 on 2024-11-02 19:45
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('personalni', '0015_tvorba_post'),
]
operations = [
]

14
personalni/migrations/0017_odstrel_treenode_post.py

@ -0,0 +1,14 @@
# Generated by Django 4.2.16 on 2024-11-02 20:52
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('personalni', '0016_odstrel_treenode_pre'),
('treenode', '0003_odstrel_treenode_post'),
]
operations = [
]

8
personalni/models.py

@ -11,7 +11,7 @@ from django_countries.fields import CountryField
from reversion import revisions as reversion from reversion import revisions as reversion
from seminar.models.base import SeminarModelBase from various.models import SeminarModelBase
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -296,7 +296,7 @@ class Resitel(SeminarModelBase):
def vsechny_body(self): def vsechny_body(self):
"Spočítá body odjakživa." "Spočítá body odjakživa."
vsechna_reseni = self.reseni_set.all() vsechna_reseni = self.reseni_set.all()
from seminar.models.odevzdavatko import Hodnoceni from odevzdavatko.models import Hodnoceni
vsechna_hodnoceni = Hodnoceni.objects.filter( vsechna_hodnoceni = Hodnoceni.objects.filter(
reseni__in=vsechna_reseni) reseni__in=vsechna_reseni)
return sum(h.body for h in list(vsechna_hodnoceni) if h.body is not None) return sum(h.body for h in list(vsechna_hodnoceni) if h.body is not None)
@ -343,7 +343,7 @@ class Resitel(SeminarModelBase):
# - body z 25. ročníku a dříve byly shledány dvakrát hodnotnějšími # - body z 25. ročníku a dříve byly shledány dvakrát hodnotnějšími
# - proto se započítávají dvojnásobně a byly posunuté hranice titulů # - proto se započítávají dvojnásobně a byly posunuté hranice titulů
# - staré tituly se ale nemají odebrat, pokud řešitel v t.č. minulém (26.) ročníku měl titul, má ho mít pořád. # - staré tituly se ale nemají odebrat, pokud řešitel v t.č. minulém (26.) ročníku měl titul, má ho mít pořád.
from seminar.models.odevzdavatko import Hodnoceni from odevzdavatko.models import Hodnoceni
hodnoceni_do_25_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=25,reseni__in=self.reseni_set.all()) hodnoceni_do_25_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=25,reseni__in=self.reseni_set.all())
novejsi_hodnoceni = Hodnoceni.objects.filter(reseni__in=self.reseni_set.all()).difference(hodnoceni_do_25_rocniku) novejsi_hodnoceni = Hodnoceni.objects.filter(reseni__in=self.reseni_set.all()).difference(hodnoceni_do_25_rocniku)
@ -381,7 +381,7 @@ class Resitel(SeminarModelBase):
else: else:
return Titul.akad return Titul.akad
from seminar.models.odevzdavatko import Hodnoceni from odevzdavatko.models import Hodnoceni
hodnoceni_do_26_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=26,reseni__in=self.reseni_set.all()) hodnoceni_do_26_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=26,reseni__in=self.reseni_set.all())
novejsi_body = body_z_hodnoceni( novejsi_body = body_z_hodnoceni(
Hodnoceni.objects.filter(reseni__in=self.reseni_set.all()) Hodnoceni.objects.filter(reseni__in=self.reseni_set.all())

4
personalni/templates/personalni/profil/orgorozcestnik.html

@ -20,7 +20,7 @@
<h2><strong>Tvorba čísla</strong></h2> <h2><strong>Tvorba čísla</strong></h2>
<ul> <ul>
<li><a href="{% url 'admin:seminar_problem_add' %}"><strong>přidat téma</strong></a></li> <li><a href="{% url 'admin:tvorba_problem_add' %}"><strong>přidat téma</strong></a></li>
<li><strong>korektury</strong> <li><strong>korektury</strong>
<ul> <ul>
<li><a href="{% url 'korektury_list' %}">korekturování</a></li> <li><a href="{% url 'korektury_list' %}">korekturování</a></li>
@ -78,7 +78,7 @@
<li>hlasování o přednáškách</li> <li>hlasování o přednáškách</li>
</ul> </ul>
</li> </li>
<li><a href="{% url 'seminar_seznam_soustredeni' %}">proběhlá soustředění</a> <li><a href="{% url 'soustredeni_seznam' %}">proběhlá soustředění</a>
<ul> <ul>
<li>vytvoření galerie</li> <li>vytvoření galerie</li>
<li>stažení seznamu účastníků</li> <li>stažení seznamu účastníků</li>

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

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

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

@ -11,7 +11,7 @@
<hr> <hr>
<p><a href="{% url 'reset_password' %}">Změnit heslo</a></p> <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"%} {% include "personalni/udaje/udaje.html"%}
<input type="submit" value="Změnit"> <input type="submit" value="Změnit">
</form> </form>

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

@ -14,7 +14,7 @@
<p><b>Tučně</b> popsaná pole jsou povinná.</p> <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" %} {% include "personalni/udaje/udaje.html" %}
<h4> <h4>
GDPR GDPR

8
personalni/tests.py

@ -2,9 +2,9 @@ from django.test import TestCase, RequestFactory
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from django.contrib.admin.sites import AdminSite from django.contrib.admin.sites import AdminSite
from personalni.admin import OsobaAdmin from personalni.admin import OsobaAdmin, udelej_orgem
# Tohle bude peklo, až jednou ty modely fakt rozstřelíme… Možná vyrobit various.all_models, které půjdou importovat jako m? :-) # 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 import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -42,7 +42,7 @@ class DelaniOrguTest(TestCase):
# Pak orga uděláme… # Pak orga uděláme…
qs = m.Osoba.objects.filter(id=self.nova_osoba.id) qs = m.Osoba.objects.filter(id=self.nova_osoba.id)
self.admin.udelej_orgem(self.request, qs) udelej_orgem(self.admin, self.request, qs)
# A pak už to org má být. # A pak už to org má být.
self.nova_osoba.refresh_from_db() self.nova_osoba.refresh_from_db()
@ -56,7 +56,7 @@ class DelaniOrguTest(TestCase):
self.assertIsNotNone(novy_org.organizuje_od) self.assertIsNotNone(novy_org.organizuje_od)
def test_pridani_stareho_orga(self): def test_pridani_stareho_orga(self):
self.admin.udelej_orgem(self.request, m.Osoba.objects.filter(id=self.stary_org.osoba.id)) # Ugly udelej_orgem(self.admin, self.request, m.Osoba.objects.filter(id=self.stary_org.osoba.id)) # Ugly
# Když to spadne, tak jsem se to dozvěděl, takže už nepotřebuju nic kontrolovat. # Když to spadne, tak jsem se to dozvěděl, takže už nepotřebuju nic kontrolovat.
# Jestli to funguje správně má řešit jiný test. # Jestli to funguje správně má řešit jiný test.

6
personalni/urls.py

@ -7,15 +7,15 @@ urlpatterns = [
path( path(
'org/rozcestnik/', 'org/rozcestnik/',
org_required(views.OrgoRozcestnikView.as_view()), 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( path(
'resitel/osobni-udaje/', 'resitel/osobni-udaje/',
login_required(views.resitelEditView), 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 # 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 from various.utils import bez_diakritiky_translate
import re 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.contrib.auth.models import AnonymousUser
from django.db import transaction from django.db import transaction
import seminar.models as m
import soustredeni.models import soustredeni.models
from odevzdavatko.models import Reseni_Resitele
from .models import Osoba, Organizator, Skola, Resitel, Prijemce 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 # Přepojit všechny vazby ze zdrojového na cílového
print('Přepojuji vazby') print('Přepojuji vazby')
# Vazby: Škola (hotovo), Řešení_Řešitelé, Konfery_Účastníci, Soustředění_Účastníci, Osoba (vyřeší se později, nejde přepojit) # Vazby: Škola (hotovo), Řešení_Řešitelé, Konfery_Účastníci, Soustředění_Účastníci, Osoba (vyřeší se později, nejde přepojit)
ct = m.Reseni_Resitele.objects.filter(resitele=zdrojovy).update(resitele=cilovy) ct = Reseni_Resitele.objects.filter(resitele=zdrojovy).update(resitele=cilovy)
print(f' Přepojeno {ct} řešení') print(f' Přepojeno {ct} řešení')
ct = soustredeni.models.Konfery_Ucastnici.objects.filter(resitel=zdrojovy).update(resitel=cilovy) ct = soustredeni.models.Konfery_Ucastnici.objects.filter(resitel=zdrojovy).update(resitel=cilovy)
print(f' Přepojeno {ct} konfer') 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.http import HttpResponse
from django.utils import timezone 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 .forms import PrihlaskaForm, ProfileEditForm, PoMaturiteProfileEditForm
from datetime import date from datetime import date
@ -91,22 +95,22 @@ class OrgoRozcestnikView(TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['posledni_soustredeni'] = s.Soustredeni.objects.order_by('-datum_konce').first() context['posledni_soustredeni'] = Soustredeni.objects.order_by('-datum_konce').first()
nastaveni = s.Nastaveni.objects.first() nastaveni = Nastaveni.objects.first()
aktualni_rocnik = nastaveni.aktualni_rocnik aktualni_rocnik = nastaveni.aktualni_rocnik
context['posledni_cislo_url'] = nastaveni.aktualni_cislo.verejne_url() context['posledni_cislo_url'] = nastaveni.aktualni_cislo.verejne_url()
# TODO možná chceme odkazovat na právě rozpracované číslo, a ne to poslední vydané # TODO možná chceme odkazovat na právě rozpracované číslo, a ne to poslední vydané
# pokud nechceme haluzit kód (= poradi) dalšího čísla, bude asi potřeba jít # pokud nechceme haluzit kód (= poradi) dalšího čísla, bude asi potřeba jít
# přes treenody (a dát si přitom pozor na MezicisloNode) # přes treenody (a dát si přitom pozor na MezicisloNode)
neobodovana_reseni = s.Hodnoceni.objects.filter(body__isnull=True) neobodovana_reseni = Hodnoceni.objects.filter(body__isnull=True)
reseni_mimo_cislo = s.Hodnoceni.objects.filter(deadline_body__isnull=True) reseni_mimo_cislo = Hodnoceni.objects.filter(deadline_body__isnull=True)
context['pocet_neobodovanych_reseni'] = neobodovana_reseni.count() context['pocet_neobodovanych_reseni'] = neobodovana_reseni.count()
context['pocet_reseni_mimo_cislo'] = reseni_mimo_cislo.count() context['pocet_reseni_mimo_cislo'] = reseni_mimo_cislo.count()
u = self.request.user u = self.request.user
os = s.Osoba.objects.get(user=u) os = m.Osoba.objects.get(user=u)
organizator = s.Organizator.objects.get(osoba=os) 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_neobodovanych_reseni'] = neobodovana_reseni.filter(Q(problem__garant=organizator) | Q(problem__autor=organizator) | Q(problem__opravovatele__in=[organizator])).distinct().count()
context['muj_pocet_reseni_mimo_cislo'] = reseni_mimo_cislo.filter(Q(problem__garant=organizator) | Q(problem__autor=organizator) | Q(problem__opravovatele__in=[organizator])).count() context['muj_pocet_reseni_mimo_cislo'] = reseni_mimo_cislo.filter(Q(problem__garant=organizator) | Q(problem__autor=organizator) | Q(problem__opravovatele__in=[organizator])).count()
@ -116,11 +120,11 @@ class OrgoRozcestnikView(TemplateView):
context["pocty_neopravenych_reseni"] = [(it['problem__nazev'], it['cas'].date) for it in pocty_neopravenych_reseni.all()] context["pocty_neopravenych_reseni"] = [(it['problem__nazev'], it['cas'].date) for it in pocty_neopravenych_reseni.all()]
#FIXME: přidat stav='STAV_ZADANY' #FIXME: přidat stav='STAV_ZADANY'
temata = s.Tema.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]), temata = Tema.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]),
rocnik=aktualni_rocnik).distinct() rocnik=aktualni_rocnik).distinct()
ulohy = s.Uloha.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]), ulohy = Uloha.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]),
cislo_zadani__rocnik=aktualni_rocnik).distinct() cislo_zadani__rocnik=aktualni_rocnik).distinct()
clanky = s.Clanek.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]), clanky = Clanek.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]),
cislo__rocnik=aktualni_rocnik).distinct() cislo__rocnik=aktualni_rocnik).distinct()
context['temata'] = temata context['temata'] = temata
@ -134,12 +138,12 @@ class OrgoRozcestnikView(TemplateView):
class ResitelView(LoginRequiredMixin,generic.DetailView): class ResitelView(LoginRequiredMixin,generic.DetailView):
model = s.Resitel model = m.Resitel
template_name = 'personalni/profil/resitel.html' template_name = 'personalni/profil/resitel.html'
def get_object(self, queryset=None): def get_object(self, queryset=None):
print(self.request.user) print(self.request.user)
return s.Resitel.objects.get(osoba__user=self.request.user) return m.Resitel.objects.get(osoba__user=self.request.user)
### Formulare ### 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') @sensitive_post_parameters('jmeno', 'prijmeni', 'email', 'telefon', 'datum_narozeni', 'ulice', 'mesto', 'psc', 'skola')
def resitelEditView(request): 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 ## Načtení objektů Osoba a Resitel patřících k aktuálně přihlášenému uživateli
u = request.user u = request.user
osoba_edit = s.Osoba.objects.get(user=u) osoba_edit = m.Osoba.objects.get(user=u)
if hasattr(osoba_edit,'resitel'): if hasattr(osoba_edit,'resitel'):
resitel_edit = osoba_edit.resitel resitel_edit = osoba_edit.resitel
else: else:
@ -195,7 +199,7 @@ def resitelEditView(request):
## Změny v osobě ## Změny v osobě
fcd = form.cleaned_data fcd = form.cleaned_data
form_hash = hash(frozenset(fcd.items())) 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 form_logger.info("EDIT:" + str(fcd) + str(form_hash)) # TODO možná logovat jinak
osoba_edit.jmeno = fcd['jmeno'] osoba_edit.jmeno = fcd['jmeno']
osoba_edit.prijmeni = fcd['prijmeni'] 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') @sensitive_post_parameters('jmeno', 'prijmeni', 'email', 'telefon', 'datum_narozeni', 'ulice', 'mesto', 'psc', 'skola', 'jak_se_dozvedeli')
def prihlaskaView(request): def prihlaskaView(request):
generic_logger = logging.getLogger('seminar.prihlaska') generic_logger = logging.getLogger('personalni.prihlaska')
err_logger = logging.getLogger('seminar.prihlaska.problem') err_logger = logging.getLogger('personalni.prihlaska.problem')
form_logger = logging.getLogger('seminar.prihlaska.form') form_logger = logging.getLogger('personalni.prihlaska.form')
if request.method == 'POST': if request.method == 'POST':
form = PrihlaskaForm(request.POST) form = PrihlaskaForm(request.POST)
# TODO vyresit, co se bude v jakych situacich zobrazovat # 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() resitel_grp = Group.objects.filter(name__exact='resitel').first()
u.groups.add(resitel_grp) u.groups.add(resitel_grp)
o = s.Osoba( o = m.Osoba(
jmeno = fcd['jmeno'], jmeno = fcd['jmeno'],
prijmeni = fcd['prijmeni'], prijmeni = fcd['prijmeni'],
osloveni = fcd['osloveni'], osloveni = fcd['osloveni'],
@ -328,7 +332,7 @@ def prihlaskaView(request):
if kolize.count() > 1: # Jednu z nich jsme právě uložili if kolize.count() > 1: # Jednu z nich jsme právě uložili
err_logger.warning(f'Zaregistrovala se osoba s kolizním jménem. ID osob: {[o.id for o in kolize]}') err_logger.warning(f'Zaregistrovala se osoba s kolizním jménem. ID osob: {[o.id for o in kolize]}')
r = s.Resitel( r = m.Resitel(
prezdivka_resitele=fcd['prezdivka_resitele'] if fcd['prezdivka_resitele'] != "" else None, prezdivka_resitele=fcd['prezdivka_resitele'] if fcd['prezdivka_resitele'] != "" else None,
rok_maturity = fcd['rok_maturity'], rok_maturity = fcd['rok_maturity'],
zasilat = fcd['zasilat'], zasilat = fcd['zasilat'],

2
prednasky/admin.py

@ -5,7 +5,7 @@ from django.utils.safestring import mark_safe
from django.utils.html import escape from django.utils.html import escape
from .models import Prednaska, Seznam, STAV_NAVRH from .models import Prednaska, Seznam, STAV_NAVRH
from seminar.models import Soustredeni from soustredeni.models import Soustredeni
class Seznam_PrednaskaInline(admin.TabularInline): 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 django.forms import Form
from prednasky.models import Prednaska, Hlasovani, Seznam, STAV_NAVRH from prednasky.models import Prednaska, Hlasovani, Seznam, STAV_NAVRH
from seminar.models import Soustredeni, Osoba from soustredeni.models import Soustredeni
from personalni.models import Osoba
def newPrednaska(request): def newPrednaska(request):
# hlasovani se vztahuje k nejnovejsimu soustredeni # hlasovani se vztahuje k nejnovejsimu soustredeni

6
seminar/__init__.py

@ -1,6 +1,4 @@
""" """
Zde bývalo vše. Teď tu zbývají všechny modely a části webu jako archiv, Zde bývalo vše. Teď tu zbývají migrace.
přehled orgů, aktuální (k aktuálnímu číslu) věci, titulka a jak řešit. A kód pro `import seminar.models as m` pro ./manage.py shell.
Také je tu generování testovacích (lokálních) dat.
""" """

5
seminar/migrations/0001_initial.py

@ -2,10 +2,11 @@ from __future__ import unicode_literals
from django.db import models, migrations from django.db import models, migrations
import django_countries.fields import django_countries.fields
import seminar.models
import django.utils.timezone import django.utils.timezone
from django.conf import settings from django.conf import settings
from odevzdavatko.models import generate_filename
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -75,7 +76,7 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(serialize=False, primary_key=True)), ('id', models.AutoField(serialize=False, primary_key=True)),
('timestamp', models.DateTimeField(auto_now=True, verbose_name='vytvo\u0159eno')), ('timestamp', models.DateTimeField(auto_now=True, verbose_name='vytvo\u0159eno')),
('soubor', models.FileField(upload_to=seminar.models.generate_filename, verbose_name='soubor')), ('soubor', models.FileField(upload_to=generate_filename, verbose_name='soubor')),
('poznamka', models.TextField(help_text='Neve\u0159ejn\xe1 pozn\xe1mka k p\u0159\xedloze \u0159e\u0161en\xed (plain text), nap\u0159. o p\u016fvodu', verbose_name='neve\u0159ejn\xe1 pozn\xe1mka', blank=True)), ('poznamka', models.TextField(help_text='Neve\u0159ejn\xe1 pozn\xe1mka k p\u0159\xedloze \u0159e\u0161en\xed (plain text), nap\u0159. o p\u016fvodu', verbose_name='neve\u0159ejn\xe1 pozn\xe1mka', blank=True)),
], ],
options={ options={

21
seminar/migrations/0001_squashed_0098_auto_20210906_0305.py

@ -7,9 +7,12 @@ import django.db.models.deletion
import django.utils.timezone import django.utils.timezone
import django_countries.fields import django_countries.fields
import imagekit.models.fields import imagekit.models.fields
import seminar.models
import taggit.managers import taggit.managers
from soustredeni.models import generate_filename_konfera
from odevzdavatko.models import generate_filename
from tvorba.models import cislo_pdf_filename, cislo_png_filename
from datetime import date from datetime import date
from django.db.models import Q from django.db.models import Q
from treenode.treelib import get_parent from treenode.treelib import get_parent
@ -962,7 +965,7 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(primary_key=True, serialize=False)), ('id', models.AutoField(primary_key=True, serialize=False)),
('timestamp', models.DateTimeField(auto_now=True, verbose_name='vytvořeno')), ('timestamp', models.DateTimeField(auto_now=True, verbose_name='vytvořeno')),
('soubor', models.FileField(upload_to=seminar.models.generate_filename, verbose_name='soubor')), ('soubor', models.FileField(upload_to=generate_filename, verbose_name='soubor')),
('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu', verbose_name='neveřejná poznámka')), ('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu', verbose_name='neveřejná poznámka')),
('reseni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='prilohy', to='seminar.Reseni', verbose_name='řešení')), ('reseni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='prilohy', to='seminar.Reseni', verbose_name='řešení')),
], ],
@ -1284,7 +1287,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='cislo', model_name='cislo',
name='pdf', name='pdf',
field=models.FileField(blank=True, help_text='Pdf čísla, které si mohou řešitelé stáhnout', null=True, upload_to=seminar.models.cislo_pdf_filename, verbose_name='pdf'), field=models.FileField(blank=True, help_text='Pdf čísla, které si mohou řešitelé stáhnout', null=True, upload_to=cislo_pdf_filename, verbose_name='pdf'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='problem', model_name='problem',
@ -1361,8 +1364,8 @@ class Migration(migrations.Migration):
('abstrakt', models.TextField(blank=True, help_text='Abstrakt konfery tak, jak byl uveden ve sborníku', verbose_name='abstrakt')), ('abstrakt', models.TextField(blank=True, help_text='Abstrakt konfery tak, jak byl uveden ve sborníku', verbose_name='abstrakt')),
('org_poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka ke konfeře(plain text)', verbose_name='neveřejná poznámka')), ('org_poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka ke konfeře(plain text)', verbose_name='neveřejná poznámka')),
('typ_prezentace', models.CharField(choices=[(b'veletrh', 'Veletrh (postery)'), (b'prezentace', 'Prezentace (přednáška)')], default=b'veletrh', max_length=16, verbose_name='typ prezentace')), ('typ_prezentace', models.CharField(choices=[(b'veletrh', 'Veletrh (postery)'), (b'prezentace', 'Prezentace (přednáška)')], default=b'veletrh', max_length=16, verbose_name='typ prezentace')),
('prezentace', models.FileField(help_text='Prezentace nebo fotka posteru', upload_to=seminar.models.generate_filename_konfera, verbose_name='prezentace')), ('prezentace', models.FileField(help_text='Prezentace nebo fotka posteru', upload_to=generate_filename_konfera, verbose_name='prezentace')),
('materialy', models.FileField(help_text='Další materiály ke konfeře zabalené do jednoho souboru', upload_to=seminar.models.generate_filename_konfera, verbose_name='materialy')), ('materialy', models.FileField(help_text='Další materiály ke konfeře zabalené do jednoho souboru', upload_to=generate_filename_konfera, verbose_name='materialy')),
('organizator', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='konfery', to='seminar.Organizator', verbose_name='organizátor')), ('organizator', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='konfery', to='seminar.Organizator', verbose_name='organizátor')),
('soustredeni', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='konfery', to='seminar.Soustredeni', verbose_name='soustředění')), ('soustredeni', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='konfery', to='seminar.Soustredeni', verbose_name='soustředění')),
], ],
@ -1400,12 +1403,12 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='konfera', model_name='konfera',
name='materialy', name='materialy',
field=models.FileField(blank=True, help_text='Další materiály ke konfeře zabalené do jednoho souboru', upload_to=seminar.models.generate_filename_konfera, verbose_name='materialy'), field=models.FileField(blank=True, help_text='Další materiály ke konfeře zabalené do jednoho souboru', upload_to=generate_filename_konfera, verbose_name='materialy'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='konfera', model_name='konfera',
name='prezentace', name='prezentace',
field=models.FileField(blank=True, help_text='Prezentace nebo fotka posteru', upload_to=seminar.models.generate_filename_konfera, verbose_name='prezentace'), field=models.FileField(blank=True, help_text='Prezentace nebo fotka posteru', upload_to=generate_filename_konfera, verbose_name='prezentace'),
), ),
migrations.AddField( migrations.AddField(
model_name='konfera', model_name='konfera',
@ -2648,12 +2651,12 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='cislo', model_name='cislo',
name='pdf', name='pdf',
field=models.FileField(blank=True, help_text='PDF čísla, které si mohou řešitelé stáhnout', null=True, upload_to=seminar.models.cislo_pdf_filename, verbose_name='pdf'), field=models.FileField(blank=True, help_text='PDF čísla, které si mohou řešitelé stáhnout', null=True, upload_to=cislo_pdf_filename, verbose_name='pdf'),
), ),
migrations.AddField( migrations.AddField(
model_name='cislo', model_name='cislo',
name='titulka_nahled', name='titulka_nahled',
field=models.ImageField(blank=True, help_text='Obrázek titulní strany, generuje se automaticky', null=True, upload_to=seminar.models.cislo_png_filename, verbose_name='Obrázek titulní strany'), field=models.ImageField(blank=True, help_text='Obrázek titulní strany, generuje se automaticky', null=True, upload_to=cislo_png_filename, verbose_name='Obrázek titulní strany'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='treenode', model_name='treenode',

6
seminar/migrations/0002_add_body_views.py

@ -1,10 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models, migrations from django.db import migrations
import django_countries.fields
import seminar.models
import django.utils.timezone
from django.conf import settings
CREATE_VIEWS=""" CREATE_VIEWS="""
create view seminar_body_za_cislo as create view seminar_body_za_cislo as

6
seminar/migrations/0028_add_body_celkem_views.py

@ -1,10 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models, migrations from django.db import migrations
import django_countries.fields
import seminar.models
import django.utils.timezone
from django.conf import settings
CREATE_VIEWS=""" CREATE_VIEWS="""
drop view seminar_body_k_cislu; drop view seminar_body_k_cislu;

6
seminar/migrations/0029_fix_body_celkem_views.py

@ -1,10 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models, migrations from django.db import migrations
import django_countries.fields
import seminar.models
import django.utils.timezone
from django.conf import settings
CREATE_VIEWS=""" CREATE_VIEWS="""

4
seminar/migrations/0031_cislo_pdf.py

@ -1,7 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models, migrations from django.db import models, migrations
import seminar.models from tvorba.models import cislo_pdf_filename
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -14,7 +14,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='cislo', model_name='cislo',
name='pdf', name='pdf',
field=models.FileField(help_text='Pdf \u010d\xedsla, kter\xe9 si mohou \u0159e\u0161itel\xe9 st\xe1hnout', upload_to=seminar.models.cislo_pdf_filename, null=True, verbose_name='pdf'), field=models.FileField(help_text='Pdf \u010d\xedsla, kter\xe9 si mohou \u0159e\u0161itel\xe9 st\xe1hnout', upload_to=cislo_pdf_filename, null=True, verbose_name='pdf'),
preserve_default=True, preserve_default=True,
), ),
] ]

5
seminar/migrations/0032_cislo_pdf_blank_typos.py

@ -2,7 +2,8 @@ from __future__ import unicode_literals
from django.db import models, migrations from django.db import models, migrations
import django_countries.fields import django_countries.fields
import seminar.models
from tvorba.models import cislo_pdf_filename
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -25,7 +26,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='cislo', model_name='cislo',
name='pdf', name='pdf',
field=models.FileField(help_text='Pdf \u010d\xedsla, kter\xe9 si mohou \u0159e\u0161itel\xe9 st\xe1hnout', upload_to=seminar.models.cislo_pdf_filename, null=True, verbose_name='pdf', blank=True), field=models.FileField(help_text='Pdf \u010d\xedsla, kter\xe9 si mohou \u0159e\u0161itel\xe9 st\xe1hnout', upload_to=cislo_pdf_filename, null=True, verbose_name='pdf', blank=True),
preserve_default=True, preserve_default=True,
), ),
migrations.AlterField( migrations.AlterField(

7
seminar/migrations/0041_konfery.py

@ -2,7 +2,8 @@ from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import seminar.models
from soustredeni.models import generate_filename_konfera
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -21,8 +22,8 @@ class Migration(migrations.Migration):
('abstrakt', models.TextField(help_text='Abstrakt konfery tak, jak byl uveden ve sborn\xedku', verbose_name='abstrakt', blank=True)), ('abstrakt', models.TextField(help_text='Abstrakt konfery tak, jak byl uveden ve sborn\xedku', verbose_name='abstrakt', blank=True)),
('org_poznamka', models.TextField(help_text='Neve\u0159ejn\xe1 pozn\xe1mka ke konfe\u0159e(plain text)', verbose_name='neve\u0159ejn\xe1 pozn\xe1mka', blank=True)), ('org_poznamka', models.TextField(help_text='Neve\u0159ejn\xe1 pozn\xe1mka ke konfe\u0159e(plain text)', verbose_name='neve\u0159ejn\xe1 pozn\xe1mka', blank=True)),
('typ_prezentace', models.CharField(default=b'veletrh', max_length=16, verbose_name='typ prezentace', choices=[(b'veletrh', 'Veletrh (postery)'), (b'prezentace', 'Prezentace (p\u0159edn\xe1\u0161ka)')])), ('typ_prezentace', models.CharField(default=b'veletrh', max_length=16, verbose_name='typ prezentace', choices=[(b'veletrh', 'Veletrh (postery)'), (b'prezentace', 'Prezentace (p\u0159edn\xe1\u0161ka)')])),
('prezentace', models.FileField(help_text='Prezentace nebo fotka posteru', upload_to=seminar.models.generate_filename_konfera, verbose_name='prezentace')), ('prezentace', models.FileField(help_text='Prezentace nebo fotka posteru', upload_to=generate_filename_konfera, verbose_name='prezentace')),
('materialy', models.FileField(help_text='Dal\u0161\xed materi\xe1ly ke konfe\u0159e zabalen\xe9 do jednoho souboru', upload_to=seminar.models.generate_filename_konfera, verbose_name='materialy')), ('materialy', models.FileField(help_text='Dal\u0161\xed materi\xe1ly ke konfe\u0159e zabalen\xe9 do jednoho souboru', upload_to=generate_filename_konfera, verbose_name='materialy')),
('organizator', models.ForeignKey(related_name='konfery', on_delete=django.db.models.deletion.SET_NULL, verbose_name='organiz\xe1tor', to='seminar.Organizator', null=True)), ('organizator', models.ForeignKey(related_name='konfery', on_delete=django.db.models.deletion.SET_NULL, verbose_name='organiz\xe1tor', to='seminar.Organizator', null=True)),
], ],
options={ options={

6
seminar/migrations/0042_auto_20161005_0847.py

@ -2,7 +2,7 @@ from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import seminar.models from soustredeni.models import generate_filename_konfera
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -15,12 +15,12 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='konfera', model_name='konfera',
name='materialy', name='materialy',
field=models.FileField(help_text='Dal\u0161\xed materi\xe1ly ke konfe\u0159e zabalen\xe9 do jednoho souboru', upload_to=seminar.models.generate_filename_konfera, verbose_name='materialy', blank=True), field=models.FileField(help_text='Dal\u0161\xed materi\xe1ly ke konfe\u0159e zabalen\xe9 do jednoho souboru', upload_to=generate_filename_konfera, verbose_name='materialy', blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='konfera', model_name='konfera',
name='prezentace', name='prezentace',
field=models.FileField(help_text='Prezentace nebo fotka posteru', upload_to=seminar.models.generate_filename_konfera, verbose_name='prezentace', blank=True), field=models.FileField(help_text='Prezentace nebo fotka posteru', upload_to=generate_filename_konfera, verbose_name='prezentace', blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='konfera', model_name='konfera',

5
seminar/migrations/0081_auto_20200408_2221.py

@ -1,7 +1,8 @@
# Generated by Django 2.2.9 on 2020-04-08 20:21 # Generated by Django 2.2.9 on 2020-04-08 20:21
from django.db import migrations, models from django.db import migrations, models
import seminar.models
from tvorba.models import cislo_pdf_filename
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -19,6 +20,6 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='cislo', model_name='cislo',
name='pdf', name='pdf',
field=models.FileField(blank=True, help_text='PDF čísla, které si mohou řešitelé stáhnout', null=True, upload_to=seminar.models.cislo_pdf_filename, verbose_name='pdf'), field=models.FileField(blank=True, help_text='PDF čísla, které si mohou řešitelé stáhnout', null=True, upload_to=cislo_pdf_filename, verbose_name='pdf'),
), ),
] ]

4
seminar/migrations/0082_auto_20200506_1951.py

@ -1,7 +1,7 @@
# Generated by Django 2.2.12 on 2020-05-06 17:51 # Generated by Django 2.2.12 on 2020-05-06 17:51
from django.db import migrations, models from django.db import migrations, models
import seminar.models from tvorba.models import cislo_png_filename
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -14,6 +14,6 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='cislo', model_name='cislo',
name='titulka_nahled', name='titulka_nahled',
field=models.ImageField(blank=True, help_text='Obrázek titulní strany, generuje se automaticky', null=True, upload_to=seminar.models.cislo_png_filename, verbose_name='Obrázek titulní strany'), field=models.ImageField(blank=True, help_text='Obrázek titulní strany, generuje se automaticky', null=True, upload_to=cislo_png_filename, verbose_name='Obrázek titulní strany'),
), ),
] ]

5
seminar/migrations/0100_auto_20211129_2354.py

@ -1,7 +1,8 @@
# Generated by Django 2.2.24 on 2021-11-29 22:54 # Generated by Django 2.2.24 on 2021-11-29 22:54
from django.db import migrations, models from django.db import migrations, models
import seminar.models.tvorba import various.models
from tvorba.models import cislo_pdf_filename
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -14,6 +15,6 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='cislo', model_name='cislo',
name='pdf', name='pdf',
field=models.FileField(blank=True, help_text='PDF čísla, které si mohou řešitelé stáhnout', null=True, storage=seminar.models.tvorba.OverwriteStorage(), upload_to=seminar.models.tvorba.cislo_pdf_filename, verbose_name='pdf'), field=models.FileField(blank=True, help_text='PDF čísla, které si mohou řešitelé stáhnout', null=True, storage=various.models.OverwriteStorage(), upload_to=cislo_pdf_filename, verbose_name='pdf'),
), ),
] ]

12
seminar/migrations/0103_deadline.py

@ -5,7 +5,7 @@ from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
from django.utils import timezone from django.utils import timezone
import seminar.models as m from tvorba.models import Deadline as mDeadline
def vytvor_deadliny(apps, schema_editor): def vytvor_deadliny(apps, schema_editor):
@ -16,7 +16,7 @@ def vytvor_deadliny(apps, schema_editor):
if cislo.rocnik.rocnik < 26: if cislo.rocnik.rocnik < 26:
Deadline.objects.create( Deadline.objects.create(
cislo=cislo, cislo=cislo,
typ=m.Deadline.TYP_CISLA, typ=mDeadline.TYP_CISLA,
deadline=timezone.make_aware(datetime.datetime.combine(datetime.date(1994 + cislo.rocnik.rocnik, 6, int(cislo.poradi[0])), datetime.time.min)), deadline=timezone.make_aware(datetime.datetime.combine(datetime.date(1994 + cislo.rocnik.rocnik, 6, int(cislo.poradi[0])), datetime.time.min)),
verejna_vysledkovka=cislo.verejna_vysledkovka, verejna_vysledkovka=cislo.verejna_vysledkovka,
) )
@ -33,24 +33,24 @@ def vytvor_deadliny(apps, schema_editor):
if cislo.datum_deadline_soustredeni and cislo.datum_deadline_soustredeni == cislo.datum_preddeadline: if cislo.datum_deadline_soustredeni and cislo.datum_deadline_soustredeni == cislo.datum_preddeadline:
vytvor_deadline( vytvor_deadline(
date=cislo.datum_deadline_soustredeni, date=cislo.datum_deadline_soustredeni,
typ=m.Deadline.TYP_PRVNI_A_SOUS typ=mDeadline.TYP_PRVNI_A_SOUS
) )
else: else:
if cislo.datum_deadline_soustredeni: if cislo.datum_deadline_soustredeni:
vytvor_deadline( vytvor_deadline(
date=cislo.datum_deadline_soustredeni, date=cislo.datum_deadline_soustredeni,
typ=m.Deadline.TYP_SOUS typ=mDeadline.TYP_SOUS
) )
if cislo.datum_preddeadline: if cislo.datum_preddeadline:
vytvor_deadline( vytvor_deadline(
date=cislo.datum_preddeadline, date=cislo.datum_preddeadline,
typ=m.Deadline.TYP_PRVNI typ=mDeadline.TYP_PRVNI
) )
if cislo.datum_deadline: if cislo.datum_deadline:
vytvor_deadline( vytvor_deadline(
date=cislo.datum_deadline, date=cislo.datum_deadline,
typ=m.Deadline.TYP_CISLA typ=mDeadline.TYP_CISLA
) )

2
seminar/migrations/0105_odstraneni_deadlinu_cisla.py

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

2
seminar/migrations/0106_remove_cislo_verejna_vysledkovka.py

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

14
seminar/migrations/0131_odstrel_odevzdavatka_pre.py

@ -0,0 +1,14 @@
# Generated by Django 4.2.13 on 2024-10-22 22:17
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('seminar', '0130_clanek_strana'),
('personalni', '0012_odstrel_odevzdavatka_pre'),
]
operations = [
]

29
seminar/migrations/0132_unmanage_odevzdavatko.py

@ -0,0 +1,29 @@
# Generated by Django 4.2.13 on 2024-10-22 22:31
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('seminar', '0131_odstrel_odevzdavatka_pre'),
]
operations = [
migrations.AlterModelOptions(
name='hodnoceni',
options={'managed': False, 'verbose_name': 'Hodnocení', 'verbose_name_plural': 'Hodnocení'},
),
migrations.AlterModelOptions(
name='prilohareseni',
options={'managed': False, 'ordering': ['reseni', 'vytvoreno'], 'verbose_name': 'Příloha řešení', 'verbose_name_plural': 'Přílohy řešení'},
),
migrations.AlterModelOptions(
name='reseni',
options={'managed': False, 'ordering': ['-cas_doruceni'], 'verbose_name': 'Řešení', 'verbose_name_plural': 'Řešení'},
),
migrations.AlterModelOptions(
name='reseni_resitele',
options={'managed': False, 'ordering': ['reseni', 'resitele'], 'verbose_name': 'Řešení řešitelů', 'verbose_name_plural': 'Řešení řešitelů'},
),
]

20
seminar/migrations/0133_relink_odevzdavatko.py

@ -0,0 +1,20 @@
# Generated by Django 4.2.13 on 2024-10-23 19:53
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('odevzdavatko', '0001_create'),
('seminar', '0132_unmanage_odevzdavatko'),
]
operations = [
migrations.AlterField(
model_name='reseninode',
name='reseni',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='odevzdavatko.reseni', verbose_name='reseni'),
),
]

50
seminar/migrations/0134_delete_odevzdavatko.py

@ -0,0 +1,50 @@
# Generated by Django 4.2.13 on 2024-10-23 19:56
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('seminar', '0133_relink_odevzdavatko'),
('odevzdavatko', '0001_create'),
]
operations = [
migrations.RemoveField(
model_name='prilohareseni',
name='reseni',
),
migrations.RemoveField(
model_name='reseni',
name='problem',
),
migrations.RemoveField(
model_name='reseni',
name='resitele',
),
migrations.RemoveField(
model_name='reseni',
name='text_cely',
),
migrations.RemoveField(
model_name='reseni_resitele',
name='reseni',
),
migrations.RemoveField(
model_name='reseni_resitele',
name='resitele',
),
migrations.DeleteModel(
name='Hodnoceni',
),
migrations.DeleteModel(
name='PrilohaReseni',
),
migrations.DeleteModel(
name='Reseni',
),
migrations.DeleteModel(
name='Reseni_Resitele',
),
]

14
seminar/migrations/0135_odstrel_odevzdavatka_post.py

@ -0,0 +1,14 @@
# Generated by Django 4.2.13 on 2024-10-23 21:10
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('seminar', '0134_delete_odevzdavatko'),
('odevzdavatko', '0003_odstrel_odevzdavatka_post'),
]
operations = [
]

21
seminar/migrations/0136_tvorba_pre.py

@ -0,0 +1,21 @@
# Generated by Django 4.2.16 on 2024-10-30 01:06
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('seminar', '0135_odstrel_odevzdavatka_post'),
('odevzdavatko', '0004_tvorba_pre'),
('various', '0004_tvorba_pre'),
('soustredeni', '0004_tvorba_pre'),
('personalni', '0014_tvorba_pre'),
# Polymorphic:
('contenttypes', '0002_remove_content_type_name'),
# Taggit
('taggit', '0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx'),
]
operations = [
]

59
seminar/migrations/0137_tvorba_unmanage.py

@ -0,0 +1,59 @@
# Generated by Django 4.2.16 on 2024-10-30 11:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('seminar', '0136_tvorba_pre'),
]
operations = [
migrations.CreateModel(
name='Problemy_Opravovatele',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
],
options={
'db_table': 'seminar_problemy_opravovatele',
'managed': False,
},
),
migrations.AlterModelOptions(
name='cislo',
options={'managed': False, 'ordering': ['-rocnik__rocnik', '-poradi'], 'verbose_name': 'Číslo', 'verbose_name_plural': 'Čísla'},
),
migrations.AlterModelOptions(
name='clanek',
options={'managed': False, 'verbose_name': 'Článek', 'verbose_name_plural': 'Články'},
),
migrations.AlterModelOptions(
name='deadline',
options={'managed': False, 'ordering': ['deadline'], 'verbose_name': 'Deadline', 'verbose_name_plural': 'Deadliny'},
),
migrations.AlterModelOptions(
name='pohadka',
options={'managed': False, 'ordering': ['vytvoreno'], 'verbose_name': 'Pohádka', 'verbose_name_plural': 'Pohádky'},
),
migrations.AlterModelOptions(
name='problem',
options={'managed': False, 'ordering': ['nazev'], 'verbose_name': 'Problém', 'verbose_name_plural': 'Problémy'},
),
migrations.AlterModelOptions(
name='rocnik',
options={'managed': False, 'ordering': ['-rocnik'], 'verbose_name': 'Ročník', 'verbose_name_plural': 'Ročníky'},
),
migrations.AlterModelOptions(
name='tema',
options={'managed': False, 'verbose_name': 'Téma', 'verbose_name_plural': 'Témata'},
),
migrations.AlterModelOptions(
name='uloha',
options={'managed': False, 'verbose_name': 'Úloha', 'verbose_name_plural': 'Úlohy'},
),
migrations.AlterModelOptions(
name='zmrazenavysledkovka',
options={'managed': False, 'verbose_name': 'Zmražená výsledkovka', 'verbose_name_plural': 'Zmražené výsledkovky'},
),
]

150
seminar/migrations/0138_tvorba_delete.py

@ -0,0 +1,150 @@
# Generated by Django 4.2.16 on 2024-10-30 14:03
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('tvorba', '0001_tvorba_create'),
('seminar', '0137_tvorba_unmanage'),
('odevzdavatko', '0005_tvorba_relink'),
('soustredeni', '0009_tvorba_relink5'),
('various', '0005_tvorba_relink'),
]
operations = [
migrations.RemoveField(
model_name='cislo',
name='rocnik',
),
migrations.RemoveField(
model_name='clanek',
name='cislo',
),
migrations.RemoveField(
model_name='clanek',
name='problem_ptr',
),
migrations.RemoveField(
model_name='deadline',
name='cislo',
),
migrations.RemoveField(
model_name='pohadka',
name='autor',
),
migrations.RemoveField(
model_name='problem',
name='autor',
),
migrations.RemoveField(
model_name='problem',
name='garant',
),
migrations.RemoveField(
model_name='problem',
name='nadproblem',
),
migrations.RemoveField(
model_name='problem',
name='opravovatele',
),
migrations.RemoveField(
model_name='problem',
name='polymorphic_ctype',
),
migrations.RemoveField(
model_name='problem',
name='zamereni',
),
migrations.DeleteModel(
name='Problemy_Opravovatele',
),
migrations.RemoveField(
model_name='tema',
name='problem_ptr',
),
migrations.RemoveField(
model_name='tema',
name='rocnik',
),
migrations.RemoveField(
model_name='uloha',
name='cislo_deadline',
),
migrations.RemoveField(
model_name='uloha',
name='cislo_reseni',
),
migrations.RemoveField(
model_name='uloha',
name='cislo_zadani',
),
migrations.RemoveField(
model_name='uloha',
name='problem_ptr',
),
migrations.RemoveField(
model_name='zmrazenavysledkovka',
name='deadline',
),
migrations.AlterField(
model_name='cislonode',
name='cislo',
field=models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to='tvorba.cislo', verbose_name='číslo'),
),
migrations.AlterField(
model_name='pohadkanode',
name='pohadka',
field=models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to='tvorba.pohadka', verbose_name='pohádka'),
),
migrations.AlterField(
model_name='rocniknode',
name='rocnik',
field=models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to='tvorba.rocnik', verbose_name='ročník'),
),
migrations.AlterField(
model_name='temavcislenode',
name='tema',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tvorba.tema', verbose_name='téma v čísle'),
),
migrations.AlterField(
model_name='ulohavzoraknode',
name='uloha',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.PROTECT, to='tvorba.uloha', verbose_name='úloha'),
),
migrations.AlterField(
model_name='ulohazadaninode',
name='uloha',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.PROTECT, to='tvorba.uloha', verbose_name='úloha'),
),
migrations.DeleteModel(
name='Cislo',
),
migrations.DeleteModel(
name='Clanek',
),
migrations.DeleteModel(
name='Deadline',
),
migrations.DeleteModel(
name='Pohadka',
),
migrations.DeleteModel(
name='Problem',
),
migrations.DeleteModel(
name='Rocnik',
),
migrations.DeleteModel(
name='Tema',
),
migrations.DeleteModel(
name='Uloha',
),
migrations.DeleteModel(
name='ZmrazenaVysledkovka',
),
]

14
seminar/migrations/0139_tvorba_post.py

@ -0,0 +1,14 @@
# Generated by Django 4.2.16 on 2024-10-30 21:35
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('seminar', '0138_tvorba_delete'),
('tvorba', '0003_tvorba_post'),
]
operations = [
]

17
seminar/migrations/0140_odstrel_treenode_pre.py

@ -0,0 +1,17 @@
# Generated by Django 4.2.16 on 2024-11-02 19:45
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('seminar', '0139_tvorba_post'),
('odevzdavatko', '0007_odstrel_treenode_pre'),
('personalni', '0016_odstrel_treenode_pre'),
('tvorba', '0004_odstrel_treenode_pre'),
('contenttypes', '0002_remove_content_type_name'),
]
operations = [
]

69
seminar/migrations/0141_odstrel_treenode_unmanage.py

@ -0,0 +1,69 @@
# Generated by Django 4.2.16 on 2024-11-02 19:56
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('seminar', '0140_odstrel_treenode_pre'),
]
operations = [
migrations.AlterModelOptions(
name='castnode',
options={'managed': False, 'verbose_name': 'Část (Node)', 'verbose_name_plural': 'Části (Node)'},
),
migrations.AlterModelOptions(
name='cislonode',
options={'managed': False, 'verbose_name': 'Číslo (Node)', 'verbose_name_plural': 'Čísla (Node)'},
),
migrations.AlterModelOptions(
name='mezicislonode',
options={'managed': False, 'verbose_name': 'Mezičíslo (Node)', 'verbose_name_plural': 'Mezičísla (Node)'},
),
migrations.AlterModelOptions(
name='obrazek',
options={'managed': False, 'verbose_name': 'obrázek', 'verbose_name_plural': 'obrázky'},
),
migrations.AlterModelOptions(
name='orgtextnode',
options={'managed': False, 'verbose_name': 'Organizátorský článek (Node)', 'verbose_name_plural': 'Organizátorské články (Node)'},
),
migrations.AlterModelOptions(
name='pohadkanode',
options={'managed': False, 'verbose_name': 'Pohádka (Node)', 'verbose_name_plural': 'Pohádky (Node)'},
),
migrations.AlterModelOptions(
name='reseninode',
options={'managed': False, 'verbose_name': 'Otištěné řešení (Node)', 'verbose_name_plural': 'Otištěná řešení (Node)'},
),
migrations.AlterModelOptions(
name='rocniknode',
options={'managed': False, 'verbose_name': 'Ročník (Node)', 'verbose_name_plural': 'Ročníky (Node)'},
),
migrations.AlterModelOptions(
name='temavcislenode',
options={'managed': False, 'verbose_name': 'Téma v čísle (Node)', 'verbose_name_plural': 'Témata v čísle (Node)'},
),
migrations.AlterModelOptions(
name='text',
options={'managed': False, 'verbose_name': 'text', 'verbose_name_plural': 'texty'},
),
migrations.AlterModelOptions(
name='textnode',
options={'managed': False, 'verbose_name': 'Text (Node)', 'verbose_name_plural': 'Text (Node)'},
),
migrations.AlterModelOptions(
name='treenode',
options={'managed': False, 'verbose_name': 'TreeNode', 'verbose_name_plural': 'TreeNody'},
),
migrations.AlterModelOptions(
name='ulohavzoraknode',
options={'managed': False, 'verbose_name': 'Vzorák úlohy (Node)', 'verbose_name_plural': 'Vzoráky úloh (Node)'},
),
migrations.AlterModelOptions(
name='ulohazadaninode',
options={'managed': False, 'verbose_name': 'Zadání úlohy (Node)', 'verbose_name_plural': 'Zadání úloh (Node)'},
),
]

153
seminar/migrations/0142_odstrel_treenode_delete.py

@ -0,0 +1,153 @@
# Generated by Django 4.2.16 on 2024-11-02 20:47
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('seminar', '0141_odstrel_treenode_unmanage'),
('odevzdavatko', '0008_odstrel_treenode_relink'),
('treenode', '0001_odstrel_treenode_create'),
]
operations = [
migrations.RemoveField(
model_name='cislonode',
name='cislo',
),
migrations.RemoveField(
model_name='cislonode',
name='treenode_ptr',
),
migrations.RemoveField(
model_name='mezicislonode',
name='treenode_ptr',
),
migrations.RemoveField(
model_name='obrazek',
name='text',
),
migrations.RemoveField(
model_name='orgtextnode',
name='organizator',
),
migrations.RemoveField(
model_name='orgtextnode',
name='treenode_ptr',
),
migrations.RemoveField(
model_name='pohadkanode',
name='pohadka',
),
migrations.RemoveField(
model_name='pohadkanode',
name='treenode_ptr',
),
migrations.RemoveField(
model_name='reseninode',
name='reseni',
),
migrations.RemoveField(
model_name='reseninode',
name='treenode_ptr',
),
migrations.RemoveField(
model_name='rocniknode',
name='rocnik',
),
migrations.RemoveField(
model_name='rocniknode',
name='treenode_ptr',
),
migrations.RemoveField(
model_name='temavcislenode',
name='tema',
),
migrations.RemoveField(
model_name='temavcislenode',
name='treenode_ptr',
),
migrations.RemoveField(
model_name='textnode',
name='text',
),
migrations.RemoveField(
model_name='textnode',
name='treenode_ptr',
),
migrations.RemoveField(
model_name='treenode',
name='first_child',
),
migrations.RemoveField(
model_name='treenode',
name='polymorphic_ctype',
),
migrations.RemoveField(
model_name='treenode',
name='root',
),
migrations.RemoveField(
model_name='treenode',
name='succ',
),
migrations.RemoveField(
model_name='ulohavzoraknode',
name='treenode_ptr',
),
migrations.RemoveField(
model_name='ulohavzoraknode',
name='uloha',
),
migrations.RemoveField(
model_name='ulohazadaninode',
name='treenode_ptr',
),
migrations.RemoveField(
model_name='ulohazadaninode',
name='uloha',
),
migrations.DeleteModel(
name='CastNode',
),
migrations.DeleteModel(
name='CisloNode',
),
migrations.DeleteModel(
name='MezicisloNode',
),
migrations.DeleteModel(
name='Obrazek',
),
migrations.DeleteModel(
name='OrgTextNode',
),
migrations.DeleteModel(
name='PohadkaNode',
),
migrations.DeleteModel(
name='ReseniNode',
),
migrations.DeleteModel(
name='RocnikNode',
),
migrations.DeleteModel(
name='TemaVCisleNode',
),
migrations.DeleteModel(
name='Text',
),
migrations.DeleteModel(
name='TextNode',
),
migrations.DeleteModel(
name='TreeNode',
),
migrations.DeleteModel(
name='UlohaVzorakNode',
),
migrations.DeleteModel(
name='UlohaZadaniNode',
),
]

14
seminar/migrations/0143_odstrel_treenode_post.py

@ -0,0 +1,14 @@
# Generated by Django 4.2.16 on 2024-11-02 20:52
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('seminar', '0142_odstrel_treenode_delete'),
('treenode', '0003_odstrel_treenode_post'),
]
operations = [
]

43
seminar/migrations/0144_post_odstrel_vseho.py

@ -0,0 +1,43 @@
# Generated by Django 4.2.16 on 2024-11-03 01:55
from django.db import migrations
# Myšlenka: Tahle migrace o sobě prohlašuje, že závisí na všem, co se do téhle chvíle stalo. To má dva důsledky:
# 1. V okamžiku, kdy tahle migrace proběhne, tak už máme model ve stavu který očekáváme. IOW slouží jako bariéra, za kterou nemůžou přetéct úpravy ostatních aplikací (hlavně těch našich)
# 2. Zároveň ale tvrdíme, že k tomu, aby tahle migrace proběhla, potřebujeme (potenciálně relativně staré) verze cizích aplikací, což způsobí uspořádání opačným směrem: DB změny cizích aplikací naopak proběhnou až po této migraci
# Vzhledem k tomu, že by i naše předchozí aplikace měly záviset na těchto změnách, tak tím efektivně vynucujeme zachování stavu pro ty mezilehlé migrace, které možná (chybou) nedokumentovaně spoléhají na to, jak vypadají cizí aplikace.
# Plán do budoucna: Jakmile tahle migrace proběhne na všech myslitelných databázích, můžeme její předchůdce prostě smazat a nahradit nějakou výrazně snazší sadou migrací, která jen vygeneruje správně tabulky a závislosti podle aktuálního modelu.
# - To se ve skutečnosti vesměs už stalo, v odstřelených aplikacích jsou modely stejně všechny „nové s daty spadlými z nebe“. Je moc pozdě v noci, ale myslím si, že prostě bude stačit smazat závislosti na migracích v `seminar`i a celou aplikaci `seminar` zrušit. (Největší problém je to při nasazování DB z nuly např. u generování testdat…)
# Je otázka, jestli tahle migrace nemá bydlet ve `various` či jinde, aby se dala smazat celá složka `seminar`.
class Migration(migrations.Migration):
dependencies = [
('admin', '0003_logentry_add_action_flag_choices'),
('auth', '0012_alter_user_first_name_max_length'),
('authtoken', '0004_alter_tokenproxy_options'),
('contenttypes', '0002_remove_content_type_name'),
('flatpages', '0001_initial'),
('galerie', '0013_post_split_soustredeni'),
('header_fotky', '0001_initial'),
('korektury', '0024_vic_orgu_k_pdf'),
('novinky', '0004_alter_novinky_id'),
('odevzdavatko', '0009_odstrel_treenode_post'),
('personalni', '0017_odstrel_treenode_post'),
('prednasky', '0018_post_split_soustredeni'),
('reversion', '0002_add_index_on_version_for_content_type_and_db'),
('seminar', '0143_odstrel_treenode_post'),
('sessions', '0001_initial'),
('sifrovacka', '0006_personalni_post_migrate'),
('sites', '0002_alter_domain_unique'),
('sitetree', '0002_alter_treeitem_parent_alter_treeitem_tree'),
('soustredeni', '0010_tvorba_post'),
('taggit', '0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx'),
('treenode', '0003_odstrel_treenode_post'),
('tvorba', '0005_odstrel_treenode_post'),
('various', '0006_tvorba_post'),
('vyroci', '0001_initial'),
]
operations = [
]

15
seminar/models.py

@ -0,0 +1,15 @@
# Tento soubor slouží pouze pro shell a podobné. Nikde neimportovat v kódu!
print("Naimportoval jsi `seminar.models`. Pevně věřím, že to nebylo nikde v kódu. Díky.")
from galerie.models import *
from header_fotky.models import *
from korektury.models import *
from novinky.models import *
from odevzdavatko.models import *
from personalni.models import *
from prednasky.models import *
from soustredeni.models import *
from treenode.models import *
from tvorba.models import *
from various.models import *

13
seminar/models/__init__.py

@ -1,13 +0,0 @@
from .tvorba import *
from .odevzdavatko import *
from .base import *
from .pomocne import *
from .treenode import *
from various.models import Nastaveni
from personalni.models import Organizator, Resitel, Skola, Prijemce, Osoba
from soustredeni.models import Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Konfera, Konfery_Ucastnici
from novinky.models import Novinky
# Kvůli migr. 0041
from soustredeni.models import generate_filename_konfera

22
seminar/models/base.py

@ -1,22 +0,0 @@
from django.urls import reverse
from django.db import models
class SeminarModelBase(models.Model):
class Meta:
abstract = True
def verejne(self):
return False
# def get_absolute_url(self):
# return "https://" + str(get_current_site(None)) + self.verejne_url()
def admin_url(self):
model_name = self.__class__.__name__.lower()
return reverse('admin:seminar_{}_change'.format(model_name), args=(self.id, ))
# def verejne_url(self):
# return None

69
seminar/models/pomocne.py

@ -1,69 +0,0 @@
import logging
import os
from django.db import models
from .base import SeminarModelBase
logger = logging.getLogger(__name__)
class Text(SeminarModelBase):
class Meta:
db_table = 'seminar_texty'
verbose_name = 'text'
verbose_name_plural = 'texty'
na_web = models.TextField(
'text na web', blank=True,
help_text='Text ke zveřejnění na webu')
do_cisla = models.TextField(
'text do čísla', blank=True,
help_text='Text ke zveřejnění v čísle')
# má OneToOneField s:
# Reseni (je u něj jako reseni_cele)
# obrázky mají návaznost opačným směrem (vazba z druhé strany)
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
# *Node.save() aktualizuje název *Nodu.
for tn in self.textnode_set.all():
tn.save()
def __str__(self):
return str(self.na_web)[:20]
class Obrazek(SeminarModelBase):
class Meta:
db_table = 'seminar_obrazky'
verbose_name = 'obrázek'
verbose_name_plural = 'obrázky'
# Interní ID
id = models.AutoField(primary_key=True)
na_web = models.ImageField(
'obrázek na web', upload_to='obrazky/%Y/%m/%d/',
null=True, blank=True)
text = models.ForeignKey(
Text, verbose_name='text',
help_text='text, ve kterém se obrázek vyskytuje',
null=False, blank=False, on_delete=models.CASCADE)
do_cisla_barevny = models.FileField(
'barevný obrázek do čísla',
help_text='Barevná verze obrázku do čísla',
upload_to='obrazky/%Y/%m/%d/', blank=True, null=True)
do_cisla_cernobily = models.FileField(
'černobílý obrázek do čísla',
help_text='Černobílá verze obrázku do čísla',
upload_to='obrazky/%Y/%m/%d/', blank=True, null=True)
# TODO placement hint - chci ho tady / pred textem / za textem

13
soustredeni/migrations/0004_tvorba_pre.py

@ -0,0 +1,13 @@
# Generated by Django 4.2.16 on 2024-10-30 01:07
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('soustredeni', '0003_post_split_soustredeni'),
]
operations = [
]

28
soustredeni/migrations/0005_tvorba_relink.py

@ -0,0 +1,28 @@
# Generated by Django 4.2.16 on 2024-10-30 13:18
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('tvorba', '0001_tvorba_create'),
('soustredeni', '0004_tvorba_pre'),
]
operations = [
## Konferu zmigrujeme jinak, kvůli <https://code.djangoproject.com/ticket/23521> jí nejde přepsat někde ve stavu `bases`.
## Proto si ji unmanagujeme a vyrobíme celou znovu, to by nemělo vadit (zvlášť když t.č. v DB žádná instance Konfery není).
## (Šlo by `SeparateStateAndData`, což v principu děláme taky ale ty migrace jsou lehce čitelnější a o poznání konzistentnější.)
#migrations.AlterField(
# model_name='konfera',
# name='problem_ptr',
# field=models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='tvorba.problem'),
#),
migrations.AlterField(
model_name='soustredeni',
name='rocnik',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='soustredeni', to='tvorba.rocnik', verbose_name='ročník'),
),
]

17
soustredeni/migrations/0006_tvorba_relink2.py

@ -0,0 +1,17 @@
# Generated by Django 4.2.16 on 2024-10-30 19:37
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('soustredeni', '0005_tvorba_relink'),
]
operations = [
migrations.AlterModelOptions(
name='konfera',
options={'managed': False, 'verbose_name': 'Konfera', 'verbose_name_plural': 'Konfery'},
),
]

15
soustredeni/migrations/0007_tvorba_relink3.py

@ -0,0 +1,15 @@
# Generated by Django 4.2.16 on 2024-10-30 19:38
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('soustredeni', '0006_tvorba_relink2'),
]
operations = [
migrations.DeleteModel(
name='Konfera',
),
]

34
soustredeni/migrations/0008_tvorba_relink4.py

@ -0,0 +1,34 @@
# Generated by Django 4.2.16 on 2024-10-30 19:45
from django.db import migrations,models
import django.db.models.deletion
import soustredeni.models
class Migration(migrations.Migration):
dependencies = [
('soustredeni', '0007_tvorba_relink3'),
]
operations = [
migrations.CreateModel(
name='Konfera',
fields=[
('problem_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='tvorba.problem')),
('anotace', models.TextField(blank=True, help_text='Popis, o čem bude konfera.', verbose_name='anotace')),
('abstrakt', models.TextField(blank=True, help_text='Abstrakt konfery tak, jak byl uveden ve sborníku', verbose_name='abstrakt')),
('typ_prezentace', models.CharField(choices=[('veletrh', 'Veletrh (postery)'), ('prezentace', 'Prezentace (přednáška)')], default='veletrh', max_length=16, verbose_name='typ prezentace')),
('prezentace', models.FileField(blank=True, help_text='Prezentace nebo fotka posteru', upload_to=soustredeni.models.generate_filename_konfera, verbose_name='prezentace')),
('materialy', models.FileField(blank=True, help_text='Další materiály ke konfeře zabalené do jednoho souboru', upload_to=soustredeni.models.generate_filename_konfera, verbose_name='materialy')),
('soustredeni', models.ForeignKey(to='soustredeni.soustredeni', verbose_name='soustředění', on_delete=models.SET_NULL, null=True, related_name='konfery')),
('ucastnici', models.ManyToManyField(help_text='Seznam účastníků konfery', through='soustredeni.Konfery_Ucastnici', to='personalni.resitel', verbose_name='účastníci konfery')),
],
options={
'verbose_name': 'Konfera',
'verbose_name_plural': 'Konfery',
'db_table': 'seminar_konfera',
'managed': False,
},
bases=('tvorba.problem',),
),
]

17
soustredeni/migrations/0009_tvorba_relink5.py

@ -0,0 +1,17 @@
# Generated by Django 4.2.16 on 2024-10-30 20:03
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('soustredeni', '0008_tvorba_relink4'),
]
operations = [
migrations.AlterModelOptions(
name='konfera',
options={'verbose_name': 'Konfera', 'verbose_name_plural': 'Konfery'},
),
]

14
soustredeni/migrations/0010_tvorba_post.py

@ -0,0 +1,14 @@
# Generated by Django 4.2.16 on 2024-10-30 21:35
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('soustredeni', '0009_tvorba_relink5'),
('tvorba', '0003_tvorba_post'),
]
operations = [
]

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save