diff --git a/seminar/urls.py b/seminar/urls.py index 777b40a4..125b1082 100644 --- a/seminar/urls.py +++ b/seminar/urls.py @@ -1,6 +1,7 @@ from django.urls import path, include, re_path from . import views from personalni.utils import org_required +import various.views urlpatterns = [ # path('aktualni/temata/', views.TemataRozcestnikView), @@ -67,7 +68,7 @@ urlpatterns = [ ), path( 'stav', - org_required(views.StavDatabazeView), + org_required(various.views.StavDatabazeView), name='stav_databaze' ), path( diff --git a/seminar/utils.py b/seminar/utils.py deleted file mode 100644 index 11a5ab30..00000000 --- a/seminar/utils.py +++ /dev/null @@ -1,96 +0,0 @@ - -import datetime - -from django.contrib.contenttypes.models import ContentType - -from personalni.models import Resitel -from tvorba.models import Clanek -from treenode.models import CisloNode -import treenode.treelib as t - - -def histogram(seznam): - d = {} - for i in seznam: - if i not in d: - d[i] = 0 - d[i] += 1 - return d - - -def seznam_problemu(): - """Funkce pro hledání nekonzistencí v databázi a dalších nežádoucích stavů webu/databáze. - - Nijak nesouvisí s Problémy zadanými řešitelům.""" - # FIXME: přejmenovat funkci? - # FIXME: Tak, jak je napsaná, asi spíš patří někam k views a ne do utils (?) - problemy = [] - - # Pomocna fce k formatovani problemovych hlasek - def prb(cls, msg, objs=None): - s = '%s: %s' % (cls.__name__, msg) - if objs: - s += ' [' - for o in objs: - try: - url = o.admin_url() - except: - url = None - if url: - s += '%s, ' % (url, o.pk,) - else: - s += '%s, ' % (o.pk,) - s = s[:-2] + ']' - problemy.append(s) - - # Duplicita jmen - jmena = {} - for r in Resitel.objects.all(): - j = r.osoba.plne_jmeno() - if j not in jmena: - jmena[j] = [] - jmena[j].append(r) - for j in jmena: - if len(jmena[j]) > 1: - prb(Resitel, 'Duplicitní jméno "%s"' % (j,), jmena[j]) - - # Data maturity a narození - for r in Resitel.objects.all(): - if not r.rok_maturity: - prb(Resitel, 'Neznámý rok maturity', [r]) - if r.rok_maturity and (r.rok_maturity < 1990 or r.rok_maturity > datetime.date.today().year + 10): - prb(Resitel, 'Podezřelé datum maturity', [r]) - if r.osoba.datum_narozeni and ( - r.osoba.datum_narozeni.year < 1970 or r.osoba.datum_narozeni.year > datetime.date.today().year - 12): - prb(Resitel, 'Podezřelé datum narození', [r]) -# if not r.email: -# prb(Resitel, u'Neznámý email', [r]) - - ## Kontroly konzistence databáze a TreeNodů - - # Články - for clanek in Clanek.objects.all(): - # získáme řešení svázané se článkem a z něj node ve stromě - reseni = clanek.reseni_set - if (reseni.count() != 1): - raise ValueError("Článek k sobě má nejedno řešení!") - r = reseni.first() - clanek_node = r.text_cely # vazba na ReseniNode z Reseni - # content type je věc pomáhající rozeznávat různé typy objektů v django-polymorphic - # protože isinstance vrátí vždy jen TreeNode - # https://django-polymorphic.readthedocs.io/en/stable/migrating.html - cislonode_ct = ContentType.objects.get_for_model(CisloNode) - node = clanek_node - while node is not None: - node_ct = node.polymorphic_ctype - if node_ct == cislonode_ct: # dostali jsme se k CisloNode - # zkontrolujeme, že stromové číslo odpovídá atributu - # .cislonode je opačná vazba k treenode_ptr, abychom z TreeNode dostali - # CisloNode - if clanek.cislo != node.cislonode.cislo: - prb(Clanek, "Číslo otištění uložené u článku nesedí s " - "číslem otištění podle struktury treenodů.", [clanek]) - break - node = t.get_parent(node) - - return problemy diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 10ab3116..41b33188 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -14,7 +14,6 @@ from seminar.models.nastaveni import Nastaveni from personalni.models import Resitel, Organizator from seminar.models.novinky import Novinky #from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva -from seminar import utils from treenode import treelib import treenode.templatetags as tnltt import treenode.serializers as vr @@ -658,26 +657,6 @@ class ClankyResitelView(generic.ListView): # queryset = Problem.objects.filter(stav=Problem.STAV_ZADANY).select_related('cislo_zadani__rocnik').order_by('-cislo_zadani__rocnik__rocnik', 'kod') -### Status - -def StavDatabazeView(request): -# nastaveni = Nastaveni.objects.get() - problemy = utils.seznam_problemu() - muzi = Resitel.objects.filter(osoba__pohlavi_muz=True) - zeny = Resitel.objects.filter(osoba__pohlavi_muz=False) - return render(request, 'seminar/stav_databaze.html', - { -# 'nastaveni': nastaveni, - 'problemy': problemy, - - 'resitele': Resitel.objects.all(), - 'muzi': muzi, - 'zeny': zeny, - 'jmena_muzu': utils.histogram([r.osoba.jmeno for r in muzi]), - 'jmena_zen': utils.histogram([r.osoba.jmeno for r in zeny]), - }) - - # Interní, nemá se nikdy objevit v urls (jinak to účastníci vytrolí) def formularOKView(request, text='', dalsi_odkazy: Sequence[tuple[str, str]] = ()): template_name = 'seminar/formular_ok.html' diff --git a/various/utils.py b/various/utils.py index de483cb2..3122c040 100644 --- a/various/utils.py +++ b/various/utils.py @@ -1,4 +1,8 @@ +import datetime + from django import views as DjangoViews +from django.contrib.contenttypes.models import ContentType + bez_diakritiky = ({} # FIXME: funguje jen pro český a slovenský text, jinak jsou špatně @@ -81,3 +85,98 @@ def viewMethodSwitch(get, post): return thePostView(request, *args, **kwargs) return NewView.as_view() + + +def histogram(seznam): + d = {} + for i in seznam: + if i not in d: + d[i] = 0 + d[i] += 1 + return d + + +def seznam_problemu(): + """Funkce pro hledání nekonzistencí v databázi a dalších nežádoucích stavů webu/databáze. + + Nijak nesouvisí s Problémy zadanými řešitelům.""" + # FIXME: přejmenovat funkci? + # FIXME: Tak, jak je napsaná, asi spíš patří někam k views a ne do utils (?) + + # Importy tady, aby various.utils zůstalo čisté od ostatních částí mamwebu + # obrana proti cyklickým importům... + from personalni.models import Resitel + from tvorba.models import Clanek + from treenode.models import CisloNode + import treenode.treelib as t + + problemy = [] + + # Pomocna fce k formatovani problemovych hlasek + def prb(cls, msg, objs=None): + s = '%s: %s' % (cls.__name__, msg) + if objs: + s += ' [' + for o in objs: + try: + url = o.admin_url() + except: + url = None + if url: + s += '%s, ' % (url, o.pk,) + else: + s += '%s, ' % (o.pk,) + s = s[:-2] + ']' + problemy.append(s) + + # Duplicita jmen + jmena = {} + for r in Resitel.objects.all(): + j = r.osoba.plne_jmeno() + if j not in jmena: + jmena[j] = [] + jmena[j].append(r) + for j in jmena: + if len(jmena[j]) > 1: + prb(Resitel, 'Duplicitní jméno "%s"' % (j,), jmena[j]) + + # Data maturity a narození + for r in Resitel.objects.all(): + if not r.rok_maturity: + prb(Resitel, 'Neznámý rok maturity', [r]) + if r.rok_maturity and (r.rok_maturity < 1990 or r.rok_maturity > datetime.date.today().year + 10): + prb(Resitel, 'Podezřelé datum maturity', [r]) + if r.osoba.datum_narozeni and ( + r.osoba.datum_narozeni.year < 1970 or r.osoba.datum_narozeni.year > datetime.date.today().year - 12): + prb(Resitel, 'Podezřelé datum narození', [r]) + # if not r.email: + # prb(Resitel, u'Neznámý email', [r]) + + ## Kontroly konzistence databáze a TreeNodů + + # Články + for clanek in Clanek.objects.all(): + # získáme řešení svázané se článkem a z něj node ve stromě + reseni = clanek.reseni_set + if (reseni.count() != 1): + raise ValueError("Článek k sobě má nejedno řešení!") + r = reseni.first() + clanek_node = r.text_cely # vazba na ReseniNode z Reseni + # content type je věc pomáhající rozeznávat různé typy objektů v django-polymorphic + # protože isinstance vrátí vždy jen TreeNode + # https://django-polymorphic.readthedocs.io/en/stable/migrating.html + cislonode_ct = ContentType.objects.get_for_model(CisloNode) + node = clanek_node + while node is not None: + node_ct = node.polymorphic_ctype + if node_ct == cislonode_ct: # dostali jsme se k CisloNode + # zkontrolujeme, že stromové číslo odpovídá atributu + # .cislonode je opačná vazba k treenode_ptr, abychom z TreeNode dostali + # CisloNode + if clanek.cislo != node.cislonode.cislo: + prb(Clanek, "Číslo otištění uložené u článku nesedí s " + "číslem otištění podle struktury treenodů.", [clanek]) + break + node = t.get_parent(node) + + return problemy diff --git a/various/views.py b/various/views.py index 91ea44a2..48cc435f 100644 --- a/various/views.py +++ b/various/views.py @@ -1,3 +1,25 @@ from django.shortcuts import render -# Create your views here. +from various import utils +from personalni.models import Resitel + + +def StavDatabazeView(request): + # nastaveni = Nastaveni.objects.get() + problemy = utils.seznam_problemu() + muzi = Resitel.objects.filter(osoba__pohlavi_muz=True) + zeny = Resitel.objects.filter(osoba__pohlavi_muz=False) + return render( + request, + 'seminar/stav_databaze.html', + { + # 'nastaveni': nastaveni, + 'problemy': problemy, + + 'resitele': Resitel.objects.all(), + 'muzi': muzi, + 'zeny': zeny, + 'jmena_muzu': utils.histogram([r.osoba.jmeno for r in muzi]), + 'jmena_zen': utils.histogram([r.osoba.jmeno for r in zeny]), + } + )