Browse Source

Merge branch 'data_migrations' of ledoian@gimli.ms.mff.cuni.cz:/akce/mam/git/mamweb.git

export_seznamu_prednasek
Pavel 'LEdoian' Turinsky 5 years ago
parent
commit
81d868eb06
  1. 16
      seminar/forms.py
  2. 23
      seminar/migrations/0072_auto_20191204_2257.py
  3. 64
      seminar/models.py
  4. 7
      seminar/templates/seminar/edit.html
  5. 14
      seminar/templates/seminar/tematka/rozcestnik.html
  6. 3
      seminar/testutils.py
  7. 3
      seminar/urls.py
  8. 9
      seminar/utils.py
  9. 123
      seminar/views.py

16
seminar/forms.py

@ -123,23 +123,10 @@ class PrihlaskaForm(forms.Form):
self.add_error('skola_adresa',forms.ValidationError('Je nutné vyplnit adresu školy')) self.add_error('skola_adresa',forms.ValidationError('Je nutné vyplnit adresu školy'))
class EditForm(forms.Form): class EditForm(forms.Form):
username = forms.CharField(label='Přihlašovací jméno', username = forms.CharField(label='Přihlašovací jméno',
max_length=256, max_length=256,
required=True, required=True)
help_text='Tímto jménem se následně budeš přihlašovat pro odevzdání řešení a další činnosti v semináři')
password = forms.CharField(
label='Heslo',
max_length=256,
required=False,
widget=forms.PasswordInput())
password_check = forms.CharField(
label='Ověření hesla',
max_length=256,
required=False,
widget=forms.PasswordInput())
jmeno = forms.CharField(label='Jméno', max_length=256, required=True) jmeno = forms.CharField(label='Jméno', max_length=256, required=True)
prijmeni = forms.CharField(label='Příjmení', max_length=256, required=True) prijmeni = forms.CharField(label='Příjmení', max_length=256, required=True)
@ -178,7 +165,6 @@ class EditForm(forms.Form):
max_value=date.today().year+8, max_value=date.today().year+8,
required=True) required=True)
zasilat = forms.ChoiceField(label='Kam zasílat čísla a řešení',choices = Resitel.ZASILAT_CHOICES, required=True) zasilat = forms.ChoiceField(label='Kam zasílat čísla a řešení',choices = Resitel.ZASILAT_CHOICES, required=True)
gdpr = forms.BooleanField(label='Souhlasím se zpracováním osobních údajů', required=True)
spam = forms.BooleanField(label='Souhlasím se zasíláním materiálů od MFF UK', required=False) spam = forms.BooleanField(label='Souhlasím se zasíláním materiálů od MFF UK', required=False)
# def clean_username(self): # def clean_username(self):
# err_logger = logging.getLogger('seminar.prihlaska.problem') # err_logger = logging.getLogger('seminar.prihlaska.problem')

23
seminar/migrations/0072_auto_20191204_2257.py

@ -0,0 +1,23 @@
# Generated by Django 2.2.7 on 2019-12-04 21:57
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('seminar', '0071_remove_nastaveni_aktualni_rocnik'),
]
operations = [
migrations.AddField(
model_name='treenode',
name='srolovatelne',
field=models.BooleanField(blank=True, help_text='Bude na stránce témátka možnost tuto položku skrýt', null=True, verbose_name='Srolovatelné'),
),
migrations.AddField(
model_name='treenode',
name='zajimave',
field=models.BooleanField(default=False, help_text='Zobrazí se daná věc na rozcestníku témátek', verbose_name='Zajímavé'),
),
]

64
seminar/models.py

@ -21,9 +21,9 @@ from taggit.managers import TaggableManager
from reversion import revisions as reversion from reversion import revisions as reversion
from seminar.utils import roman from seminar.utils import roman, FirstTagParser # Pro získání úryvku z TextNode
from unidecode import unidecode from unidecode import unidecode # Používám pro získání ID odkazu (ještě je to někde po někom zakomentované)
from polymorphic.models import PolymorphicModel from polymorphic.models import PolymorphicModel
@ -620,7 +620,7 @@ class Problem(SeminarModelBase,PolymorphicModel):
id = models.AutoField(primary_key = True) id = models.AutoField(primary_key = True)
# Název # Název
nazev = models.CharField('název', max_length=256) nazev = models.CharField('název', max_length=256) # Zveřejnitelný na stránky
# Problém má podproblémy # Problém má podproblémy
nadproblem = models.ForeignKey('self', verbose_name='nadřazený problém', nadproblem = models.ForeignKey('self', verbose_name='nadřazený problém',
@ -785,7 +785,10 @@ class Text(SeminarModelBase):
for tn in self.textnode_set.all(): for tn in self.textnode_set.all():
tn.save() tn.save()
def __str__(self):
parser = FirstTagParser()
parser.feed(str(self.na_web))
return parser.firstTag
class Uloha(Problem): class Uloha(Problem):
class Meta: class Meta:
@ -1239,7 +1242,14 @@ class TreeNode(PolymorphicModel):
verbose_name="další element na stejné úrovni") verbose_name="další element na stejné úrovni")
nazev = models.TextField("název tohoto node", nazev = models.TextField("název tohoto node",
help_text = "Tento název se zobrazuje v nabídkách pro výběr vhodného TreeNode", help_text = "Tento název se zobrazuje v nabídkách pro výběr vhodného TreeNode",
blank=False, null=True) blank=False,
null=True) # Nezveřejnitelný název na stránky - pouze do adminu
zajimave = models.BooleanField(default = False,
verbose_name = "Zajímavé",
help_text = "Zobrazí se daná věc na rozcestníku témátek")
srolovatelne = models.BooleanField(null = True, blank = True,
verbose_name = "Srolovatelné",
help_text = "Bude na stránce témátka možnost tuto položku skrýt")
def print_tree(self,indent=0): def print_tree(self,indent=0):
print("{}TreeNode({})".format(" "*indent,self.id)) print("{}TreeNode({})".format(" "*indent,self.id))
@ -1248,6 +1258,26 @@ class TreeNode(PolymorphicModel):
if self.succ: if self.succ:
self.succ.print_tree(indent=indent) self.succ.print_tree(indent=indent)
def getOdkazStr(self): # String na rozcestník
return self.first_child.getOdkazStr()
def getOdkaz(self): # ID HTML tagu, na který se bude scrollovat #{{self.getOdkaz}}
# Jsem si vědom, že tu potenciálně vznikají kolize.
# Přijdou mi natolik nepravděpodobné, že je neřeším
# Chtěl jsem ale hezké odkazy
string = unidecode(self.getOdkazStr())
returnVal = ""
i = 0
while len(returnVal) < 16: # Max 15 znaků
if i == len(string):
break
if string[i] == " ":
returnVal += "-"
if string[i].isalnum():
returnVal += string[i].lower()
i += 1
return returnVal
def __str__(self): def __str__(self):
if self.nazev: if self.nazev:
return self.nazev return self.nazev
@ -1283,6 +1313,9 @@ class CisloNode(TreeNode):
def aktualizuj_nazev(self): def aktualizuj_nazev(self):
self.nazev = "CisloNode: "+str(self.cislo) self.nazev = "CisloNode: "+str(self.cislo)
def getOdkazStr(self):
return "Číslo " + str(self.cislo)
class MezicisloNode(TreeNode): class MezicisloNode(TreeNode):
class Meta: class Meta:
db_table = 'seminar_nodes_mezicislo' db_table = 'seminar_nodes_mezicislo'
@ -1304,6 +1337,8 @@ class MezicisloNode(TreeNode):
else: else:
print("!!!!! Nějaké neidentifikované mezičíslo !!!!!") print("!!!!! Nějaké neidentifikované mezičíslo !!!!!")
self.nazev = "MezicisloNode: Neidentifikovatelné mezičíslo!" self.nazev = "MezicisloNode: Neidentifikovatelné mezičíslo!"
def getOdkazStr(self):
return "Obsah dostupný pouze na webu"
class TemaVCisleNode(TreeNode): class TemaVCisleNode(TreeNode):
""" Obsahuje příspěvky k tématu v daném čísle """ """ Obsahuje příspěvky k tématu v daném čísle """
@ -1318,6 +1353,9 @@ class TemaVCisleNode(TreeNode):
def aktualizuj_nazev(self): def aktualizuj_nazev(self):
self.nazev = "TemaVCisleNode: "+str(self.tema) self.nazev = "TemaVCisleNode: "+str(self.tema)
def getOdkazStr(self):
return str(self.tema)
class KonferaNode(TreeNode): class KonferaNode(TreeNode):
class Meta: class Meta:
db_table = 'seminar_nodes_konfera' db_table = 'seminar_nodes_konfera'
@ -1346,6 +1384,10 @@ class ClanekNode(TreeNode):
def aktualizuj_nazev(self): def aktualizuj_nazev(self):
self.nazev = "ClanekNode: "+str(self.clanek) self.nazev = "ClanekNode: "+str(self.clanek)
def getOdkazStr(self):
return str(self.clanek)
class UlohaZadaniNode(TreeNode): class UlohaZadaniNode(TreeNode):
class Meta: class Meta:
db_table = 'seminar_nodes_uloha_zadani' db_table = 'seminar_nodes_uloha_zadani'
@ -1360,6 +1402,10 @@ class UlohaZadaniNode(TreeNode):
def aktualizuj_nazev(self): def aktualizuj_nazev(self):
self.nazev = "UlohaZadaniNode: "+str(self.uloha) self.nazev = "UlohaZadaniNode: "+str(self.uloha)
def getOdkazStr(self):
return str(self.uloha)
class PohadkaNode(TreeNode): class PohadkaNode(TreeNode):
class Meta: class Meta:
db_table = 'seminar_nodes_pohadka' db_table = 'seminar_nodes_pohadka'
@ -1387,6 +1433,10 @@ class UlohaVzorakNode(TreeNode):
def aktualizuj_nazev(self): def aktualizuj_nazev(self):
self.nazev = "UlohaVzorakNode: "+str(self.uloha) self.nazev = "UlohaVzorakNode: "+str(self.uloha)
def getOdkazStr(self):
return str(self.uloha)
class TextNode(TreeNode): class TextNode(TreeNode):
class Meta: class Meta:
db_table = 'seminar_nodes_obsah' db_table = 'seminar_nodes_obsah'
@ -1399,6 +1449,10 @@ class TextNode(TreeNode):
def aktualizuj_nazev(self): def aktualizuj_nazev(self):
self.nazev = "TextNode: "+str(self.text) self.nazev = "TextNode: "+str(self.text)
def getOdkazStr(self):
return str(self.text)
## FIXME: Logiku přesunout do views. ## FIXME: Logiku přesunout do views.
#class VysledkyBase(SeminarModelBase): #class VysledkyBase(SeminarModelBase):
# #

7
seminar/templates/seminar/edit.html

@ -20,10 +20,6 @@
Přihlašovací údaje Přihlašovací údaje
</li><li> </li><li>
{% include "seminar/prihlaska_field.html" with field=form.username %} {% include "seminar/prihlaska_field.html" with field=form.username %}
</li><li>
{% include "seminar/prihlaska_field.html" with field=form.password %}
</li><li>
{% include "seminar/prihlaska_field.html" with field=form.password_check %}
</li><li> </li><li>
Osobní údaje Osobní údaje
</li><li> </li><li>
@ -68,9 +64,6 @@
{% include "seminar/prihlaska_field.html" with field=form.rok_maturity %} {% include "seminar/prihlaska_field.html" with field=form.rok_maturity %}
</li><li> </li><li>
{% include "seminar/prihlaska_field.html" with field=form.zasilat %} {% include "seminar/prihlaska_field.html" with field=form.zasilat %}
</li><li>
{% include "seminar/gdpr.html" %}
{% include "seminar/prihlaska_field.html" with field=form.gdpr %}
</li><li> </li><li>
{% include "seminar/prihlaska_field.html" with field=form.spam %} {% include "seminar/prihlaska_field.html" with field=form.spam %}
</li> </li>

14
seminar/templates/seminar/tematka/rozcestnik.html

@ -0,0 +1,14 @@
{% for tematko in tematka %}
<h1>{{tematko.nazev}}</h1>
<p>{{tematko.abstrakt}}</p>
<ul>
{% for cislo in tematko.cisla %}
<li>{{cislo.0.0}} -&gt; /{{tematko.kod}}/#{{cislo.0.1}}</li>
<ul>
{% for odkaz in cislo.1 %}
<li>{{odkaz.0}} -&gt; /{{tematko.kod}}/#{{odkaz.1}}</li>
{% endfor %}
</ul>
{% endfor %}
</ul>
{% endfor %}

3
seminar/testutils.py

@ -380,7 +380,8 @@ def gen_temata(rnd, rocniky, rocnik_cisla, organizatori):
kod=str(n), kod=str(n),
# atributy třídy Téma # atributy třídy Téma
tema_typ=rnd.choice(Tema.TEMA_CHOICES)[0], tema_typ=rnd.choice(Tema.TEMA_CHOICES)[0],
rocnik=rocnik rocnik=rocnik,
abstrakt = "Abstrakt tematka {}".format(n)
) )
konec_tematu = min(rnd.randint(ci, 7), len(cisla)) konec_tematu = min(rnd.randint(ci, 7), len(cisla))
for i in range(ci, konec_tematu+1): for i in range(ci, konec_tematu+1):

3
seminar/urls.py

@ -8,6 +8,9 @@ from django.contrib.auth import views as auth_views
staff_member_required = user_passes_test(lambda u: u.is_staff) staff_member_required = user_passes_test(lambda u: u.is_staff)
urlpatterns = [ urlpatterns = [
path('tematka/', views.TemataRozcestnikView),
path('tematko/<int:tematko>/', views.TematkoView),
# REDIRECTy # REDIRECTy
path('jak-resit/', RedirectView.as_view(url='/co-je-MaM/jak-resit/')), path('jak-resit/', RedirectView.as_view(url='/co-je-MaM/jak-resit/')),

9
seminar/utils.py

@ -2,9 +2,18 @@
import datetime import datetime
from django.contrib.auth.decorators import user_passes_test from django.contrib.auth.decorators import user_passes_test
from html.parser import HTMLParser
staff_member_required = user_passes_test(lambda u: u.is_staff) staff_member_required = user_passes_test(lambda u: u.is_staff)
class FirstTagParser(HTMLParser):
def __init__(self, *args, **kwargs):
self.firstTag = None
super().__init__(*args, **kwargs)
def handle_data(self, data):
if self.firstTag == None:
self.firstTag = data
def histogram(seznam): def histogram(seznam):
d = {} d = {}
for i in seznam: for i in seznam:

123
seminar/views.py

@ -16,7 +16,8 @@ from django.contrib.auth.mixins import LoginRequiredMixin
from django.db import transaction from django.db import transaction
from dal import autocomplete from dal import autocomplete
from .models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni, Organizator, Resitel, Novinky, Soustredeni_Ucastnici, Pohadka, Tema, Clanek, Osoba, Skola import seminar.models as s
from .models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni, Organizator, Resitel, Novinky, Soustredeni_Ucastnici, Pohadka, Tema, Clanek, Osoba, Skola # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci
#from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva #from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva
from . import utils from . import utils
from .unicodecsv import UnicodeWriter from .unicodecsv import UnicodeWriter
@ -74,6 +75,91 @@ def ZadaniTemataView(request):
} }
) )
#TODO na příště - implementovat DFS, které vrátí seznam objektů, jejich hloubku a objekt, který chci zobrazit,
#TODO na příště - rozmyslet, jak zobrazovat objekty - u každého Nodu se objekt, na který ukazuje jmenuje jinak, zavést metodu, která se u každé subclassy bude jmenovat stejně? __str__
#TODO na příště - v jaké formě předávat templatu? Jak řešit rozbalovací tagy?
#TODO na příště - implementace vpisování rozbalovacích tagů, vytvořit si nový objekt, který bude mít stejnou metodu jako objekty, které mají node, která bude vracet vhodný tag a prostě ji přidat do seznamu?
def vytahniZLesaSeznam(tematko, koren, pouze_zajimave=False):
returnVal = []
stack = []
stack.append((koren.first_child, 0, False)) #Tuple of node, depth and relevance
while len(stack) > 0:
wn, wd, wr = stack.pop()
if wn.succ != None:
stack.append((wn.succ, wd, wr))
if isinstance(wn, s.TemaVCisleNode):
print("TEMA")
print(wn.tema.id)
print(tematko.id)
if wn.tema.id == tematko.id:
returnVal.append((posledni_cislo, 0))
print("PRIDANO")
wr = True
wd = 1
if wn.srolovatelne:
tagOpen = s.Text(na_web = "Otevírací srolovací tag")
tagOpenNode = s.TextNode(text = tagOpen)
tagClose = s.Text(na_web = "Zavírací srolovací tag")
tagCloseNode = s.TextNode(text = tagClose)
stack.append((tagCloseNode, wd, True))
if wn.first_child != None:
stack.append((wn.first_child, wd + 1, wr))
if isinstance(wn, s.CisloNode):
posledni_cislo = wn
print(wn)
if wr:
print("ZAJIMAVE")
if pouze_zajimave:
if not wn.zajimave:
continue
returnVal.append((wn, wd))
return returnVal
def TematkoView(request):
neco
def TemataRozcestnikView(request):
print("=============================================")
nastaveni = s.Nastaveni.objects.first()
tematka_objects = s.Tema.objects.filter(rocnik=nastaveni.aktualni_rocnik())
tematka = [] #List tematka obsahuje pro kazde tematko object a list vsech TemaVCisleNodu - implementované pomocí slovníku
for tematko_object in tematka_objects:
print("AKTUALNI TEMATKO")
print(tematko_object.id)
odkazy = vytahniZLesaSeznam(tematko_object, nastaveni.aktualni_rocnik().rocniknode, pouze_zajimave = True) #Odkazy jsou tuply (node, depth) v listu
print(odkazy)
cisla = [] # List tuplů (nazev cisla, list odkazů)
vcisle = []
cislo = None
for odkaz in odkazy:
if odkaz[1] == 0:
if cislo != None:
cisla.append((cislo, vcisle))
cislo = (odkaz[0].getOdkazStr(), odkaz[0].getOdkaz())
vcisle = []
else:
print(odkaz[0].getOdkaz())
vcisle.append((odkaz[0].getOdkazStr(), odkaz[0].getOdkaz()))
if cislo != None:
cisla.append((cislo, vcisle))
print(cisla)
tematka.append({
"kod" : tematko_object.kod,
"nazev" : tematko_object.nazev,
"abstrakt" : tematko_object.abstrakt,
"obrazek": tematko_object.obrazek,
"cisla" : cisla
})
return render(request, 'seminar/tematka/rozcestnik.html', {"tematka": tematka})
#def ZadaniAktualniVysledkovkaView(request): #def ZadaniAktualniVysledkovkaView(request):
# nastaveni = get_object_or_404(Nastaveni) # nastaveni = get_object_or_404(Nastaveni)
@ -1028,15 +1114,16 @@ def prihlaska_log_gdpr_safe(logger, gdpr_logger, msg, form_data):
from django.forms.models import model_to_dict from django.forms.models import model_to_dict
def resitelEditView(request): def resitelEditView(request):
err_logger = logging.getLogger('seminar.prihlaska.problem')
## Načtení objektu Osoba a Resitel, patrici k aktuálně přihlášenému uživately ## Načtení objektu Osoba a Resitel, patrici k aktuálně přihlášenému uživately
u = request.user u = request.user
osoba_edit = Osoba.objects.get(user=u) osoba_edit = Osoba.objects.get(user=u)
resitel_edit = osoba_edit.resitel resitel_edit = osoba_edit.resitel
user_edit = osoba_edit.user user_edit = osoba_edit.user
## Vytvoření slovníku, kterým předvyplním formulář ## Vytvoření slovníku, kterým předvyplním formulář
prefill_1=model_to_dict(osoba_edit) prefill_1=model_to_dict(user_edit)
prefill_2=model_to_dict(resitel_edit) prefill_2=model_to_dict(resitel_edit)
prefill_3=model_to_dict(user_edit) prefill_3=model_to_dict(osoba_edit)
prefill_1.update(prefill_2) prefill_1.update(prefill_2)
prefill_1.update(prefill_3) prefill_1.update(prefill_3)
form = EditForm(initial=prefill_1) form = EditForm(initial=prefill_1)
@ -1044,7 +1131,35 @@ def resitelEditView(request):
if request.method == 'POST': if request.method == 'POST':
form = EditForm(request.POST) form = EditForm(request.POST)
if form.is_valid(): if form.is_valid():
osoba_edit.prijmeni = 'NOVOTA' ## Změny v osobě
fcd = form.cleaned_data
osoba_edit.jmeno = fcd['jmeno']
osoba_edit.prijmeni = fcd['prijmeni']
osoba_edit.pohlavi_muz = fcd['pohlavi_muz']
osoba_edit.email = fcd['email']
osoba_edit.telefon = fcd['telefon']
osoba_edit.ulice = fcd['ulice']
osoba_edit.mesto = fcd['mesto']
osoba_edit.psc = fcd['psc']
## Změny v osobě s podmínkami
if fcd.get('spam',False):
osoba_edit.datum_souhlasu_zasilani = date.today()
if fcd.get('stat','') in ('CZ','SK'):
osoba_edit.stat = fcd['stat']
else:
## Neznámá země
msg = "Unknown country {}".format(fcd['stat_text'])
## Změny v řešiteli
resitel_edit.skola = fcd['skola']
resitel_edit.rok_maturity = fcd['rok_maturity']
resitel_edit.zasilat = fcd['zasilat']
if fcd.get('skola'):
resitel_edit.skola = fcd['skola']
else:
# Unknown school - log it
msg = "Unknown school {}, {}".format(fcd['skola_nazev'],fcd['skola_adresa'])
resitel_edit.save()
osoba_edit.save() osoba_edit.save()
return HttpResponseRedirect('/thanks/') return HttpResponseRedirect('/thanks/')
else: else:

Loading…
Cancel
Save