Compare commits

..

No commits in common. "master" and "odstrel_modelu_odevzdavatko" have entirely different histories.

189 changed files with 1600 additions and 3834 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,645 +0,0 @@
[
{
"fields": {
"name": "org",
"permissions": [
[
"org",
"auth",
"user"
],
[
"add_flatpage",
"flatpages",
"flatpage"
],
[
"delete_flatpage",
"flatpages",
"flatpage"
],
[
"change_flatpage",
"flatpages",
"flatpage"
],
[
"view_flatpage",
"flatpages",
"flatpage"
],
[
"add_galerie",
"galerie",
"galerie"
],
[
"delete_galerie",
"galerie",
"galerie"
],
[
"change_galerie",
"galerie",
"galerie"
],
[
"view_galerie",
"galerie",
"galerie"
],
[
"add_obrazek",
"galerie",
"obrazek"
],
[
"delete_obrazek",
"galerie",
"obrazek"
],
[
"change_obrazek",
"galerie",
"obrazek"
],
[
"view_obrazek",
"galerie",
"obrazek"
],
[
"add_fotkaheader",
"header_fotky",
"fotkaheader"
],
[
"change_fotkaheader",
"header_fotky",
"fotkaheader"
],
[
"view_fotkaheader",
"header_fotky",
"fotkaheader"
],
[
"add_fotkaurlvazba",
"header_fotky",
"fotkaurlvazba"
],
[
"change_fotkaurlvazba",
"header_fotky",
"fotkaurlvazba"
],
[
"view_fotkaurlvazba",
"header_fotky",
"fotkaurlvazba"
],
[
"add_komentar",
"korektury",
"komentar"
],
[
"delete_komentar",
"korektury",
"komentar"
],
[
"change_komentar",
"korektury",
"komentar"
],
[
"view_komentar",
"korektury",
"komentar"
],
[
"add_korekturovanepdf",
"korektury",
"korekturovanepdf"
],
[
"delete_korekturovanepdf",
"korektury",
"korekturovanepdf"
],
[
"change_korekturovanepdf",
"korektury",
"korekturovanepdf"
],
[
"view_korekturovanepdf",
"korektury",
"korekturovanepdf"
],
[
"add_oprava",
"korektury",
"oprava"
],
[
"delete_oprava",
"korektury",
"oprava"
],
[
"change_oprava",
"korektury",
"oprava"
],
[
"view_oprava",
"korektury",
"oprava"
],
[
"add_novinky",
"novinky",
"novinky"
],
[
"delete_novinky",
"novinky",
"novinky"
],
[
"change_novinky",
"novinky",
"novinky"
],
[
"view_novinky",
"novinky",
"novinky"
],
[
"change_organizator",
"personalni",
"organizator"
],
[
"view_organizator",
"personalni",
"organizator"
],
[
"change_osoba",
"personalni",
"osoba"
],
[
"view_osoba",
"personalni",
"osoba"
],
[
"add_prijemce",
"personalni",
"prijemce"
],
[
"delete_prijemce",
"personalni",
"prijemce"
],
[
"change_prijemce",
"personalni",
"prijemce"
],
[
"view_prijemce",
"personalni",
"prijemce"
],
[
"change_resitel",
"personalni",
"resitel"
],
[
"view_resitel",
"personalni",
"resitel"
],
[
"add_skola",
"personalni",
"skola"
],
[
"delete_skola",
"personalni",
"skola"
],
[
"change_skola",
"personalni",
"skola"
],
[
"view_skola",
"personalni",
"skola"
],
[
"add_hlasovani",
"prednasky",
"hlasovani"
],
[
"delete_hlasovani",
"prednasky",
"hlasovani"
],
[
"change_hlasovani",
"prednasky",
"hlasovani"
],
[
"view_hlasovani",
"prednasky",
"hlasovani"
],
[
"add_prednaska",
"prednasky",
"prednaska"
],
[
"delete_prednaska",
"prednasky",
"prednaska"
],
[
"change_prednaska",
"prednasky",
"prednaska"
],
[
"view_prednaska",
"prednasky",
"prednaska"
],
[
"add_seznam",
"prednasky",
"seznam"
],
[
"delete_seznam",
"prednasky",
"seznam"
],
[
"change_seznam",
"prednasky",
"seznam"
],
[
"view_seznam",
"prednasky",
"seznam"
],
[
"add_konfera",
"soustredeni",
"konfera"
],
[
"delete_konfera",
"soustredeni",
"konfera"
],
[
"change_konfera",
"soustredeni",
"konfera"
],
[
"view_konfera",
"soustredeni",
"konfera"
],
[
"add_konfery_ucastnici",
"soustredeni",
"konfery_ucastnici"
],
[
"delete_konfery_ucastnici",
"soustredeni",
"konfery_ucastnici"
],
[
"change_konfery_ucastnici",
"soustredeni",
"konfery_ucastnici"
],
[
"view_konfery_ucastnici",
"soustredeni",
"konfery_ucastnici"
],
[
"add_soustredeni",
"soustredeni",
"soustredeni"
],
[
"delete_soustredeni",
"soustredeni",
"soustredeni"
],
[
"change_soustredeni",
"soustredeni",
"soustredeni"
],
[
"view_soustredeni",
"soustredeni",
"soustredeni"
],
[
"add_soustredeni_organizatori",
"soustredeni",
"soustredeni_organizatori"
],
[
"delete_soustredeni_organizatori",
"soustredeni",
"soustredeni_organizatori"
],
[
"change_soustredeni_organizatori",
"soustredeni",
"soustredeni_organizatori"
],
[
"view_soustredeni_organizatori",
"soustredeni",
"soustredeni_organizatori"
],
[
"add_soustredeni_ucastnici",
"soustredeni",
"soustredeni_ucastnici"
],
[
"delete_soustredeni_ucastnici",
"soustredeni",
"soustredeni_ucastnici"
],
[
"change_soustredeni_ucastnici",
"soustredeni",
"soustredeni_ucastnici"
],
[
"view_soustredeni_ucastnici",
"soustredeni",
"soustredeni_ucastnici"
],
[
"add_tag",
"taggit",
"tag"
],
[
"delete_tag",
"taggit",
"tag"
],
[
"change_tag",
"taggit",
"tag"
],
[
"view_tag",
"taggit",
"tag"
],
[
"add_taggeditem",
"taggit",
"taggeditem"
],
[
"delete_taggeditem",
"taggit",
"taggeditem"
],
[
"change_taggeditem",
"taggit",
"taggeditem"
],
[
"view_taggeditem",
"taggit",
"taggeditem"
],
[
"add_cislo",
"tvorba",
"cislo"
],
[
"delete_cislo",
"tvorba",
"cislo"
],
[
"change_cislo",
"tvorba",
"cislo"
],
[
"view_cislo",
"tvorba",
"cislo"
],
[
"add_clanek",
"tvorba",
"clanek"
],
[
"delete_clanek",
"tvorba",
"clanek"
],
[
"change_clanek",
"tvorba",
"clanek"
],
[
"view_clanek",
"tvorba",
"clanek"
],
[
"add_deadline",
"tvorba",
"deadline"
],
[
"change_deadline",
"tvorba",
"deadline"
],
[
"view_deadline",
"tvorba",
"deadline"
],
[
"add_pohadka",
"tvorba",
"pohadka"
],
[
"delete_pohadka",
"tvorba",
"pohadka"
],
[
"change_pohadka",
"tvorba",
"pohadka"
],
[
"view_pohadka",
"tvorba",
"pohadka"
],
[
"add_problem",
"tvorba",
"problem"
],
[
"delete_problem",
"tvorba",
"problem"
],
[
"change_problem",
"tvorba",
"problem"
],
[
"view_problem",
"tvorba",
"problem"
],
[
"add_rocnik",
"tvorba",
"rocnik"
],
[
"delete_rocnik",
"tvorba",
"rocnik"
],
[
"change_rocnik",
"tvorba",
"rocnik"
],
[
"view_rocnik",
"tvorba",
"rocnik"
],
[
"add_tema",
"tvorba",
"tema"
],
[
"delete_tema",
"tvorba",
"tema"
],
[
"change_tema",
"tvorba",
"tema"
],
[
"view_tema",
"tvorba",
"tema"
],
[
"add_uloha",
"tvorba",
"uloha"
],
[
"delete_uloha",
"tvorba",
"uloha"
],
[
"change_uloha",
"tvorba",
"uloha"
],
[
"view_uloha",
"tvorba",
"uloha"
],
[
"add_nastaveni",
"various",
"nastaveni"
],
[
"delete_nastaveni",
"various",
"nastaveni"
],
[
"change_nastaveni",
"various",
"nastaveni"
],
[
"view_nastaveni",
"various",
"nastaveni"
]
]
},
"model": "auth.group",
"pk": 1
},
{
"fields": {
"name": "resitel",
"permissions": [
[
"resitel",
"auth",
"user"
]
]
},
"model": "auth.group",
"pk": 2
}
]

View file

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

View file

@ -0,0 +1,622 @@
[
{
"codename": "org",
"ct_app_label": "auth",
"ct_model": "user"
},
{
"codename": "add_flatpage",
"ct_app_label": "flatpages",
"ct_model": "flatpage"
},
{
"codename": "change_flatpage",
"ct_app_label": "flatpages",
"ct_model": "flatpage"
},
{
"codename": "delete_flatpage",
"ct_app_label": "flatpages",
"ct_model": "flatpage"
},
{
"codename": "view_flatpage",
"ct_app_label": "flatpages",
"ct_model": "flatpage"
},
{
"codename": "add_galerie",
"ct_app_label": "galerie",
"ct_model": "galerie"
},
{
"codename": "change_galerie",
"ct_app_label": "galerie",
"ct_model": "galerie"
},
{
"codename": "delete_galerie",
"ct_app_label": "galerie",
"ct_model": "galerie"
},
{
"codename": "view_galerie",
"ct_app_label": "galerie",
"ct_model": "galerie"
},
{
"codename": "add_obrazek",
"ct_app_label": "galerie",
"ct_model": "obrazek"
},
{
"codename": "change_obrazek",
"ct_app_label": "galerie",
"ct_model": "obrazek"
},
{
"codename": "delete_obrazek",
"ct_app_label": "galerie",
"ct_model": "obrazek"
},
{
"codename": "view_obrazek",
"ct_app_label": "galerie",
"ct_model": "obrazek"
},
{
"codename": "add_fotkaheader",
"ct_app_label": "header_fotky",
"ct_model": "fotkaheader"
},
{
"codename": "change_fotkaheader",
"ct_app_label": "header_fotky",
"ct_model": "fotkaheader"
},
{
"codename": "view_fotkaheader",
"ct_app_label": "header_fotky",
"ct_model": "fotkaheader"
},
{
"codename": "add_fotkaurlvazba",
"ct_app_label": "header_fotky",
"ct_model": "fotkaurlvazba"
},
{
"codename": "change_fotkaurlvazba",
"ct_app_label": "header_fotky",
"ct_model": "fotkaurlvazba"
},
{
"codename": "view_fotkaurlvazba",
"ct_app_label": "header_fotky",
"ct_model": "fotkaurlvazba"
},
{
"codename": "add_komentar",
"ct_app_label": "korektury",
"ct_model": "komentar"
},
{
"codename": "change_komentar",
"ct_app_label": "korektury",
"ct_model": "komentar"
},
{
"codename": "delete_komentar",
"ct_app_label": "korektury",
"ct_model": "komentar"
},
{
"codename": "view_komentar",
"ct_app_label": "korektury",
"ct_model": "komentar"
},
{
"codename": "add_korekturovanepdf",
"ct_app_label": "korektury",
"ct_model": "korekturovanepdf"
},
{
"codename": "change_korekturovanepdf",
"ct_app_label": "korektury",
"ct_model": "korekturovanepdf"
},
{
"codename": "delete_korekturovanepdf",
"ct_app_label": "korektury",
"ct_model": "korekturovanepdf"
},
{
"codename": "view_korekturovanepdf",
"ct_app_label": "korektury",
"ct_model": "korekturovanepdf"
},
{
"codename": "add_oprava",
"ct_app_label": "korektury",
"ct_model": "oprava"
},
{
"codename": "change_oprava",
"ct_app_label": "korektury",
"ct_model": "oprava"
},
{
"codename": "delete_oprava",
"ct_app_label": "korektury",
"ct_model": "oprava"
},
{
"codename": "view_oprava",
"ct_app_label": "korektury",
"ct_model": "oprava"
},
{
"codename": "add_hlasovani",
"ct_app_label": "prednasky",
"ct_model": "hlasovani"
},
{
"codename": "change_hlasovani",
"ct_app_label": "prednasky",
"ct_model": "hlasovani"
},
{
"codename": "delete_hlasovani",
"ct_app_label": "prednasky",
"ct_model": "hlasovani"
},
{
"codename": "view_hlasovani",
"ct_app_label": "prednasky",
"ct_model": "hlasovani"
},
{
"codename": "add_prednaska",
"ct_app_label": "prednasky",
"ct_model": "prednaska"
},
{
"codename": "change_prednaska",
"ct_app_label": "prednasky",
"ct_model": "prednaska"
},
{
"codename": "delete_prednaska",
"ct_app_label": "prednasky",
"ct_model": "prednaska"
},
{
"codename": "view_prednaska",
"ct_app_label": "prednasky",
"ct_model": "prednaska"
},
{
"codename": "add_seznam",
"ct_app_label": "prednasky",
"ct_model": "seznam"
},
{
"codename": "change_seznam",
"ct_app_label": "prednasky",
"ct_model": "seznam"
},
{
"codename": "delete_seznam",
"ct_app_label": "prednasky",
"ct_model": "seznam"
},
{
"codename": "view_seznam",
"ct_app_label": "prednasky",
"ct_model": "seznam"
},
{
"codename": "add_cislo",
"ct_app_label": "seminar",
"ct_model": "cislo"
},
{
"codename": "change_cislo",
"ct_app_label": "seminar",
"ct_model": "cislo"
},
{
"codename": "delete_cislo",
"ct_app_label": "seminar",
"ct_model": "cislo"
},
{
"codename": "view_cislo",
"ct_app_label": "seminar",
"ct_model": "cislo"
},
{
"codename": "add_clanek",
"ct_app_label": "seminar",
"ct_model": "clanek"
},
{
"codename": "change_clanek",
"ct_app_label": "seminar",
"ct_model": "clanek"
},
{
"codename": "delete_clanek",
"ct_app_label": "seminar",
"ct_model": "clanek"
},
{
"codename": "view_clanek",
"ct_app_label": "seminar",
"ct_model": "clanek"
},
{
"codename": "add_deadline",
"ct_app_label": "seminar",
"ct_model": "deadline"
},
{
"codename": "change_deadline",
"ct_app_label": "seminar",
"ct_model": "deadline"
},
{
"codename": "view_deadline",
"ct_app_label": "seminar",
"ct_model": "deadline"
},
{
"codename": "add_konfera",
"ct_app_label": "soustredeni",
"ct_model": "konfera"
},
{
"codename": "change_konfera",
"ct_app_label": "soustredeni",
"ct_model": "konfera"
},
{
"codename": "delete_konfera",
"ct_app_label": "soustredeni",
"ct_model": "konfera"
},
{
"codename": "view_konfera",
"ct_app_label": "soustredeni",
"ct_model": "konfera"
},
{
"codename": "add_konfery_ucastnici",
"ct_app_label": "soustredeni",
"ct_model": "konfery_ucastnici"
},
{
"codename": "change_konfery_ucastnici",
"ct_app_label": "soustredeni",
"ct_model": "konfery_ucastnici"
},
{
"codename": "delete_konfery_ucastnici",
"ct_app_label": "soustredeni",
"ct_model": "konfery_ucastnici"
},
{
"codename": "view_konfery_ucastnici",
"ct_app_label": "soustredeni",
"ct_model": "konfery_ucastnici"
},
{
"codename": "add_nastaveni",
"ct_app_label": "various",
"ct_model": "nastaveni"
},
{
"codename": "change_nastaveni",
"ct_app_label": "various",
"ct_model": "nastaveni"
},
{
"codename": "delete_nastaveni",
"ct_app_label": "various",
"ct_model": "nastaveni"
},
{
"codename": "view_nastaveni",
"ct_app_label": "various",
"ct_model": "nastaveni"
},
{
"codename": "add_novinky",
"ct_app_label": "novinky",
"ct_model": "novinky"
},
{
"codename": "change_novinky",
"ct_app_label": "novinky",
"ct_model": "novinky"
},
{
"codename": "delete_novinky",
"ct_app_label": "novinky",
"ct_model": "novinky"
},
{
"codename": "view_novinky",
"ct_app_label": "novinky",
"ct_model": "novinky"
},
{
"codename": "change_organizator",
"ct_app_label": "personalni",
"ct_model": "organizator"
},
{
"codename": "view_organizator",
"ct_app_label": "personalni",
"ct_model": "organizator"
},
{
"codename": "change_osoba",
"ct_app_label": "personalni",
"ct_model": "osoba"
},
{
"codename": "view_osoba",
"ct_app_label": "personalni",
"ct_model": "osoba"
},
{
"codename": "add_pohadka",
"ct_app_label": "seminar",
"ct_model": "pohadka"
},
{
"codename": "change_pohadka",
"ct_app_label": "seminar",
"ct_model": "pohadka"
},
{
"codename": "delete_pohadka",
"ct_app_label": "seminar",
"ct_model": "pohadka"
},
{
"codename": "view_pohadka",
"ct_app_label": "seminar",
"ct_model": "pohadka"
},
{
"codename": "add_prijemce",
"ct_app_label": "personalni",
"ct_model": "prijemce"
},
{
"codename": "change_prijemce",
"ct_app_label": "personalni",
"ct_model": "prijemce"
},
{
"codename": "delete_prijemce",
"ct_app_label": "personalni",
"ct_model": "prijemce"
},
{
"codename": "view_prijemce",
"ct_app_label": "personalni",
"ct_model": "prijemce"
},
{
"codename": "add_problem",
"ct_app_label": "seminar",
"ct_model": "problem"
},
{
"codename": "change_problem",
"ct_app_label": "seminar",
"ct_model": "problem"
},
{
"codename": "delete_problem",
"ct_app_label": "seminar",
"ct_model": "problem"
},
{
"codename": "view_problem",
"ct_app_label": "seminar",
"ct_model": "problem"
},
{
"codename": "change_resitel",
"ct_app_label": "personalni",
"ct_model": "resitel"
},
{
"codename": "view_resitel",
"ct_app_label": "personalni",
"ct_model": "resitel"
},
{
"codename": "add_rocnik",
"ct_app_label": "seminar",
"ct_model": "rocnik"
},
{
"codename": "change_rocnik",
"ct_app_label": "seminar",
"ct_model": "rocnik"
},
{
"codename": "delete_rocnik",
"ct_app_label": "seminar",
"ct_model": "rocnik"
},
{
"codename": "view_rocnik",
"ct_app_label": "seminar",
"ct_model": "rocnik"
},
{
"codename": "add_skola",
"ct_app_label": "personalni",
"ct_model": "skola"
},
{
"codename": "change_skola",
"ct_app_label": "personalni",
"ct_model": "skola"
},
{
"codename": "delete_skola",
"ct_app_label": "personalni",
"ct_model": "skola"
},
{
"codename": "view_skola",
"ct_app_label": "personalni",
"ct_model": "skola"
},
{
"codename": "add_soustredeni",
"ct_app_label": "soustredeni",
"ct_model": "soustredeni"
},
{
"codename": "change_soustredeni",
"ct_app_label": "soustredeni",
"ct_model": "soustredeni"
},
{
"codename": "delete_soustredeni",
"ct_app_label": "soustredeni",
"ct_model": "soustredeni"
},
{
"codename": "view_soustredeni",
"ct_app_label": "soustredeni",
"ct_model": "soustredeni"
},
{
"codename": "add_soustredeni_organizatori",
"ct_app_label": "soustredeni",
"ct_model": "soustredeni_organizatori"
},
{
"codename": "change_soustredeni_organizatori",
"ct_app_label": "soustredeni",
"ct_model": "soustredeni_organizatori"
},
{
"codename": "delete_soustredeni_organizatori",
"ct_app_label": "soustredeni",
"ct_model": "soustredeni_organizatori"
},
{
"codename": "view_soustredeni_organizatori",
"ct_app_label": "soustredeni",
"ct_model": "soustredeni_organizatori"
},
{
"codename": "add_soustredeni_ucastnici",
"ct_app_label": "soustredeni",
"ct_model": "soustredeni_ucastnici"
},
{
"codename": "change_soustredeni_ucastnici",
"ct_app_label": "soustredeni",
"ct_model": "soustredeni_ucastnici"
},
{
"codename": "delete_soustredeni_ucastnici",
"ct_app_label": "soustredeni",
"ct_model": "soustredeni_ucastnici"
},
{
"codename": "view_soustredeni_ucastnici",
"ct_app_label": "soustredeni",
"ct_model": "soustredeni_ucastnici"
},
{
"codename": "add_tema",
"ct_app_label": "seminar",
"ct_model": "tema"
},
{
"codename": "change_tema",
"ct_app_label": "seminar",
"ct_model": "tema"
},
{
"codename": "delete_tema",
"ct_app_label": "seminar",
"ct_model": "tema"
},
{
"codename": "view_tema",
"ct_app_label": "seminar",
"ct_model": "tema"
},
{
"codename": "add_uloha",
"ct_app_label": "seminar",
"ct_model": "uloha"
},
{
"codename": "change_uloha",
"ct_app_label": "seminar",
"ct_model": "uloha"
},
{
"codename": "delete_uloha",
"ct_app_label": "seminar",
"ct_model": "uloha"
},
{
"codename": "view_uloha",
"ct_app_label": "seminar",
"ct_model": "uloha"
},
{
"codename": "add_tag",
"ct_app_label": "taggit",
"ct_model": "tag"
},
{
"codename": "change_tag",
"ct_app_label": "taggit",
"ct_model": "tag"
},
{
"codename": "delete_tag",
"ct_app_label": "taggit",
"ct_model": "tag"
},
{
"codename": "view_tag",
"ct_app_label": "taggit",
"ct_model": "tag"
},
{
"codename": "add_taggeditem",
"ct_app_label": "taggit",
"ct_model": "taggeditem"
},
{
"codename": "change_taggeditem",
"ct_app_label": "taggit",
"ct_model": "taggeditem"
},
{
"codename": "delete_taggeditem",
"ct_app_label": "taggit",
"ct_model": "taggeditem"
},
{
"codename": "view_taggeditem",
"ct_app_label": "taggit",
"ct_model": "taggeditem"
}
]

View file

@ -13,6 +13,7 @@ make install_venv
make install
deploy_v2/pre_migration.py
make deploy_test
./manage.py load_org_permissions admin_org_prava.json
./manage.py loaddata data/*
systemctl --user start mamweb-test.service
./manage.py generate_thumbnails

View file

@ -30,7 +30,6 @@ Dokumentace (jak v ``docs/``, tak přímo v kódu) je psaná ve
zavislosti
sphinx
skripty
zkratky
modules/modules
dalsi_soubory
zapisy/zapisy

View file

@ -1,15 +1,13 @@
Sphinx na našem webu
====================
Dokumentace se zkompiluje příkazem ``make html`` ve složce ``docs``. (Musíte mít zapnutý virtualenv)
Dokumentace se zkompiluje příkazem ``make html`` ve složce ``doc``.
Složka ``modules`` je automaticiky generována a přegenerovávána. (**Nic v ní neupravovat!**)
Jinak všechny rst, co jsou ve složce ``docs`` a jejích podsložkách nezačínajících podtržítkem, budou v dokumentaci a to je přesně to, co editovat pro změnu dokumentace (kromě dokumentace přímo v Pythonu).
Jinak všechny rst, co jsou ve složce ``doc`` a jejích podsložkách nezačínajících podtržítkem, budou v dokumentaci a to je přesně to, co editovat pro změnu dokumentace (kromě dokumentace přímo v Pythonu).
Sphinx se píše v rst: `Návod na syntaxi rst`_ `Cheat sheet`_
Pokud něco chcete protlačit do bočního meníčka, je potřeba to připsat do souboru ``docs/index.rst`` (Zatím není úplně konsensus nad tím, co tam má a nemá být, takže pokud si nejste jistí, cpěte tam *všechno* ☺)
To je snad vše, co je potřeba vědět k dokumentaci mamwebu. Následující sekce jsou o tom, co jsem provedl Sphinxu, aby to fungovalo:
.. _Návod na syntaxi rst: https://sphinx-tutorial.readthedocs.io/step-1/#sections

View file

@ -10,9 +10,11 @@ věci jako chybové hlášky a vzhled M&M stránek (menu, patička, atd.). Aktu
i veškeré csv.
Další jsou pak jednotlivé aplikace (pokud něco hledáte, tak zřejmě chcete najít
tu aplikaci, která tomu odpovídá, respektive se k ní dostat přes url).
tu aplikaci, která tomu odpovídá, respektive se k ní dostat přes url), za
zmínku stojí seminar, kde jsou takové ty věci, co zbyly. Plus jsou tam aktuálně
téměř všechny modely, protože je těžké je přesunout jinam.
**TLDR: Nevšímejte si složek data/ seminar/ a souborů přímo v kořenové složce.**
**TLDR: Nevšímejte si složky data/ 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,
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
@ -20,9 +22,6 @@ databázi), tj. statické stránky, menu a obrázky v pozadí menu. Ten je třeb
měnit hlavně na produkci a sekundárně tady (může to dělat i newebař a nechcete
přepsat jeho práci). Vše, co nejsou aplikace je popsáno :doc:`tady <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
--------------

View file

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

View file

@ -1,86 +0,0 @@
Zkratky aplikací ve zdrojácích
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Ve zdrojácích (zejména různé ``models.py``, ``views.py`` ap.) používáme spoustu
modelů. Někdy je praktičtější / někdo preferuje importovat celou aplikaci jako
jedno jméno a používat modely bez explicitních importů, tj::
# „hromadné“ importy:
import personalni.models as p
...
p.Organizator.objects.all()
# „explicitní“ importy:
from personalni.models import Organizator
...
Organizator.objects.all()
Na webschůzce 2024-11-05 jsme na toto téma otevřeli diskusi, tady je její závěr.
.. admonition:: Historické okénko
:class: note
Kdysi jsme měli (prakticky) všechny modely v jedné aplikaci, ``seminar``. Na
různých místech se pak ``seminar.models`` importovalo typicky jako ``s``
nebo jako ``m``.
Přirozeně, toto už nejde tak snadno, protože už neexistuje jedno místo, ze
kterého chceme tahat modely v kódu.
Konvence
========
Shodli jsme se, že nám rozhodně nevadí explicitní importy a z pohledu
čitelnosti je preferujeme. Nicméně při psaní kódu to některým webařům přijde
nepohodlné, takže očekáváme, že bude existovat spousta kódu, která bude chtít
importovat hromadně. Usnesli jsme se proto na následujících kanonických
zkratkách, aby se aplikace alespoň zkracovaly konzistentně.
V závorkách je uvedené případné jméno, ale nepředpokládáme, že někdo bude danou
aplikaci chtít importovat hromadně. Některé aplikace zkratku nemají, ty se
importují vždy pod plným jménem nebo explicitně.
.. list-table::
:header-rows: 1
* - Model
- Zkratka
* - ``aesop``
- ---
* - ``api``
- --- (``api``)
* - ``galerie``
- ``g``
* - ``header_fotky``
- --- (``hdr``)
* - ``korektury``
- ``kor``
* - ``novinky``
- ``nov``
* - ``odevzdavatko``
- ``odev``
* - ``personální``
- ``pers``/``p``
* - ``sifrovacka``
- (``sifr``)
* - ``soustredeni``
- ``sou``
* - ``treenode``
- ``tn``
* - ``tvorba``
- ``tv``
* - ``various``
- ``v``/``var``
* - ``vyroci``
- ---
* - ``vysledkovky``
- ``vysl``
.. admonition:: O všech modelech pod jedním jménem
:class: warning
Historické okénko výš zatajuje jeden detail: Při práci v shellu se hodí mít
modely k dispozici a nemuset přemýšlet nad dělením do aplikací, takže ve
skutečnosti existuje ``mamweb.vsechno``, jenž všechny modely obsahuje.
Z čitelnostních důvodů je ale *zakázáno* tento modul používat v kódu.

View file

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

View file

@ -1,6 +1,5 @@
import os
from django.db import models
from django.urls import reverse
from django.utils import timezone
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
@ -130,9 +129,6 @@ class KorekturovanePDF(models.Model):
except IndexError:
return self.nazev
def get_absolute_url(self):
return reverse('korektury', kwargs={'pdf': self.id})
@reversion.register(ignore_duplicates=True)
class Oprava(models.Model):

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 697 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 717 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 702 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 617 B

View file

@ -35,27 +35,24 @@ def create_test_pdf(rnd, organizatori):
# TODO silent ghostscript (vypisuje odstavec za každou stránku…)
korekturovane_pdf = KorekturovanePDF.objects.create(
nazev='B', komentar='Neuronové sítě', pdf=gen_filename(filename='B.pdf')
KorekturovanePDF.objects.create(
nazev='B', komentar='Neuronové sítě', org=rnd.choice(organizatori), pdf=gen_filename(filename='B.pdf')
)
korekturovane_pdf.orgove.set((rnd.choice(organizatori),))
korekturovane_pdf = KorekturovanePDF.objects.create(
nazev='A', komentar='M&M: Jak řešit?', pdf=gen_filename(filename='A.pdf')
)
korekturovane_pdf.orgove.set(rnd.sample(organizatori, 2))
KorekturovanePDF.objects.create(
nazev='A', komentar='M&M: Jak řešit?', pdf=gen_filename(filename='A.pdf'),
nazev='A', komentar='M&M: Jak řešit?', org=rnd.choice(organizatori), pdf=gen_filename(filename='A.pdf')
)
korekturovane_pdf = KorekturovanePDF.objects.create(
nazev='A', komentar='M&M: Jak řešit?', org=rnd.choice(organizatori), pdf=gen_filename(filename='A.pdf'),
status='zanaseni'
)
korekturovane_pdf = KorekturovanePDF.objects.create(
nazev='A', komentar='M&M: Jak řešit?', pdf=gen_filename(filename='A.pdf'),
KorekturovanePDF.objects.create(
nazev='A', komentar='M&M: Jak řešit?', org=rnd.choice(organizatori), pdf=gen_filename(filename='A.pdf'),
status='zastarale'
)
korekturovane_pdf.orgove.set((rnd.choice(organizatori),))
except OSError as e:
except (FileNotFoundError, Exception) as e:
# TODO najít správné chyby, které vyhazují různé systémy při neexistenci ImageMagick, nebo knihoven
logger.error(str(e))
logger.error(

View file

@ -8,3 +8,4 @@ ensure_venv
./manage.py testdata
./manage.py loaddata data/*
#make/sync_prod_flatpages
./manage.py load_org_permissions deploy_v2/admin_org_prava.json

View file

@ -95,7 +95,7 @@ function safe_checkout_branch {
echo >&2 "Změna v $SCRIPT, prosím pullni manuálně"
exit 1
fi
git checkout "$BRANCH" --
git checkout "$BRANCH"
git pull
git clean -f
}

View file

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

View file

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

View file

@ -7,18 +7,17 @@ import locale
from django.contrib import admin
from django.contrib.admin import AdminSite
from django.contrib.flatpages.models import FlatPage
import logging
# Note: we are renaming the original Admin and Form as we import them!
from django.contrib.flatpages.admin import FlatPageAdmin as FlatPageAdminOld
from django.contrib.flatpages.admin import FlatpageForm as FlatpageFormOld
from django import forms
from django_ckeditor_5.widgets import CKEditor5Widget
from ckeditor_uploader.widgets import CKEditorUploadingWidget
class FlatpageForm(FlatpageFormOld):
content = forms.CharField(widget=CKEditor5Widget())
content = forms.CharField(widget=CKEditorUploadingWidget())
class Meta:
model = FlatPage # this is not automatically inherited from FlatpageFormOld
exclude = []
@ -44,7 +43,7 @@ def get_app_list(self, request, app_label=None):
app_dict = self._build_app_dict(request, label=app_label)
aplikace_nahore = [
'tvorba',
'seminar',
'personalni',
'novinky',
'korektury',
@ -57,13 +56,8 @@ def get_app_list(self, request, app_label=None):
# Sort the models alphabetically within each app.
try: # na macu nefunguje locale.strxfrm :-/ proto je tu try except block
for app in app_list:
app['models'].sort(key=lambda x: locale.strxfrm(x['name'].lower()))
except OSError as e:
# locale.strxfrm nefunguje na macu... :-/ -> neprovede se řazení
logger = logging.getLogger(__name__)
logger.error(e)
app['models'].sort(key=lambda x: locale.strxfrm('žž' + x['name'].lower()) if (x['name'].endswith("(Node)")) else locale.strxfrm(x['name'].lower()))
return app_list

View file

@ -110,7 +110,8 @@ INSTALLED_APPS = (
'reversion',
'django_countries',
'solo',
'django_ckeditor_5',
'ckeditor',
'ckeditor_uploader',
'taggit',
'dal',
'dal_select2',
@ -185,98 +186,26 @@ SUMMERNOTE_CONFIG = {
]
}
CKEDITOR_5_CUSTOM_CSS = "css/ckeditor5_fix.css"
# customColorPalette = [
# {
# 'color': 'hsl(4, 90%, 58%)',
# 'label': 'Red',
# },
# {
# 'color': 'hsl(340, 82%, 52%)',
# 'label': 'Pink',
# },
# {
# 'color': 'hsl(291, 64%, 42%)',
# 'label': 'Purple',
# },
# {
# 'color': 'hsl(262, 52%, 47%)',
# 'label': 'Deep Purple',
# },
# {
# 'color': 'hsl(231, 48%, 48%)',
# 'label': 'Indigo',
# },
# {
# 'color': 'hsl(207, 90%, 54%)',
# 'label': 'Blue',
# },
# ]
CKEDITOR_5_FILE_STORAGE = "various.storage.UploadStorage"
CKEDITOR_5_CONFIGS = {
CKEDITOR_UPLOAD_PATH = "uploads/"
CKEDITOR_IMAGE_BACKEND = 'pillow'
#CKEDITOR_JQUERY_URL = '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js'
CKEDITOR_CONFIGS = {
'default': {
'language': 'cs',
'blockToolbar': [
'paragraph', 'heading1', 'heading2', 'heading3',
'|',
'bulletedList', 'numberedList',
'|',
'blockQuote',
],
'toolbar': ['sourceEditing', '|', 'heading', '|',
# 'outdent', 'indent', '|',
'bold', 'italic', 'link', 'underline', 'strikethrough',
'code',
# 'subscript', 'superscript',
# 'highlight',
'|', 'codeBlock', 'insertImage',
'bulletedList', 'numberedList', 'todoList', '|',
# 'blockQuote', '|',
# 'fontSize', 'fontFamily', 'fontColor', 'fontBackgroundColor',
# 'mediaEmbed',
'removeFormat',
# 'insertTable',
],
'image': {
'toolbar': ['imageTextAlternative', '|', 'imageStyle:alignLeft',
'imageStyle:alignRight', 'imageStyle:alignCenter', 'imageStyle:side', '|'],
'styles': [
'full',
'side',
'alignLeft',
'alignRight',
'alignCenter',
]
'entities': False,
'toolbar': [
['Source', 'ShowBlocks', '-', 'Maximize'],
['Bold', 'Italic', 'Subscript', 'Superscript', '-', 'RemoveFormat'],
['NumberedList','BulletedList','-','Blockquote','-','JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'],
['Link', 'Unlink', 'Anchor', '-', 'Image', 'Table', 'HorizontalRule'],
['Format'],
],
# 'toolbar': 'full',
'height': '40em',
'width': '100%',
'toolbarStartupExpanded': False,
'allowedContent' : True,
},
# 'table': {
# 'contentToolbar': [ 'tableColumn', 'tableRow', 'mergeTableCells',
# 'tableProperties', 'tableCellProperties' ],
# 'tableProperties': {
# 'borderColors': customColorPalette,
# 'backgroundColors': customColorPalette,
# },
# 'tableCellProperties': {
# 'borderColors': customColorPalette,
# 'backgroundColors': customColorPalette,
# }
# },
'heading' : {
'options': [
{ 'model': 'paragraph', 'title': 'Paragraph', 'class': 'ck-heading_paragraph' },
{ 'model': 'heading1', 'view': 'h1', 'title': 'Heading 1', 'class': 'ck-heading_heading1' },
{ 'model': 'heading2', 'view': 'h2', 'title': 'Heading 2', 'class': 'ck-heading_heading2' },
{ 'model': 'heading3', 'view': 'h3', 'title': 'Heading 3', 'class': 'ck-heading_heading3' },
]
},
},
'list': {
'properties': {
'styles': 'true',
'startIndex': 'true',
'reversed': 'true',
},
}
}
# Webpack loader
@ -350,11 +279,11 @@ LOGGING = {
'filters': ['Http404AsInfo'],
},
'personalni.prihlaska.form':{
'seminar.prihlaska.form':{
'handlers': ['console','registration_logfile'],
'level': 'INFO'
},
'personalni.prihlaska.problem':{
'seminar.prihlaska.problem':{
'handlers': ['console','mail_registration','registration_error_log'],
'level': 'INFO'
},
@ -413,7 +342,6 @@ SEMINAR_KONFERY_DIR = os.path.join('konfery')
KOREKTURY_PDF_DIR = os.path.join('korektury', 'pdf')
KOREKTURY_IMG_DIR = os.path.join('korektury', 'img')
CISLO_IMG_DIR = os.path.join('cislo', 'img')
SOUSTREDENI_KONTAKTNICKY_DIR = os.path.join('soustredeni', 'kontaktnicky')

View file

@ -1,3 +0,0 @@
.ck.ck-editor {
color: black !important; /* V tmavém módu zapomene CKEditor přepnout barvu textu. (Bílý text na bílém pozadí je best.) */
}

View file

@ -10,8 +10,6 @@
--orgovska-fialova: #6a0043;
--orgovska-svetla-fialova: #eee4ec;
--resitelska-fialova: #f296b3;
--resitelska-svetla-fialova: #f2E5EF;
--barva-pozadi: #fffbf6;
}

View file

@ -17,27 +17,7 @@
border: var(--orgovska-fialova) 2px dashed;
& .mam-org-only {
/* Vnitřní rámečky mají být taky vidět */
border-width: 1px;
background-color: rgba(0, 0, 0, 0.06);
}
& li {
padding: 3px 0;
margin: -2px 0;
}
}
.mam-resitel-only {
background: var(--resitelska-svetla-fialova);
padding: 10px;
margin: 10px -10px;
border: var(--resitelska-fialova) 2px dashed;
& .mam-resitel-only {
/* Vnitřní rámečky mají být taky vidět */
border-width: 1px;
background-color: rgba(0, 0, 0, 0.06);
border: 0;
}
& li {
@ -389,7 +369,7 @@ div.org_email {
}
/* td obsahující křížek v detailu řešení se nesmí smrštit na 0 */
td:has(.smazat_hodnoceni) {
.td:has(.smazat_hodnoceni) {
min-width: 20px;
padding: 3px;
}

View file

@ -15,7 +15,7 @@ urlpatterns = [
# Admin a nastroje
path('admin/', admin.site.urls), # NOQA
path("ckeditor5/", include('django_ckeditor_5.urls')),
path('ckeditor/', include('ckeditor_uploader.urls')),
# Tvorba = ročníky, čísla, problémy atd. (ma vlastni podadresare)
path('', include('tvorba.urls')),

View file

@ -1,15 +0,0 @@
# Tento soubor slouží pouze pro shell a podobné. Nikde neimportovat v kódu!
print("Naimportoval jsi `mamweb.vsechno`. 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 *

View file

@ -1,20 +1,5 @@
import django.forms
from django.contrib import admin
from django_ckeditor_5.widgets import CKEditor5Widget
from .models import Novinky
class NovinkyAdminForm(django.forms.ModelForm):
class Meta:
model = Novinky
widgets = {
'text': CKEditor5Widget,
}
fields = '__all__'
@admin.register(Novinky)
class NovinkyAdmin(admin.ModelAdmin):
form = NovinkyAdminForm
admin.site.register(Novinky)

View file

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

View file

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

View file

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

View file

@ -1,13 +0,0 @@
# 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 = [
]

View file

@ -1,35 +0,0 @@
# 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'),
),
]

View file

@ -1,14 +0,0 @@
# 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 = [
]

View file

@ -1,13 +0,0 @@
# 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 = [
]

View file

@ -1,20 +0,0 @@
# 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í'),
),
]

View file

@ -1,14 +0,0 @@
# 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 = [
]

View file

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

View file

@ -191,7 +191,7 @@ Sloupce:
</ul>
</li>
<li>Pokud nemáš důvod, deadline neměň. Sloupeček s deadlinem znamená, do kterého deadlinu se započítají body (nemusí se shodovat s deadlinem řešení).</li>
<li>Poslední sloupec je na zpětnou vazbu řešiteli, tedy (na rozdíl od Neveřejné poznámky, která je určena pro synchronizaci orgů) ji uvidí řešitelé. Změníte-li u nějakého hodnocení toto políčko, řešitel bude upozorněn emailem, pokud si tuto možnost nevypl ve svém profilu. Pohled řešitele si můžete prohlédnout <a href="{% url 'odevzdavatko_resitel_reseni' reseni.id %}">zde</a>. Pokud chcete z nějakého důvodu napsat řešitelům e-mail, klikněte na „Poslat mail všem řešitelům“.</li>
<li>Poslední sloupec je na zpětnou vazbu řešiteli, tedy (na rozdíl od Neveřejné poznámky, která je určena pro synchronizaci orgů) ji uvidí řešitelé. Zatím jen pasivně (nechodí e-mail). Pohled řešitele si můžete prohlédnout <a href="{% url 'odevzdavatko_resitel_reseni' reseni.id %}">zde</a>. Pokud chcete z nějakého důvodu napsat řešitelům e-mail, klikněte na „Poslat mail všem řešitelům“.</li>
</ol>
Další poznámky

View file

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

View file

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

View file

@ -12,7 +12,7 @@
<br>
{% for rocnik, hodnoceni, suma_bodu in podle_rocniku %}
{% for rocnik, hodnoceni in podle_rocniku %}
<h1>Ročník {{ rocnik }}</h1>
<table class="moje_reseni plne_ohranicena_tabulka">
<tr>
@ -33,7 +33,7 @@
</tr>
{% endfor %}
</table>
<p>Celkový počet bodů {{suma_bodu}}</p>
<br>
{% endfor %}

View file

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

View file

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

View file

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

View file

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

View file

@ -17,14 +17,10 @@ from decimal import Decimal
from itertools import groupby
import logging
import seminar.models as m
from . import forms as f
from .forms import OdevzdavatkoTabulkaFiltrForm as FiltrForm
from .models import Hodnoceni, Reseni
from personalni.models import Resitel, Osoba, Organizator
from tvorba.models import Problem, Deadline, Rocnik
from tvorba.utils import resi_v_rocniku
from various.models import Nastaveni
from various.views.pomocne import formularOKView
logger = logging.getLogger(__name__)
@ -44,20 +40,20 @@ logger = logging.getLogger(__name__)
class TabulkaOdevzdanychReseniView(ListView):
template_name = 'odevzdavatko/tabulka.html'
model = Hodnoceni
model = m.Hodnoceni
def inicializuj_osy_tabulky(self):
"""Vyrobí prvotní querysety pro sloupce a řádky, tj. seznam všech řešitelů a problémů"""
# FIXME: jméno metody není vypovídající...
# NOTE: Tenhle blok nemůže být přímo ve třídě, protože před vyrobením databáze neexistují ty objekty (?). TODO: Otestovat
# TODO: Prefetches, Select related, ...
self.resitele = Resitel.objects.all()
self.problemy = Problem.objects.all()
self.reseni = Reseni.objects.all()
self.resitele = m.Resitel.objects.all()
self.problemy = m.Problem.objects.all()
self.reseni = m.Reseni.objects.all()
self.aktualni_rocnik = Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci
self.aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci
if 'rocnik' in self.kwargs:
self.aktualni_rocnik = get_object_or_404(Rocnik, rocnik=self.kwargs['rocnik'])
self.aktualni_rocnik = get_object_or_404(m.Rocnik, rocnik=self.kwargs['rocnik'])
form = FiltrForm(self.request.GET, rocnik=self.aktualni_rocnik)
if form.is_valid():
@ -90,14 +86,14 @@ class TabulkaOdevzdanychReseniView(ListView):
self.resitele = self.resitele.filter(rok_maturity__gt=self.aktualni_rocnik.prvni_rok)
if problemy == FiltrForm.PROBLEMY_MOJE:
org = Organizator.objects.get(osoba__user=self.request.user)
org = m.Organizator.objects.get(osoba__user=self.request.user)
self.problemy = self.problemy.filter(
Q(autor=org)|Q(garant=org)|Q(opravovatele=org),
Q(stav=Problem.STAV_ZADANY)|Q(stav=Problem.STAV_VYRESENY),
Q(stav=m.Problem.STAV_ZADANY)|Q(stav=m.Problem.STAV_VYRESENY),
)
elif problemy == FiltrForm.PROBLEMY_LETOSNI:
self.problemy = self.problemy.filter(
Q(stav=Problem.STAV_ZADANY)|Q(stav=Problem.STAV_VYRESENY),
Q(stav=m.Problem.STAV_ZADANY)|Q(stav=m.Problem.STAV_VYRESENY),
)
#self.problemy = list(filter(lambda problem: problem.rocnik() == self.aktualni_rocnik, self.problemy)) # DB HOG? # FIXME: některé problémy nemají ročník....
# NOTE: Protože řešení odkazuje přímo na Problém a QuerySet na Hodnocení je nepolymorfní, musíme porovnávat taky s nepolymorfními Problémy.
@ -125,8 +121,8 @@ class TabulkaOdevzdanychReseniView(ListView):
ctx = super().get_context_data(*args, **kwargs)
ctx['problemy'] = self.problemy
ctx['resitele'] = self.resitele
tabulka: dict[Problem, dict[Resitel, list[tuple[Reseni, Hodnoceni]]]] = dict()
soucty: dict[Problem, dict[Resitel, Decimal]] = dict()
tabulka: dict[m.Problem, dict[m.Resitel, list[tuple[m.Reseni, m.Hodnoceni]]]] = dict()
soucty: dict[m.Problem, dict[m.Resitel, Decimal]] = dict()
def pridej_reseni(resitel, hodnoceni):
problem = hodnoceni.problem
@ -147,11 +143,11 @@ class TabulkaOdevzdanychReseniView(ListView):
for resitel in hodnoceni.reseni.resitele.all():
pridej_reseni(resitel, hodnoceni)
hodnoty: list[list[tuple[Decimal,list[tuple[Reseni, Hodnoceni]]]]] = [] # Seznam řádků výsledné tabulky podle self.resitele, v každém řádku buňky v pořadí podle self.problemy + jejich součty, v každé buňce seznam řešení k danému řešiteli a problému.
resitele_do_tabulky: list[Resitel] = []
hodnoty: list[list[tuple[Decimal,list[tuple[m.Reseni, m.Hodnoceni]]]]] = [] # Seznam řádků výsledné tabulky podle self.resitele, v každém řádku buňky v pořadí podle self.problemy + jejich součty, v každé buňce seznam řešení k danému řešiteli a problému.
resitele_do_tabulky: list[m.Resitel] = []
for resitel in self.resitele:
dostal_body = False
resiteluv_radek: list[tuple[Decimal,list[tuple[Reseni, Hodnoceni]]]] = [] # podle pořadí v self.problemy
resiteluv_radek: list[tuple[Decimal,list[tuple[m.Reseni, m.Hodnoceni]]]] = [] # podle pořadí v self.problemy
for problem in self.problemy:
if problem in tabulka and resitel in tabulka[problem]:
resiteluv_radek.append((soucty[problem][resitel], tabulka[problem][resitel]))
@ -166,7 +162,7 @@ class TabulkaOdevzdanychReseniView(ListView):
# Pro použití hacku na automatické {{form.media}} v template:
ctx['form'] = ctx['filtr']
# Pro maximum v přesměrovátku ročníků
ctx['aktualni_rocnik'] = Nastaveni.get_solo().aktualni_rocnik
ctx['aktualni_rocnik'] = m.Nastaveni.get_solo().aktualni_rocnik
ctx['barvicky'] = self.barvicky
if 'rocnik' in self.kwargs:
ctx['rocnik'] = self.kwargs['rocnik']
@ -182,7 +178,7 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi
Asi bude zastaralý v okamžiku, kdy se tenhle komentář nasadí na produkci :-)
V případě, že takové řešení existuje jen jedno, tak na něj přesměruje."""
model = Reseni
model = m.Reseni
template_name = 'odevzdavatko/seznam.html'
def get_queryset(self):
@ -194,8 +190,8 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi
if problem_id is None:
raise ValueError("Nemám problém! (To je problém!)")
resitel = Resitel.objects.get(id=resitel_id)
problem = Problem.objects.get(id=problem_id)
resitel = m.Resitel.objects.get(id=resitel_id)
problem = m.Problem.objects.get(id=problem_id)
qs = qs.filter(
problem__in=[problem],
resitele__in=[resitel],
@ -222,7 +218,17 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi
ctx["problem_id"] = self.kwargs['problem']
return ctx
HODNOCENI_INITIAL_DATA = [
## XXX: https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#avoid-anything-more-complex
class DetailReseniView(DetailView):
""" Náhled na řešení. Editace je v :py:class:`EditReseniView`. """
model = m.Reseni
template_name = 'odevzdavatko/detail.html'
def aktualni_hodnoceni(self):
self.reseni = get_object_or_404(m.Reseni, id=self.kwargs['pk'])
result = [] # Slovníky s klíči problem, body, deadline_body -- initial data pro f.OhodnoceniReseniFormSet
for hodn in m.Hodnoceni.objects.filter(reseni=self.reseni):
seznam_atributu = [
"problem",
"body",
"body_celkem",
@ -233,17 +239,7 @@ HODNOCENI_INITIAL_DATA = [
"deadline_body",
"feedback",
]
## XXX: https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#avoid-anything-more-complex
class DetailReseniView(DetailView):
""" Náhled na řešení. Editace je v :py:class:`EditReseniView`. """
model = Reseni
template_name = 'odevzdavatko/detail.html'
def aktualni_hodnoceni(self):
self.reseni = get_object_or_404(Reseni, id=self.kwargs['pk'])
result = [] # Slovníky s klíči problem, body, deadline_body -- initial data pro f.OhodnoceniReseniFormSet
for hodn in Hodnoceni.objects.filter(reseni=self.reseni):
result.append({attr: getattr(hodn, attr) for attr in HODNOCENI_INITIAL_DATA})
result.append({attr: getattr(hodn, attr) for attr in seznam_atributu})
return result
def get_context_data(self, **kw):
@ -288,12 +284,12 @@ class EditReseniView(DetailReseniView):
def hodnoceniReseniView(request, pk, *args, **kwargs):
reseni = get_object_or_404(Reseni, pk=pk)
reseni = get_object_or_404(m.Reseni, pk=pk)
success_url = reverse('odevzdavatko_detail_reseni', kwargs={'pk': pk})
formset = f.OhodnoceniReseniFormSet(request.POST, initial=[
{k: getattr(h, k) for k in HODNOCENI_INITIAL_DATA} for h in Hodnoceni.objects.filter(reseni=reseni)
])
# FIXME: Použit initial i tady a nebastlit hodnocení tak nízkoúrovňově
# Also: https://docs.djangoproject.com/en/2.2/topics/forms/modelforms/#django.forms.ModelForm
formset = f.OhodnoceniReseniFormSet(request.POST)
poznamka_form = f.PoznamkaReseniForm(request.POST, instance=reseni)
# TODO: Napsat validaci formuláře a formsetu
if not (formset.is_valid() and poznamka_form.is_valid()):
@ -304,76 +300,46 @@ def hodnoceniReseniView(request, pk, *args, **kwargs):
poznamka_form.save()
# Smažeme všechna dosavadní hodnocení tohoto řešení
qs = Hodnoceni.objects.filter(reseni=reseni)
qs = m.Hodnoceni.objects.filter(reseni=reseni)
logger.info(f"Will delete {qs.count()} objects: {qs}")
qs.delete()
# Vyrobíme nová podle formsetu
notifikace = False
for form in formset:
notifikace |= 'feedback' in form.changed_data
data_for_hodnoceni = form.cleaned_data
data_for_body = data_for_hodnoceni.copy()
del(data_for_hodnoceni["body_celkem"])
del(data_for_hodnoceni["body_neprepocitane"])
del(data_for_hodnoceni["body_neprepocitane_celkem"])
hodnoceni = Hodnoceni(
hodnoceni = m.Hodnoceni(
reseni=reseni,
**form.cleaned_data,
)
logger.info(f"Creating Hodnoceni: {hodnoceni}")
# FIXME následující kód má velmi vysokou šanci se rozbít, vymyslet, jak to udělat jinak
zmeny_bodu = [it for it in form.changed_data if it.startswith("body")]
if len(zmeny_bodu) != 0:
body_nastaveny: None | tuple[str, object] = None
def nastav_body(jake, na_kolik):
nonlocal body_nastaveny
if body_nastaveny is not None:
logger.warning(f"Hodnocení {hodnoceni} s id {hodnoceni.id} k řešení {reseni.id} mělo mít nastavené kromě {body_nastaveny[0]} na {body_nastaveny[1]} ještě další body: {jake} na {na_kolik}. Nastavuji -0.1.")
if len(zmeny_bodu) == 1:
hodnoceni.__setattr__(zmeny_bodu[0], data_for_body[zmeny_bodu[0]])
# > jedna změna je špatně, ale 4 "změny" znamenají že nebylo nic zadáno
if len(zmeny_bodu) > 1 and len(zmeny_bodu) != 4 and len(zmeny_bodu) != 2:
# 4 znamená vše už vyplněno a nic nezměněno, 2 znamená předvyplnili se součty a nic se nezměnilo
logger.warning(f"Hodnocení {hodnoceni} mělo mít nastavené víc různých bodů: {zmeny_bodu}. Nastavuji -0.1.")
hodnoceni.body = -0.1
else:
body_nastaveny = (jake, na_kolik)
hodnoceni.__setattr__(jake, na_kolik)
for key, value in data_for_body.items():
if key.startswith("body") and value is not None:
nastav_body(key, value)
# Něco se změnilo, ale nic není nastavené = něco bylo smazáno
if body_nastaveny is None:
hodnoceni.body = None
hodnoceni.save()
adresati = reseni.resitele.filter(upozornovat_na_opravy_reseni=True).values_list('osoba__email', flat=True)
if notifikace and adresati:
email = EmailMessage(
subject='Změna hodnocení odevzdaného řešení',
body=f"""Milá řešitelko, milý řešiteli,
došlo ke změně zpětné vazby k Tebou odevzdanému řešení. Zobrazit si ji můžeš na {reseni.resitel_url()}.
Tvoji organizátoři M&M
---
Nechceš-li tato upozornění dostávat, můžeš si to nastavit ve svém profilu.""",
from_email='odevzdavatko@mam.mff.cuni.cz',
bcc=adresati,
)
email.send()
return redirect(success_url)
class PrehledOdevzdanychReseni(ListView):
model = Hodnoceni
model = m.Hodnoceni
template_name = 'odevzdavatko/prehled_reseni.html'
def get_queryset(self):
if not self.request.user.is_authenticated:
raise RuntimeError("Uživatel měl být přihlášený!")
# get_or_none, aby neexistence řešitele (např. u orgů) neházela chybu
resitel = Resitel.objects.filter(osoba__user=self.request.user).first()
resitel = m.Resitel.objects.filter(osoba__user=self.request.user).first()
qs = super().get_queryset()
qs = qs.filter(reseni__resitele__in=[resitel])
# Setřídíme podle času doručení řešení, aby se netřídily podle okamžiku vyrobení Hodnocení
@ -386,12 +352,7 @@ class PrehledOdevzdanychReseni(ListView):
# Chceme to mít seřazené, takže místo comphrerehsion ručně postavíme pole polí. Django templates neumí použít OrderedDict :-/
podle_rocniku = []
for rocnik, hodnoceni in groupby(ctx['object_list'], lambda ho: ho.deadline_body.cislo.rocnik if ho.deadline_body is not None else None):
suma_bodu = 0
hodnoceni = list(hodnoceni)
for i in hodnoceni :
if i.body != None : suma_bodu += i.body
podle_rocniku.append((rocnik, hodnoceni, suma_bodu))
podle_rocniku.append((rocnik, list(hodnoceni)))
ctx['podle_rocniku'] = reversed(podle_rocniku) # Od nejnovějšího ročníku
# TODO: Umožnit stažení / zobrazení řešení
return ctx
@ -399,13 +360,13 @@ class PrehledOdevzdanychReseni(ListView):
# Přehled všech řešení kvůli debugování
class SeznamReseniView(ListView):
model = Reseni
model = m.Reseni
template_name = 'odevzdavatko/seznam.html'
class SeznamAktualnichReseniView(SeznamReseniView):
def get_queryset(self):
qs = super().get_queryset()
akt_rocnik = Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci, asi...
akt_rocnik = m.Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci, asi...
resitele = resi_v_rocniku(akt_rocnik)
qs = qs.filter(resitele__in=resitele) # FIXME: Najde řešení i ze starých ročníků, která odevzdal alespoň jeden aktuální řešitel
return qs
@ -417,7 +378,7 @@ class VlozReseniView(LoginRequiredMixin, FormView):
def form_valid(self, form):
data = form.cleaned_data
nove_reseni = Reseni.objects.create(
nove_reseni = m.Reseni.objects.create(
cas_doruceni=data['cas_doruceni'],
forma=data['forma'],
poznamka=data['poznamka'],
@ -444,35 +405,35 @@ class VlozReseniView(LoginRequiredMixin, FormView):
class NahrajReseniRozcestnikTematekView(LoginRequiredMixin, ListView):
model = Problem
model = m.Problem
template_name = 'odevzdavatko/nahraj_reseni_nadproblem.html'
def get_queryset(self):
return super().get_queryset().filter(stav=Problem.STAV_ZADANY, nadproblem__isnull=True)
return super().get_queryset().filter(stav=m.Problem.STAV_ZADANY, nadproblem__isnull=True)
class NahrajReseniView(LoginRequiredMixin, CreateView):
model = Reseni
model = m.Reseni
template_name = 'odevzdavatko/nahraj_reseni.html'
form_class = f.NahrajReseniForm
nadproblem: Problem
nadproblem: m.Problem
def setup(self, request, *args, **kwargs):
super().setup(request, *args, **kwargs)
nadproblem_id = self.kwargs["nadproblem_id"]
self.nadproblem = get_object_or_404(Problem, id=nadproblem_id)
self.nadproblem = get_object_or_404(m.Problem, id=nadproblem_id)
def get(self, request, *args, **kwargs):
# Zaříznutí nezadaných problémů
if self.nadproblem.stav != Problem.STAV_ZADANY:
if self.nadproblem.stav != m.Problem.STAV_ZADANY:
raise PermissionDenied()
# Zaříznutí starých řešitelů:
# FIXME: Je to tady dost naprasené, mělo by to asi být jinde…
osoba = Osoba.objects.get(user=self.request.user)
osoba = m.Osoba.objects.get(user=self.request.user)
resitel = osoba.resitel
if resitel.rok_maturity <= Nastaveni.get_solo().aktualni_rocnik.prvni_rok:
if resitel.rok_maturity <= m.Nastaveni.get_solo().aktualni_rocnik.prvni_rok:
return render(request, 'universal.html', {
'title': 'Nelze odevzdat',
'error': 'Zdá se, že jsi již odmaturoval/a, a tedy nemůžeš odevzdat do našeho semináře řešení.',
@ -484,7 +445,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
nadproblem_id = self.nadproblem.id
return {
"nadproblem_id": nadproblem_id,
"problem": [] if self.nadproblem.podproblem.filter(stav=Problem.STAV_ZADANY).exists() else nadproblem_id
"problem": [] if self.nadproblem.podproblem.filter(stav=m.Problem.STAV_ZADANY).exists() else nadproblem_id
}
@ -496,7 +457,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
data['prilohy'] = f.ReseniSPrilohamiFormSet()
data["nadproblem_id"] = self.nadproblem.id
data["nadproblem"] = get_object_or_404(Problem, id=self.nadproblem.id)
data["nadproblem"] = get_object_or_404(m.Problem, id=self.nadproblem.id)
return data
# FIXME prepsat tak, aby form_valid se volalo jen tehdy, kdyz je form i formset validni
@ -508,17 +469,17 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
return super().form_invalid(form)
with transaction.atomic():
self.object = form.save()
self.object.resitele.add(Resitel.objects.get(osoba__user = self.request.user))
self.object.resitele.add(m.Resitel.objects.get(osoba__user = self.request.user))
self.object.resitele.add(*form.cleaned_data["resitele"])
self.object.cas_doruceni = timezone.now()
self.object.forma = Reseni.FORMA_UPLOAD
self.object.forma = m.Reseni.FORMA_UPLOAD
self.object.save()
prilohy.instance = self.object
prilohy.save()
for hodnoceni in self.object.hodnoceni_set.all():
hodnoceni.deadline_body = Deadline.objects.filter(deadline__gte=self.object.cas_doruceni).first()
hodnoceni.deadline_body = m.Deadline.objects.filter(deadline__gte=self.object.cas_doruceni).first()
hodnoceni.save()
# Pošleme mail opravovatelům a garantovi
@ -536,7 +497,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
# FIXME: Víc informativní obsah mailů, možná vč. příloh?
prijemci = map(lambda it: it.osoba.email, prijemci)
resitel = Osoba.objects.get(user = self.request.user)
resitel = m.Osoba.objects.get(user = self.request.user)
seznam = "problému " + str(problemy[0]) if len(problemy) == 1 else 'následujícím problémům:\n' + ', \n'.join(map(str, problemy))
seznam_do_subjectu = "problému " + str(problemy[0]) + ("" if len(problemy) == 1 else f" (a dalším { len(problemy) - 1 })")
@ -551,5 +512,5 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
return formularOKView(
self.request,
text='Řešení úspěšně odevzdáno',
dalsi_odkazy=[("Odevzdat další řešení", reverse("odevzdavatko_nahraj_reseni"))],
dalsi_odkazy=[("Odevzdat další řešení", reverse("seminar_nahraj_reseni"))],
)

View file

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

View file

@ -4,7 +4,7 @@ from django.contrib.auth.forms import PasswordResetForm
from django.core.exceptions import ObjectDoesNotExist
from django.contrib.auth.models import User
from personalni.models import Skola, Resitel, Osoba
from seminar.models import Skola, Resitel, Osoba
from datetime import date
import logging
@ -27,7 +27,7 @@ class TelInput(forms.TextInput):
class UdajeForm(forms.Form):
username = None
err_logger = logging.getLogger('personalni.prihlaska.problem')
err_logger = logging.getLogger('seminar.prihlaska.problem')
jmeno = forms.CharField(label='Jméno', max_length=256, required=True)
prezdivka_resitele = forms.CharField(label='Přezdívka (veřejná)', max_length=256, required=False)
@ -62,7 +62,7 @@ class UdajeForm(forms.Form):
rok_maturity = forms.IntegerField(
label='Rok maturity',
min_value=date.today().year,
max_value=date.today().year+13,
max_value=date.today().year+8,
required=True,
)
@ -71,8 +71,6 @@ class UdajeForm(forms.Form):
zasilat_cislo_emailem = forms.BooleanField(label='Chci dostávat e-mailem upozornění na vydání nového čísla', required=False, initial=True)
spam = forms.BooleanField(label='Souhlasím se zasíláním propagačních materiálů od MFF UK', required=False)
upozornovat_na_opravy_reseni = forms.BooleanField(label='Chci dostávat emailová upozornění na změnu zpětné vazby k mým řešením', required=False, initial=True)
def clean_prezdivka_resitele(self):
prezdivka_resitele = self.cleaned_data.get('prezdivka_resitele')
if prezdivka_resitele == '':
@ -149,7 +147,7 @@ class PrihlaskaForm(PasswordResetForm, UdajeForm):
class ProfileEditForm(UdajeForm):
err_logger = logging.getLogger('personalni.prihlaska.problem.edit')
err_logger = logging.getLogger('seminar.edit.problem')
username = forms.CharField(
label='Přihlašovací jméno',
max_length=256,

View file

@ -1,13 +0,0 @@
# 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 = [
]

View file

@ -1,14 +0,0 @@
# 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 = [
]

View file

@ -1,13 +0,0 @@
# 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 = [
]

View file

@ -1,14 +0,0 @@
# 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 = [
]

View file

@ -1,22 +0,0 @@
# Generated by Django 4.2.16 on 2024-12-03 19:08
from django.db import migrations, models
def vypnuti_upozorneni_na_opravy_reseni(apps, schema_editor):
Resitel = apps.get_model('personalni', 'Resitel')
Resitel.objects.update(upozorneni=False)
class Migration(migrations.Migration):
dependencies = [
('personalni', '0017_odstrel_treenode_post'),
]
operations = [
migrations.AddField(
model_name='resitel',
name='upozorneni',
field=models.BooleanField(default=True, verbose_name='zasílat upozornění na změnu zpětné vazby k řešení emailem'),
),
migrations.RunPython(vypnuti_upozorneni_na_opravy_reseni),
]

View file

@ -1,18 +0,0 @@
# Generated by Django 4.2.18 on 2025-01-14 19:48
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('personalni', '0018_resitel_upozorneni'),
]
operations = [
migrations.RenameField(
model_name='resitel',
old_name='upozorneni',
new_name='upozornovat_na_opravy_reseni',
),
]

View file

@ -11,7 +11,7 @@ from django_countries.fields import CountryField
from reversion import revisions as reversion
from various.models import SeminarModelBase
from seminar.models.base import SeminarModelBase
logger = logging.getLogger(__name__)
@ -250,8 +250,6 @@ class Resitel(SeminarModelBase):
poznamka = models.TextField('neveřejná poznámka', blank=True,
help_text='Neveřejná poznámka k řešiteli (plain text)')
upozornovat_na_opravy_reseni = models.BooleanField('zasílat upozornění na změnu zpětné vazby k řešení emailem', default=True)
def export_row(self):
"Slovnik pro pouziti v AESOP exportu"

View file

@ -1,34 +0,0 @@
.seznam {
display: flex;
flex-direction: column;
gap: 0.3em;
}
.hint {
border: 1px solid #ccc;
padding: 0.3em 1em;
border-radius: 5px;
margin-bottom: 1em;
}
.osoba {
display: flex;
justify-content: space-between;
gap: 0.5em;
.uno {
flex: 2;
}
.dos {
flex: 2;
}
.tres {
flex: 1;
}
.grey {
opacity: 0.5;
}
}

View file

@ -1,28 +0,0 @@
{% extends "base.html" %}
{% block custom_css %}
{% load static %}
<link href="{% static 'personalni/jak_se_dozvedeli.css' %}" rel="stylesheet">
{% endblock %}
{% block content %}
<div class="seznam">
<div class="osoba hint">
<div class="uno">Jméno</div>
<div class="dos">Jak se dozvěděli</div>
<div class="tres">Datum registrace</div>
</div>
{% for osoba in object_list %}
<div class="osoba">
<div class="uno">{{ osoba.jmeno }} {{ osoba.prijmeni }}</div>
<div class="dos {% if not osoba.jak_se_dozvedeli %}grey{% endif %}">{% if osoba.jak_se_dozvedeli %} {{osoba.jak_se_dozvedeli}} {% else %} NEZADÁNO {% endif %}</div>
<div class="tres">{{ osoba.datum_registrace }}</div>
</div>
{% endfor %}
</div>
{% endblock%}

View file

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

View file

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

View file

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

View file

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

View file

@ -51,7 +51,6 @@
</h4>
<table class="form">
{% include "personalni/udaje/prihlaska_field.html" with field=form.zasilat_cislo_emailem %}
{% include "personalni/udaje/prihlaska_field.html" with field=form.upozornovat_na_opravy_reseni %}
{% include "personalni/udaje/prihlaska_field.html" with field=form.zasilat_cislo_papirove %}
{% include "personalni/udaje/prihlaska_field.html" with field=form.spam %}
{% include "personalni/udaje/prihlaska_field.html" with field=form.zasilat %}

View file

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

View file

@ -7,15 +7,15 @@ urlpatterns = [
path(
'org/rozcestnik/',
org_required(views.OrgoRozcestnikView.as_view()),
name='personalni_org_rozcestnik'
name='seminar_org_rozcestnik'
),
path('prihlaska/', views.prihlaskaView, name='personalni_prihlaska'),
path('prihlaska/', views.prihlaskaView, name='seminar_prihlaska'),
path(
'resitel/osobni-udaje/',
login_required(views.resitelEditView),
name='personalni_resitel_edit'
name='seminar_resitel_edit'
),
# Obecný view na profil -- orgům dá rozcestník, řešitelům jejich stránku
@ -33,11 +33,4 @@ urlpatterns = [
name='stari_organizatori'
),
# Zpřístupnění dat z "jak jste se o nás dozvěděli" pro orgy propagace
path(
'org/propagace/jak-se-dozvedeli/',
org_required(views.JakSeDozvedeliView.as_view()),
name='jak_se_dozvedeli'
)
]

View file

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

View file

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

View file

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

View file

@ -30,8 +30,6 @@ Jak moc by ses chtěl(a) zúčastnit následujících přednášek?
<INPUT TYPE="radio" NAME="q{{p.pk}}" VALUE="1" {% if h == 1 %} CHECKED="checked" {% endif %}> rozhodně chci
</td></tr>
<tr><td>&nbsp;</td></tr>
{% empty %}
Nejsou žádné přednášky o kterých by šlo hlasovat.
{% endfor %}
<tr><td><input name="odeslat" type="submit" value="Odeslat"></td><tr>
</table>

View file

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load humanize %}
{% load static %}
{% block content %}
<h1> Děkujeme. </h1>
{% endblock %}

View file

@ -1,5 +1,3 @@
import http
from django.shortcuts import render, get_object_or_404
from django.views import generic
from django.shortcuts import HttpResponseRedirect
@ -7,22 +5,13 @@ from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Sum
from django.forms import Form
from various.views.pomocne import formularOKView
from various.models import Nastaveni
from prednasky.models import Prednaska, Hlasovani, Seznam, STAV_NAVRH
from soustredeni.models import Soustredeni
from personalni.models import Osoba
from seminar.models import Soustredeni, Osoba
def newPrednaska(request):
# hlasovani se vztahuje k nejnovejsimu soustredeni
sous = Nastaveni.get_solo().aktualni_sous
sous = Soustredeni.objects.first()
seznam = Seznam.objects.filter(soustredeni = sous, stav = STAV_NAVRH).first()
if sous is None or seznam is None:
return render(request, 'universal.html', {
'title': "Nelze hlasovat",
'text': "Není žádný seznam přednášek, o kterém by se dalo hlasovat.",
}, status=http.HTTPStatus.NOT_FOUND)
osoba = Osoba.objects.filter(user=request.user).first()
ucastnik = osoba.plne_jmeno() + ' ' + str(osoba.id)
# obsluha formulare
@ -64,7 +53,7 @@ def newPrednaska(request):
def Prednaska_hotovo(request):
return formularOKView(request, "Děkujeme za vyplnění hlasování o přednáškách a těšíme se na soustředění.")
return render(request, 'prednasky/hotovo.html')
class MetaSeznamListView(generic.ListView):
model = Seznam
@ -77,7 +66,7 @@ class SeznamListView(generic.ListView):
def get_queryset(self):
self.seznam = get_object_or_404(Seznam, id=self.kwargs["seznam"])
prednasky = Prednaska.objects.filter(seznamy=self.seznam).order_by(
'org__osoba__user__first_name', 'org__osoba__user__last_name'
'org__user__first_name', 'org__user__last_name'
)
return prednasky

View file

@ -14,10 +14,10 @@ Django<5.0
django-reversion # Version control na datech v databázi
django-countries # Políčko ve formu / field v modelu ohledně států
django-solo # Singleton model (speciálně Nastavení)
django-ckeditor-5 # Editor htmlka (hlavně v adminu u flatpages)
django-ckeditor # Editor htmlka (hlavně v adminu u flatpages)
django-cleanup # Uklízí media/ od smazaných „databázových“ souborů
django-taggit # Taggy v djangu (speciálně zaměření problémů)
django-autocomplete-light>=3.9.0,<3.12.0 # Automatické doplňování (problémů, účastníků, …) ve formulářích
django-autocomplete-light>=3.9.0 # Automatické doplňování (problémů, účastníků, …) ve formulářích
django-imagekit # Všechny možné obrázky v Djangu
django-polymorphic # Polymorfismus na django modelech (hlavně Problém nebo treenode)
django-sitetree # Struktura stránek, hlavně pro meníčko

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,21 +0,0 @@
# 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 = [
]

View file

@ -1,59 +0,0 @@
# 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'},
),
]

View file

@ -1,150 +0,0 @@
# 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',
),
]

View file

@ -1,14 +0,0 @@
# 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 = [
]

View file

@ -1,17 +0,0 @@
# 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 = [
]

View file

@ -1,69 +0,0 @@
# 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)'},
),
]

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