Merge branch (quasi) 'data_migrations' into test
data_migrations jsem nepushnul, takže auto-popisek kecá.
This commit is contained in:
commit
0f0d73f122
29 changed files with 804 additions and 111 deletions
513
db_compare.py
Executable file
513
db_compare.py
Executable file
|
@ -0,0 +1,513 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import psycopg2
|
||||
import psycopg2.extras
|
||||
|
||||
OLD_DB = "mam_old"
|
||||
NEW_DB = "mamweb"
|
||||
|
||||
oldconn = psycopg2.connect(f"dbname={OLD_DB}")
|
||||
newconn = psycopg2.connect(f"dbname={NEW_DB}")
|
||||
|
||||
oldcur = oldconn.cursor(cursor_factory=psycopg2.extras.DictCursor)
|
||||
newcur = newconn.cursor(cursor_factory=psycopg2.extras.DictCursor)
|
||||
|
||||
|
||||
# Uses global variables oldcur, newcur!
|
||||
def execute_simple(old_query, new_query=None):
|
||||
if new_query is None:
|
||||
new_query = old_query
|
||||
|
||||
oldcur.execute(old_query)
|
||||
newcur.execute(new_query)
|
||||
|
||||
if oldcur.rowcount != newcur.rowcount:
|
||||
raise ValueError(f"Queries '{old_query}' and '{new_query}' returned different number of rows ({oldcur.rowcount} and {newcur.rowcount})")
|
||||
|
||||
return(oldcur.fetchall(), newcur.fetchall())
|
||||
|
||||
def check_same(old_row, new_row, old_fields, new_fields=None):
|
||||
if type(old_fields) != list:
|
||||
old_fields = [old_fields]
|
||||
|
||||
if new_fields is None:
|
||||
new_fields = old_fields
|
||||
|
||||
fields = zip(old_fields, new_fields)
|
||||
|
||||
for old_field, new_field in fields:
|
||||
if old_row[old_field] == new_row[new_field]:
|
||||
continue
|
||||
raise ValueError(f"Fields '{old_field}'({old_row[old_field]}) and '{new_field}'({new_row[new_field]}) differs for rows \n'{old_row}' and \n'{new_row}'")
|
||||
return True
|
||||
|
||||
def get_user_id_for_org_id(org_id):
|
||||
query = """SELECT auth_user.id FROM auth_user
|
||||
INNER JOIN seminar_osoby ON seminar_osoby.user_id = auth_user.id
|
||||
INNER JOIN seminar_organizator ON seminar_organizator.osoba_id = seminar_osoby.id
|
||||
WHERE seminar_organizator.id = %s """
|
||||
|
||||
newcur.execute(query,(org_id,))
|
||||
return newcur.fetchone()['id']
|
||||
|
||||
|
||||
|
||||
|
||||
def check_skola():
|
||||
old_query = "SELECT * FROM seminar_skoly ORDER BY id"
|
||||
|
||||
old_res, new_res = execute_simple(old_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n,['id','aesop_id','izo','nazev','kratky_nazev','ulice','mesto','psc','stat','je_zs','je_ss','poznamka'])
|
||||
|
||||
def check_resitel():
|
||||
old_query = 'SELECT * FROM seminar_resitele ORDER BY id'
|
||||
new_query = '''SELECT seminar_resitele.id, skola_id, rok_maturity, zasilat, seminar_resitele.poznamka,
|
||||
o.jmeno AS jmeno, o.prijmeni AS prijmeni, o.user_id AS user_id, o.pohlavi_muz AS pohlavi_muz, o.email AS email, o.telefon AS telefon, o.datum_narozeni AS datum_narozeni,
|
||||
o.datum_souhlasu_udaje AS datum_souhlasu_udaje, o.datum_souhlasu_zasilani AS datum_souhlasu_zasilani, o.datum_registrace AS datum_prihlaseni, o.ulice AS ulice, o.mesto AS mesto, o.psc AS psc, o.stat AS stat
|
||||
FROM seminar_resitele JOIN seminar_osoby AS o ON seminar_resitele.osoba_id = o.id ORDER BY seminar_resitele.id'''
|
||||
|
||||
old_res, new_res = execute_simple(old_query,new_query)
|
||||
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
fields_osoba = [
|
||||
'jmeno',
|
||||
'prijmeni',
|
||||
'user_id',
|
||||
'pohlavi_muz',
|
||||
#'email', #vyreseno separatne
|
||||
'telefon',
|
||||
'datum_narozeni',
|
||||
'datum_souhlasu_udaje',
|
||||
'datum_souhlasu_zasilani',
|
||||
'datum_prihlaseni',
|
||||
'ulice',
|
||||
'mesto',
|
||||
'psc',
|
||||
'stat',
|
||||
]
|
||||
fields_keep = [
|
||||
'id',
|
||||
'skola_id',
|
||||
'rok_maturity',
|
||||
'zasilat',
|
||||
'poznamka',
|
||||
]
|
||||
fields = fields_keep+fields_osoba
|
||||
for o,n in res:
|
||||
check_same(o,n,fields)
|
||||
if o['email'] != n['email'] and o['email'] != '':
|
||||
print(f"WARNING: Emails differ: old: {o['email']}, new: {n['email']}")
|
||||
|
||||
def check_reseni():
|
||||
# Migrace 0058 zamerne meni (zmensuje) pocet reseni, aby kazdy clanek mel
|
||||
# jen jedno reseni (s vice resiteli, coz postaru neslo)
|
||||
# Kvuli tomu je potreba kontrolovat dve veci:
|
||||
# 1) Ze kazdy resitel dostal za kazdy problem spravne bodu
|
||||
# 2) Ze detaily reseni zustaly zachovany
|
||||
|
||||
# Cast 1)
|
||||
old_query = 'SELECT * FROM seminar_reseni ORDER BY problem_id, resitel_id, body, timestamp'
|
||||
new_query = '''SELECT seminar_reseni.id, forma, seminar_reseni.poznamka, cas_doruceni, hodnoceni.problem_id AS problem_id, hodnoceni.body AS body, hodnoceni.cislo_body_id AS cislo_body_id, res.id AS resitel_id
|
||||
FROM seminar_reseni
|
||||
JOIN seminar_hodnoceni AS hodnoceni ON seminar_reseni.id = hodnoceni.reseni_id
|
||||
JOIN seminar_reseni_resitele AS rr ON seminar_reseni.id = rr.reseni_id
|
||||
JOIN seminar_resitele AS res ON res.id = rr.resitele_id
|
||||
ORDER BY problem_id, resitel_id, body, cas_doruceni'''
|
||||
|
||||
# Po spojeni nekterych problemu se lisi casy doruceni a poznamky, proto je nebudeme kontrolovat (jde v podstate o triviality, tak je to snad jedno)
|
||||
same_fields = ['forma', 'problem_id', 'body', 'cislo_body_id', 'resitel_id']
|
||||
renamed_fields = [
|
||||
#('timestamp', 'cas_doruceni'),
|
||||
]
|
||||
old_fields = same_fields + [f[0] for f in renamed_fields]
|
||||
new_fields = same_fields + [f[1] for f in renamed_fields]
|
||||
|
||||
old_res, new_res = execute_simple(old_query, new_query)
|
||||
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n,old_fields, new_fields)
|
||||
|
||||
# Cast 2)
|
||||
# Query se lisi tim, ze uz nejoinujeme resitele.
|
||||
old_query = 'SELECT * FROM seminar_reseni ORDER BY id'
|
||||
new_query = '''SELECT seminar_reseni.id, forma, poznamka, cas_doruceni AS timestamp, h.problem_id AS problem_id, h.body AS body, h.cislo_body_id AS cislo_body_id
|
||||
FROM seminar_reseni
|
||||
JOIN seminar_hodnoceni AS h ON h.reseni_id = seminar_reseni.id
|
||||
ORDER BY id'''
|
||||
|
||||
# execute_simple kontroluje stejnost poctu radku, to nechceme.
|
||||
oldcur.execute(old_query)
|
||||
newcur.execute(new_query)
|
||||
old_res, new_res = oldcur.fetchall(), newcur.fetchall()
|
||||
# Zkontrolujeme, ze pro kazde nove reseni ma stare reseni spravna data.
|
||||
new_ids = [n['id'] for n in new_res]
|
||||
spravna_old = list(filter(lambda o: o['id'] in new_ids, old_res))
|
||||
res = zip(spravna_old,new_res)
|
||||
for o,n in res:
|
||||
# Tady by se poznamky i timestampy mely zachovat
|
||||
# Z nejakeho duvodu se ale poznamky lisi ve whitespace, tak je zkontrolujeme separatne
|
||||
check_same(o,n,['id', 'forma', 'timestamp', 'problem_id', 'body', 'cislo_body_id'])
|
||||
old_pozn = o['poznamka'].strip()
|
||||
new_pozn = n['poznamka'].strip()
|
||||
if old_pozn != new_pozn:
|
||||
raise ValueError('Poznamky se lisi pro radky {dict(o)} a {dict(n)}')
|
||||
|
||||
|
||||
|
||||
def check_organizator():
|
||||
old_query = 'SELECT * FROM seminar_organizator ORDER BY id'
|
||||
new_query = '''SELECT seminar_organizator.id AS id, studuje, strucny_popis_organizatora, users.id AS uid, osoba.prezdivka AS o_prezdivka, osoba.foto AS o_foto, organizuje_od, organizuje_do
|
||||
FROM seminar_organizator
|
||||
JOIN seminar_osoby AS osoba ON osoba_id = osoba.id
|
||||
JOIN auth_user AS users ON osoba.user_id = users.id
|
||||
ORDER BY seminar_organizator.id'''
|
||||
|
||||
same_fields = ['studuje', 'strucny_popis_organizatora']
|
||||
renamed_fields = [
|
||||
('user_id', 'uid'),
|
||||
#('prezdivka', 'o_prezdivka'),
|
||||
('foto', 'o_foto'),
|
||||
]
|
||||
old_fields = same_fields + [f[0] for f in renamed_fields]
|
||||
new_fields = same_fields + [f[1] for f in renamed_fields]
|
||||
|
||||
old_res, new_res = execute_simple(old_query,new_query)
|
||||
res = zip(old_res, new_res)
|
||||
for o,n in res:
|
||||
check_same(o,n,old_fields, new_fields)
|
||||
# organizuje od, do:
|
||||
# Migrace prirazuje aktualni casovou zonu, takze chceme tady rucne vynutit CET.
|
||||
from datetime import timedelta, timezone
|
||||
cet = timezone(timedelta(hours=1))
|
||||
if o['organizuje_od_roku'] is None and n['organizuje_od'] is None:
|
||||
pass
|
||||
elif o['organizuje_od_roku'] != n['organizuje_od'].astimezone(cet).year:
|
||||
raise ValueError(f'Not matching organizuje_od for org id={o["id"]}: old {o["organizuje_od_roku"]}, new {n["organizuje_od"]}')
|
||||
if o['organizuje_do_roku'] is None and n['organizuje_do'] is None:
|
||||
pass
|
||||
elif o['organizuje_do_roku'] != n['organizuje_do'].astimezone(cet).year:
|
||||
raise ValueError(f'Not matching organizuje_do for org id={o["id"]}: old {o["organizuje_do_roku"]}, new {n["organizuje_do"]}')
|
||||
if o['prezdivka'] == n['o_prezdivka']:
|
||||
continue
|
||||
if o['prezdivka'] is None and n['o_prezdivka'] == '':
|
||||
continue
|
||||
raise ValueError(f'Not matching prezdivka for org id={o["id"]}: old {o["prezdivka"]}, new {n["o_prezdivka"]}')
|
||||
|
||||
|
||||
def check_rocnik():
|
||||
old_query = "SELECT * FROM seminar_rocniky ORDER BY id"
|
||||
|
||||
old_res, new_res = execute_simple(old_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n,['id','prvni_rok', 'rocnik', 'exportovat'])
|
||||
|
||||
def check_cislo():
|
||||
old_query = "SELECT * FROM seminar_cisla ORDER BY id"
|
||||
|
||||
old_res, new_res = execute_simple(old_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n, ['id','rocnik_id','cislo', 'datum_vydani','datum_deadline','verejne','poznamka','pdf'],
|
||||
['id','rocnik_id','poradi','datum_vydani','datum_deadline','verejne','poznamka','pdf'])
|
||||
|
||||
def check_priloha_reseni():
|
||||
old_query = "SELECT * FROM seminar_priloha_reseni"
|
||||
|
||||
old_res, new_res = execute_simple(old_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n, ['id','reseni_id', 'timestamp', 'soubor', 'poznamka'],
|
||||
['id','reseni_id', 'vytvoreno', 'soubor', 'poznamka'])
|
||||
|
||||
def check_soustredeni():
|
||||
old_query = "SELECT * FROM seminar_soustredeni ORDER BY id"
|
||||
|
||||
old_res, new_res = execute_simple(old_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n,['id','rocnik_id','datum_zacatku','datum_konce','verejne','misto','text','typ','exportovat'])
|
||||
#Kontrola ucasnici, organizatori v samostatnych funkcich
|
||||
|
||||
def check_soustredeni_ucastnici():
|
||||
old_query = "SELECT * FROM seminar_soustredeni_ucastnici ORDER BY id"
|
||||
|
||||
old_res, new_res = execute_simple(old_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n,['id','resitel_id','soustredeni_id','poznamka'])
|
||||
|
||||
def check_soustredeni_organizatori():
|
||||
old_query = "SELECT * FROM seminar_soustredeni_organizatori ORDER BY id"
|
||||
|
||||
old_res, new_res = execute_simple(old_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n,['id','organizator_id','soustredeni_id','poznamka'])
|
||||
|
||||
def check_nastaveni():
|
||||
old_query = "SELECT * FROM seminar_nastaveni ORDER BY id"
|
||||
|
||||
old_res, new_res = execute_simple(old_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n,['id','aktualni_cislo_id'])
|
||||
|
||||
def check_novinky():
|
||||
old_query = "SELECT * FROM seminar_novinky ORDER BY id"
|
||||
|
||||
old_res, new_res = execute_simple(old_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n,['id','datum','text','obrazek','zverejneno'])
|
||||
if get_user_id_for_org_id(n['autor_id']) != o['autor_id']:
|
||||
raise ValueError("Nesedi autori u novinek")
|
||||
|
||||
def check_pohadka():
|
||||
old_query = "SELECT * FROM seminar_pohadky ORDER BY id"
|
||||
new_query = """SELECT sp.id AS id, sp.autor_id AS autor_id, sp.vytvoreno AS vytvoreno, snp.treenode_ptr_id AS treenode_ptr_id, st.na_web AS text,
|
||||
zn_pred.uloha_id AS uloha_pred, zn_po.uloha_id AS uloha_po
|
||||
FROM seminar_pohadky AS sp
|
||||
-- Text pohádky
|
||||
INNER JOIN seminar_nodes_pohadka AS snp ON sp.id = snp.pohadka_id
|
||||
INNER JOIN seminar_nodes_treenode AS snt ON snt.id = snp.treenode_ptr_id
|
||||
INNER JOIN seminar_nodes_obsah AS sno ON sno.treenode_ptr_id = snt.first_child_id
|
||||
INNER JOIN seminar_texty AS st ON sno.text_id = st.id
|
||||
-- Predchozí úloha
|
||||
LEFT OUTER JOIN seminar_nodes_treenode AS ztn_pred ON ztn_pred.succ_id = snt.id
|
||||
LEFT OUTER JOIN seminar_nodes_uloha_zadani AS zn_pred ON zn_pred.treenode_ptr_id = ztn_pred.id
|
||||
-- Následující úloha
|
||||
LEFT OUTER JOIN seminar_nodes_uloha_zadani AS zn_po ON zn_po.treenode_ptr_id = snt.succ_id
|
||||
|
||||
ORDER BY sp.id"""
|
||||
|
||||
old_res, new_res = execute_simple(old_query,new_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n,['id','timestamp','text'],['id','vytvoreno','text'])
|
||||
if o['autor_id'] is not None:
|
||||
if get_user_id_for_org_id(n['autor_id']) != o['autor_id']:
|
||||
raise ValueError("Nesedi autori u pohadky")
|
||||
# Správné úlohy
|
||||
# NOTE: o['pred'] rika, zda je pohadka pred ulohou, nikoliv zda je relevantni uloha pred pohadkou!
|
||||
spravny_klic = 'uloha_po' if o['pred'] else 'uloha_pred'
|
||||
if o['uloha_id'] != n[spravny_klic]:
|
||||
raise ValueError(f"Pohádka přidružená ke špatné úloze! old: {o['uloha_id']}, new: {n[spravny_klic]}, pozice: {spravny_klic}")
|
||||
|
||||
|
||||
# Problémy jsou rozdělené podle typů:
|
||||
def check_problem_common():
|
||||
old_query = "SELECT id, nazev, stav, kod, autor_id, text_org, timestamp, typ FROM seminar_problemy ORDER BY id"
|
||||
new_query = """SELECT sp.id AS id, sp.nazev AS nazev, sp.stav AS stav, sp.kod AS kod, au.id AS autor_id, sp.poznamka AS poznamka, sp.vytvoreno AS vytvoreno
|
||||
FROM seminar_problemy AS sp
|
||||
LEFT OUTER JOIN seminar_organizator AS so ON sp.autor_id = so.id
|
||||
LEFT OUTER JOIN seminar_osoby AS sos ON so.osoba_id = sos.id
|
||||
LEFT OUTER JOIN auth_user AS au ON sos.user_id = au.id
|
||||
ORDER BY sp.id"""
|
||||
|
||||
same_fields = ['id', 'nazev', 'stav', 'autor_id', 'kod']
|
||||
renamed_fields = [
|
||||
('text_org', 'poznamka'),
|
||||
('timestamp', 'vytvoreno'),
|
||||
]
|
||||
old_fields = same_fields + [f[0] for f in renamed_fields]
|
||||
new_fields = same_fields + [f[1] for f in renamed_fields]
|
||||
|
||||
old_res, new_res = execute_simple(old_query,new_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n, old_fields, new_fields)
|
||||
|
||||
# Opravovatelé
|
||||
# Po staru byli opravovatele organizatori, takze je potreba je dohledat.
|
||||
old_query = """SELECT seminar_problemy.id, org.id AS opravovatel_id FROM seminar_problemy
|
||||
JOIN seminar_organizator AS org ON seminar_problemy.opravovatel_id = org.user_id;"""
|
||||
new_query = "SELECT problem_id, organizator_id FROM seminar_problemy_opravovatele"
|
||||
|
||||
# Simple cursors
|
||||
#oldcur = oldconn.cursor()
|
||||
oldcur.execute(old_query)
|
||||
old_results = oldcur.fetchall()
|
||||
#newcur = newconn.cursor()
|
||||
newcur.execute(new_query)
|
||||
new_results = newcur.fetchall()
|
||||
|
||||
for oldr in old_results:
|
||||
if oldr not in new_results:
|
||||
raise ValueError(f'Opravovatel pair {oldr} not found in new db.')
|
||||
|
||||
# Zaměření se vyřeší okometricky (#1186)
|
||||
|
||||
|
||||
def check_uloha():
|
||||
old_query = "SELECT * FROM seminar_problemy WHERE typ = 'uloha' ORDER BY id"
|
||||
new_query = """SELECT cislo_zadani_id, cislo_reseni_id, problem_ptr_id, max_body, COALESCE(uzt.na_web, '') AS text_zadani, COALESCE(uvt.na_web, '') AS text_reseni, cislo_deadline_id
|
||||
FROM seminar_ulohy
|
||||
-- Problém:
|
||||
JOIN seminar_problemy AS problem ON problem_ptr_id = problem.id
|
||||
-- Text zadání:
|
||||
-- ZadaniNode a VzorakNode maji existovat vzdy, ale obsah nemusi (pokud ho nemaji)
|
||||
INNER JOIN seminar_nodes_uloha_zadani AS uzn ON problem.id = uzn.uloha_id
|
||||
INNER JOIN seminar_nodes_treenode AS uztn ON uztn.id = uzn.treenode_ptr_id
|
||||
LEFT OUTER JOIN seminar_nodes_obsah AS uzo ON uzo.treenode_ptr_id = uztn.first_child_id
|
||||
LEFT OUTER JOIN seminar_texty AS uzt ON uzo.text_id = uzt.id
|
||||
-- Text vzoráku:
|
||||
INNER JOIN seminar_nodes_uloha_vzorak AS uvn ON problem.id = uvn.uloha_id
|
||||
INNER JOIN seminar_nodes_treenode AS uvtn ON uvtn.id = uvn.treenode_ptr_id
|
||||
LEFT OUTER JOIN seminar_nodes_obsah AS uvo ON uvo.treenode_ptr_id = uvtn.first_child_id
|
||||
LEFT OUTER JOIN seminar_texty AS uvt ON uvo.text_id = uvt.id
|
||||
|
||||
ORDER BY problem_ptr_id"""
|
||||
|
||||
same_fields = ['cislo_zadani_id', 'cislo_reseni_id', 'text_zadani', 'text_reseni']
|
||||
renamed_fields = [
|
||||
('id', 'problem_ptr_id'),
|
||||
('body', 'max_body'),
|
||||
]
|
||||
old_fields = same_fields + [f[0] for f in renamed_fields]
|
||||
new_fields = same_fields + [f[1] for f in renamed_fields]
|
||||
|
||||
old_res, new_res = execute_simple(old_query, new_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n, old_fields, new_fields)
|
||||
# Datum deadline vypadá prázdně, tak to budeme předpokládat.
|
||||
if n['cislo_deadline_id'] is not None:
|
||||
raise ValueError("Úloha má deadline.")
|
||||
|
||||
def check_tema():
|
||||
old_query = """SELECT text_zadani, text_reseni, typ, c.rocnik_id AS rocnik_id
|
||||
FROM seminar_problemy
|
||||
LEFT OUTER JOIN seminar_cisla AS c ON c.id = cislo_zadani_id
|
||||
WHERE typ IN ('tema', 'serial')
|
||||
ORDER BY seminar_problemy.id"""
|
||||
new_query = """SELECT tema_typ, COALESCE(zad_text.na_web, '') AS text_zadani, COALESCE(res_text.na_web, '') AS text_reseni, rn.rocnik_id AS rocnik_id
|
||||
FROM seminar_temata
|
||||
-- Problém:
|
||||
JOIN seminar_problemy AS problem ON problem_ptr_id = problem.id
|
||||
-- Text:
|
||||
-- TvCNode má dva potomky, oba TextNode. První drží původní text zadání, druhý řešení.
|
||||
INNER JOIN seminar_nodes_temavcisle AS tvcn ON tvcn.tema_id = id
|
||||
INNER JOIN seminar_nodes_treenode AS ttn ON tvcn.treenode_ptr_id = ttn.id
|
||||
LEFT OUTER JOIN seminar_nodes_treenode AS zad_tn ON ttn.first_child_id = zad_tn.id -- jen 33 z nich ma zadani
|
||||
LEFT OUTER JOIN seminar_nodes_treenode AS res_tn ON zad_tn.succ_id = res_tn.id -- jen 4 z nich ma reseni
|
||||
LEFT OUTER JOIN seminar_nodes_obsah AS zad_on ON zad_on.treenode_ptr_id = zad_tn.id
|
||||
LEFT OUTER JOIN seminar_nodes_obsah AS res_on ON res_on.treenode_ptr_id = res_tn.id
|
||||
LEFT OUTER JOIN seminar_texty AS zad_text ON zad_on.text_id = zad_text.id
|
||||
LEFT OUTER JOIN seminar_texty AS res_text ON res_on.text_id = res_text.id -- vsechny 4
|
||||
-- Ročník tématu:
|
||||
-- Podle rootu TvCN
|
||||
LEFT OUTER JOIN seminar_nodes_rocnik AS rn ON ttn.root_id = rn.treenode_ptr_id
|
||||
|
||||
ORDER BY problem_ptr_id"""
|
||||
same_fields = ['text_zadani', 'text_reseni', 'rocnik_id']
|
||||
renamed_fields = [
|
||||
('typ', 'tema_typ'),
|
||||
]
|
||||
old_fields = same_fields + [f[0] for f in renamed_fields]
|
||||
new_fields = same_fields + [f[1] for f in renamed_fields]
|
||||
|
||||
old_res, new_res = execute_simple(old_query, new_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
check_same(o,n, old_fields, new_fields)
|
||||
|
||||
def check_konfera():
|
||||
old_query = "SELECT * FROM seminar_problemy WHERE typ = 'konfera'"
|
||||
new_query = "SELECT * FROM seminar_konfera"
|
||||
|
||||
oldcur.execute(old_query)
|
||||
newcur.execute(new_query)
|
||||
|
||||
if oldcur.rowcount != 0 or newcur.rowcount != 0:
|
||||
raise ValueError('There exists a Konfera!')
|
||||
|
||||
def check_org_clanek():
|
||||
old_query = "SELECT * FROM seminar_problemy WHERE typ = 'org-clanek'"
|
||||
|
||||
oldcur.execute(old_query)
|
||||
|
||||
if oldcur.rowcount != 0:
|
||||
raise ValueError('There exists a Org-clanek!')
|
||||
|
||||
def check_res_clanek():
|
||||
# Dva(!) články mají text (zadání), který se má zachovat.
|
||||
old_query = "SELECT * FROM seminar_problemy WHERE typ = 'res-clanek' ORDER BY id"
|
||||
new_query = """SELECT cislo_id, text.na_web AS text_zadani
|
||||
FROM seminar_clanky
|
||||
JOIN seminar_problemy AS problem ON problem_ptr_id = problem.id
|
||||
INNER JOIN seminar_hodnoceni AS hodn ON problem.id = hodn.problem_id
|
||||
INNER JOIN seminar_reseni AS rese ON rese.id = hodn.reseni_id
|
||||
INNER JOIN seminar_nodes_otistene_reseni AS rn ON rese.text_cely_id = rn.treenode_ptr_id -- Tenhle radek neni potreba, ale ujistuje se mj. o spravnem typu TreeNode.
|
||||
INNER JOIN seminar_nodes_treenode AS tn ON rn.treenode_ptr_id = tn.id
|
||||
-- Nektere clanky vubec nemely text, tak jim migr 0058 nevyrobila dalsi treenody
|
||||
LEFT OUTER JOIN seminar_nodes_obsah AS son ON son.treenode_ptr_id = tn.first_child_id
|
||||
LEFT OUTER JOIN seminar_texty AS text ON text.id = son.text_id
|
||||
|
||||
ORDER BY problem_ptr_id"""
|
||||
same_fields = ['text_zadani']
|
||||
renamed_fields = [
|
||||
('cislo_zadani_id', 'cislo_id'),
|
||||
]
|
||||
old_fields = same_fields + [f[0] for f in renamed_fields]
|
||||
new_fields = same_fields + [f[1] for f in renamed_fields]
|
||||
|
||||
old_res, new_res = execute_simple(old_query, new_query)
|
||||
res = zip(old_res,new_res)
|
||||
|
||||
for o,n in res:
|
||||
# text_zadani po novu mohl byt None
|
||||
if n['text_zadani'] is None:
|
||||
n['text_zadani'] = ''
|
||||
check_same(o,n, old_fields, new_fields)
|
||||
assert(o['text_reseni'] == '')
|
||||
|
||||
def check_untyped_problem():
|
||||
old_query = "SELECT * FROM seminar_problemy WHERE typ NOT IN ('uloha', 'tema', 'serial', 'konfera', 'org-clanek', 'res-clanek')"
|
||||
|
||||
oldcur.execute(old_query)
|
||||
|
||||
if oldcur.rowcount != 0:
|
||||
raise ValueError('There exists a Problem without type!')
|
||||
|
||||
|
||||
|
||||
check_skola()
|
||||
check_resitel()
|
||||
check_reseni()
|
||||
check_organizator()
|
||||
check_rocnik()
|
||||
check_cislo()
|
||||
check_priloha_reseni()
|
||||
check_soustredeni()
|
||||
check_soustredeni_ucastnici()
|
||||
check_soustredeni_organizatori()
|
||||
check_nastaveni()
|
||||
check_novinky()
|
||||
check_pohadka()
|
||||
|
||||
check_problem_common()
|
||||
check_uloha()
|
||||
check_tema()
|
||||
check_konfera()
|
||||
check_org_clanek()
|
||||
check_res_clanek()
|
||||
check_untyped_problem()
|
3
deploy_v2/README
Normal file
3
deploy_v2/README
Normal file
|
@ -0,0 +1,3 @@
|
|||
Tahle slozka obsahuje vsechny detaily a popisy, jak nasadit "druhou verzi" M&M webu.
|
||||
|
||||
TODO: chybi tu popis na zprovozneni flatpages, na loaddata &c.
|
38
deploy_v2/pre_migration.py
Executable file
38
deploy_v2/pre_migration.py
Executable file
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import django
|
||||
|
||||
#### Inicializace Djanga
|
||||
sys.path.append(os.path.dirname(os.path.realpath(__file__))+'/..')
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mamweb.settings')
|
||||
django.setup()
|
||||
|
||||
## Pozor, nejde pouzit ORM, protoze kod je na jine verzi nez databaze a nejde namigrovat.
|
||||
from django.db import connection
|
||||
|
||||
|
||||
def smaz_zle_clanky():
|
||||
# Tyhle clanky vubec nejsou clanky, bude potreba je udelat cele jinak a spravne.
|
||||
#m.Problem.objects.filter(id__in=[1981, 1970, 2222]).delete()
|
||||
with connection.cursor() as cursor:
|
||||
# Nejdriv musime smazat reseni:
|
||||
cursor.execute('DELETE FROM seminar_reseni WHERE problem_id IN (1981, 1970, 2222);')
|
||||
# Nakonec i ty clanky samotne
|
||||
cursor.execute('DELETE FROM seminar_problemy WHERE id IN (1981, 1970, 2222);')
|
||||
|
||||
def smaz_divne_uzivatele():
|
||||
# U techto uzivatelu neexistuje Organizator s nimi spojeny
|
||||
# Takze pak delaji akorat neporadek
|
||||
with connection.cursor() as cursor:
|
||||
# Jeste je potreba zrusit vazby
|
||||
cursor.execute('UPDATE django_comments SET user_id = NULL WHERE user_id = 34;')
|
||||
cursor.execute('UPDATE seminar_problemy SET autor_id = NULL WHERE autor_id = 34;')
|
||||
cursor.execute('DELETE FROM django_admin_log WHERE user_id = 34;')
|
||||
cursor.execute('DELETE FROM auth_user_groups WHERE user_id = 34;')
|
||||
cursor.execute('DELETE FROM auth_user WHERE id IN (34, 40, 30, 50, 54, 58, 43);')
|
||||
|
||||
smaz_zle_clanky()
|
||||
smaz_divne_uzivatele()
|
|
@ -404,6 +404,7 @@ class OdevzdavatkoTabulkaFiltrForm(forms.Form):
|
|||
'problemy': cls.PROBLEMY_MOJE,
|
||||
'reseni_od': terminy[-2],
|
||||
'reseni_do': terminy[-1],
|
||||
'neobodovane': False,
|
||||
}
|
||||
return initial
|
||||
|
||||
|
@ -426,3 +427,4 @@ class OdevzdavatkoTabulkaFiltrForm(forms.Form):
|
|||
|
||||
reseni_od = forms.DateField(input_formats=[DATE_FORMAT])
|
||||
reseni_do = forms.DateField(input_formats=[DATE_FORMAT])
|
||||
neobodovane = forms.BooleanField(required=False)
|
||||
|
|
|
@ -63,7 +63,7 @@ class Migration(migrations.Migration):
|
|||
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('jmeno', models.CharField(max_length=256, verbose_name='jméno')),
|
||||
('prijmeni', models.CharField(max_length=256, verbose_name='příjmení')),
|
||||
('prezdivka', models.CharField(max_length=256, verbose_name='přezdívka')),
|
||||
('prezdivka', models.CharField(max_length=256, verbose_name='přezdívka', blank=True, null=False)),
|
||||
('pohlavi_muz', models.BooleanField(default=False, verbose_name='pohlaví (muž)')),
|
||||
('email', models.EmailField(blank=True, default='', max_length=256, verbose_name='e-mail')),
|
||||
('telefon', models.CharField(blank=True, default='', max_length=256, verbose_name='telefon')),
|
||||
|
@ -480,11 +480,6 @@ class Migration(migrations.Migration):
|
|||
name='resitele',
|
||||
field=models.ManyToManyField(help_text='Seznam autorů řešení', through='seminar.Reseni_Resitele', to='seminar.Resitel', verbose_name='autoři řešení'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='reseni',
|
||||
name='text_cely',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='reseni_cely_set', to='seminar.Text', verbose_name='Plná verze textu řešení'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='reseni',
|
||||
name='text_zkraceny',
|
||||
|
|
|
@ -23,11 +23,6 @@ class Migration(migrations.Migration):
|
|||
name='osoba',
|
||||
field=models.OneToOneField(help_text='osobní údaje organizátora', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='org', to='seminar.Osoba', verbose_name='osoba'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='reseni',
|
||||
name='text_cely',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='reseni_cely_set', to='seminar.Text', verbose_name='Plná verze textu řešení'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='resitel',
|
||||
name='osoba',
|
||||
|
|
|
@ -81,7 +81,7 @@ def osoba_to_resitel(apps, schema_editor):
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0050_auto_20190510_2228'),
|
||||
('seminar', '0066c_reseninode'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
# Generated by Django 1.11.20 on 2019-05-17 17:44
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
from django.db import migrations, models
|
||||
|
||||
from django.db.models import Q
|
||||
import django.db.models.deletion
|
||||
|
||||
def poskladej_strom(apps, rodic, *texty):
|
||||
Text = apps.get_model('seminar', 'Text')
|
||||
|
@ -43,22 +44,22 @@ def uloha_to_Uloha(apps,schema_editor):
|
|||
ulohy = Problem.objects.filter(typ = 'uloha')
|
||||
for uold in ulohy:
|
||||
unew = Uloha.objects.create(
|
||||
problem_ptr = uold,
|
||||
# Zakomentované fieldy by se už měly nacházet v příslušném problému
|
||||
#nazev = uold.nazev,
|
||||
#stav = uold.stav,
|
||||
#zamereni = uold.zamereni,
|
||||
#poznamka = uold.poznamka,
|
||||
#autor = uold.autor,
|
||||
#kod = uold.kod,
|
||||
problem_ptr = uold,
|
||||
nazev = uold.nazev,
|
||||
stav = uold.stav,
|
||||
zamereni = uold.zamereni,
|
||||
poznamka = uold.poznamka,
|
||||
autor = uold.autor,
|
||||
kod = uold.kod,
|
||||
cislo_zadani = uold.cislo_zadani_old,
|
||||
cislo_reseni = uold.cislo_reseni_old,
|
||||
max_body = uold.body,
|
||||
#vytvoreno = uold.vytvoreno,
|
||||
vytvoreno = uold.vytvoreno,
|
||||
)
|
||||
# unew.opravovatele.add(*uold.opravovatele.all())
|
||||
unew.save()
|
||||
|
||||
uold.save() # DULEZITE!!! Jinak Uloha.objects.create() přepíše všechny atributy Problému
|
||||
unew.opravovatele.add(*uold.opravovatele.all())
|
||||
|
||||
# Nody:
|
||||
zadani_node = UlohaZadaniNode.objects.create(uloha = unew)
|
||||
poskladej_strom(apps, zadani_node, uold.text_zadani)
|
||||
|
@ -77,11 +78,12 @@ def konfery_rucne(apps, schema_editor):
|
|||
def clanek_to_Clanek(apps,schema_editor):
|
||||
Problem = apps.get_model('seminar', 'Problem')
|
||||
Clanek = apps.get_model('seminar', 'Clanek')
|
||||
ClanekNode = apps.get_model('seminar', 'ClanekNode')
|
||||
ReseniNode = apps.get_model('seminar', 'ReseniNode')
|
||||
Text = apps.get_model('seminar', 'Text')
|
||||
TextNode = apps.get_model('seminar', 'TextNode')
|
||||
|
||||
clanky = Problem.objects.filter(Q(typ='org-clanek') | Q(typ='res-clanek'))
|
||||
# XXX: Org-clanky neexistuji, tak je migrace ani nepodporuje.
|
||||
clanky = Problem.objects.filter(typ='res-clanek')
|
||||
for cl in clanky:
|
||||
# Vybereme vhodné číslo pro článek z čísla zadání a čísla řešení:
|
||||
if cl.cislo_zadani_old is None:
|
||||
|
@ -99,18 +101,64 @@ def clanek_to_Clanek(apps,schema_editor):
|
|||
cislo = cislo,
|
||||
# Body ignorujeme, protože už jsou v hodnocení
|
||||
)
|
||||
clnew.save()
|
||||
cl.save() # DULEZITE!!! Jinak Clanek.objects.create() přepíše všechny atributy Problému
|
||||
|
||||
# Aktuálně nemáme v modelu informaci o tom, jestli je to org-článek
|
||||
# nebo řešitelský článek. Aby se neztratila informace, poznamenám to do
|
||||
# poznámky.
|
||||
cl.poznamka += "\nTyp:\t{}".format(cl.typ)
|
||||
cl.save()
|
||||
|
||||
def Clanek_Treenody(apps, schema_editor):
|
||||
Problem = apps.get_model('seminar', 'Problem')
|
||||
Clanek = apps.get_model('seminar', 'Clanek')
|
||||
ReseniNode = apps.get_model('seminar', 'ReseniNode')
|
||||
Text = apps.get_model('seminar', 'Text')
|
||||
TextNode = apps.get_model('seminar', 'TextNode')
|
||||
for cl in Clanek.objects.all():
|
||||
# Vyrobíme nody:
|
||||
clnode = ClanekNode(clanek = clnew)
|
||||
poskladej_strom(apps, clnode, cl.text_zadani, cl.text_reseni)
|
||||
clnode.save()
|
||||
# Clanek nema vlastni node, ma (prave jedno) Reseni a to ma text_cely -- ReseniNode
|
||||
reseni = cl.reseni_set.all()
|
||||
if len(reseni) != 1:
|
||||
raise ValueError(f'Clanek {cl.id} ma vic reseni {len(reseni)} ({reseni})')
|
||||
reseni = reseni[0]
|
||||
resnode = ReseniNode(reseni=reseni)
|
||||
poskladej_strom(apps, resnode, cl.text_zadani, cl.text_reseni)
|
||||
resnode.save()
|
||||
reseni.text_cely = resnode
|
||||
reseni.save()
|
||||
|
||||
def fix_Clanek_Reseni(apps, schema_editor):
|
||||
Problem = apps.get_model('seminar', 'Problem')
|
||||
Clanek = apps.get_model('seminar', 'Clanek')
|
||||
Reseni = apps.get_model('seminar', 'Reseni')
|
||||
Hodnoceni = apps.get_model('seminar', 'Hodnoceni')
|
||||
Resitel = apps.get_model('seminar', 'Resitel')
|
||||
|
||||
# Je potreba zajistit, ze clanky budou mit jen jedno reseni -- z pohledu
|
||||
# modelu nic jineho nedava smysl. Ve stavajicim modelu ale naopak nelze
|
||||
# reprezentovat vice resitelu jednoho clanku (coz je ale bezne -- clanky z
|
||||
# konfer) Musime tedy opravit, aby misto nekolika reseni kazdeho resitele
|
||||
# samostatne zustalo jen jedno reseni, spravne obodovane a s vice resiteli
|
||||
# jako autory
|
||||
|
||||
for cl in Clanek.objects.all():
|
||||
rr = cl.reseni_set.all()
|
||||
if len(rr) == 1: continue
|
||||
# Vice nez jedno reseni, jdeme je sjednotit.
|
||||
resitele = []
|
||||
vzor_hodnoceni = rr[0].hodnoceni_set.first()
|
||||
ostatni_hodnoceni = []
|
||||
for r in rr:
|
||||
# Overime, ze nemame kolizi v datech:
|
||||
h = r.hodnoceni_set.first()
|
||||
if h.cislo_body != vzor_hodnoceni.cislo_body or h.body != vzor_hodnoceni.body:
|
||||
raise ValueError(f'Clanek {cl.id} ma vice nekonzistentnich reseni')
|
||||
if h.id != vzor_hodnoceni.id:
|
||||
ostatni_hodnoceni.append(h)
|
||||
resitele.extend(r.resitele.all())
|
||||
rr[0].resitele.set(resitele)
|
||||
rr[0].save()
|
||||
vzor_hodnoceni.save()
|
||||
# Ted mame spravne databazi, jeste potrebujeme z databaze smazat po novu nepouzita hodnoceni
|
||||
for h in ostatni_hodnoceni:
|
||||
h.reseni.delete()
|
||||
h.delete()
|
||||
|
||||
def tema_to_Tema(apps, schema_editor):
|
||||
Problem = apps.get_model('seminar', 'Problem')
|
||||
|
@ -138,7 +186,7 @@ def tema_to_Tema(apps, schema_editor):
|
|||
tema_typ = t.typ,
|
||||
rocnik = rocnik,
|
||||
)
|
||||
tnew.save()
|
||||
t.save() # DULEZITE!!! Jinak Tema.objects.create() přepíše všechny atributy Problému
|
||||
|
||||
# Nody:
|
||||
tnode = TemaVCisleNode(tema = tnew)
|
||||
|
@ -149,7 +197,7 @@ def tema_to_Tema(apps, schema_editor):
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0057_reseni_to_reseni_hodnoceni'),
|
||||
('seminar', '0087_fix_polymorphism'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
@ -157,5 +205,7 @@ class Migration(migrations.Migration):
|
|||
migrations.RunPython(uloha_to_Uloha, migrations.RunPython.noop),
|
||||
migrations.RunPython(tema_to_Tema, migrations.RunPython.noop),
|
||||
migrations.RunPython(clanek_to_Clanek, migrations.RunPython.noop),
|
||||
migrations.RunPython(fix_Clanek_Reseni, migrations.RunPython.noop),
|
||||
migrations.RunPython(Clanek_Treenody, migrations.RunPython.noop),
|
||||
migrations.RunPython(konfery_rucne, migrations.RunPython.noop),
|
||||
]
|
||||
|
|
|
@ -6,6 +6,15 @@ from django.db import migrations
|
|||
|
||||
from django.db.models import Q
|
||||
|
||||
def nastav_koren(koren, node):
|
||||
node.root = koren
|
||||
node.save()
|
||||
|
||||
if node.succ:
|
||||
nastav_koren(koren, node.succ)
|
||||
if node.first_child:
|
||||
nastav_koren(koren, node.first_child)
|
||||
|
||||
def pridej_potomka(rodic, potomek):
|
||||
# Daný vrchol bude posledním potomkem rodiče
|
||||
uz_ma_deti = False
|
||||
|
@ -23,8 +32,7 @@ def pridej_potomka(rodic, potomek):
|
|||
posledni = posledni.succ
|
||||
|
||||
# Nastavíme kořen:
|
||||
potomek.root = rodic.root
|
||||
potomek.save()
|
||||
nastav_koren(rodic.root, potomek)
|
||||
|
||||
# Připojíme vrchol:
|
||||
if uz_ma_deti:
|
||||
|
@ -56,15 +64,19 @@ def pokacej_les(apps, schema_editor):
|
|||
relevantni_temata = Tema.objects.filter(Q(cislo_zadani_old = c) | Q(cislo_reseni_old = c)).order_by('kod')
|
||||
# Téma dáme do prvního čísla, kde se vyskytne
|
||||
for t in relevantni_temata:
|
||||
tnode = t.temavcislenode
|
||||
tnodes = t.temavcislenode_set.all()
|
||||
# Migrujeme, TvCN je jen jedno dohromady
|
||||
assert(len(tnodes) == 1)
|
||||
tnode = tnodes[0]
|
||||
# Zkontrolujeme a preskocime cislo_reseni
|
||||
if t.cislo_zadani_old and t.cislo_reseni_old:
|
||||
assert(t.cislo_zadani_old <= t.cislo_reseni_old)
|
||||
assert(t.cislo_zadani_old.rocnik == t.cislo_reseni_old.rocnik
|
||||
and t.cislo_zadani_old.cislo <= t.cislo_reseni_old.cislo)
|
||||
if t.cislo_reseni_old == c:
|
||||
# Už by mělo být přidané do čísla zadání
|
||||
continue
|
||||
else:
|
||||
# Patří sem (buď je to jediné číslo, nebo je to číslo zadání)
|
||||
pridej_potomka(cnode, tnode)
|
||||
# Patří sem (buď je to jediné číslo, nebo je to číslo zadání)
|
||||
pridej_potomka(cnode, tnode)
|
||||
|
||||
# Úložky (zadání) a pohádky
|
||||
for u in Uloha.objects.filter(cislo_zadani = c).order_by('kod'):
|
||||
|
@ -85,8 +97,12 @@ def pokacej_les(apps, schema_editor):
|
|||
|
||||
# Články
|
||||
for cl in Clanek.objects.filter(cislo = c).order_by('kod'):
|
||||
clnode = cl.claneknode
|
||||
pridej_potomka(cnode, clnode)
|
||||
# Zmena: Clanky nemaji vlastni Node, ale pouziva se ReseniNode v text_cely
|
||||
reseni = cl.reseni_set.all()
|
||||
if len(reseni) != 1:
|
||||
raise ValueError('Clanek ma vic reseni')
|
||||
resnode = reseni[0].text_cely
|
||||
pridej_potomka(cnode, resnode)
|
||||
|
||||
# Konfery
|
||||
for k in Konfera.objects.all():
|
||||
|
|
|
@ -92,7 +92,7 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='reseni',
|
||||
name='text_cely',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='reseni_cely_set', to='seminar.Text', verbose_name='Plná verze textu řešení'),
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='reseni_cely_set', to='seminar.ReseniNode', verbose_name='Plná verze textu řešení'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='reseni_resitele',
|
||||
|
|
|
@ -18,7 +18,7 @@ class Migration(migrations.Migration):
|
|||
|
||||
dependencies = [
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
('seminar', '0064_auto_20190610_2358'),
|
||||
('seminar', '0057_reseni_to_reseni_hodnoceni'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
|
31
seminar/migrations/0066b_orgtextnode.py
Normal file
31
seminar/migrations/0066b_orgtextnode.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
|
||||
# Generated by Django 2.2.4 on 2019-08-13 19:45
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
('seminar', '0066_problem_polymorphic_ctype'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='OrgTextNode',
|
||||
fields=[
|
||||
('treenode_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='seminar.TreeNode')),
|
||||
('org_verejny', models.BooleanField(default=True, help_text='Pokud ano, bude org pod článkem podepsaný', verbose_name='Org je veřejný?')),
|
||||
('organizator', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='seminar.Organizator', verbose_name='Organizátor')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Organizátorský článek (Node)',
|
||||
'verbose_name_plural': 'Organizátorské články (Node)',
|
||||
'db_table': 'seminar_nodes_orgtextnode',
|
||||
},
|
||||
bases=('seminar.treenode',),
|
||||
),
|
||||
|
||||
]
|
34
seminar/migrations/0066c_reseninode.py
Normal file
34
seminar/migrations/0066c_reseninode.py
Normal file
|
@ -0,0 +1,34 @@
|
|||
# Generated by Django 2.2.4 on 2019-08-13 19:45
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
('seminar', '0050_auto_20190510_2228'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ReseniNode',
|
||||
fields=[
|
||||
('treenode_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='seminar.TreeNode')),
|
||||
('reseni', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='seminar.Reseni', verbose_name='reseni')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Otištěné řešení (Node)',
|
||||
'verbose_name_plural': 'Otištěná řešení (Node)',
|
||||
'db_table': 'seminar_nodes_otistene_reseni',
|
||||
},
|
||||
bases=('seminar.treenode',),
|
||||
),
|
||||
migrations.AddField(
|
||||
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='seminar.ReseniNode', verbose_name='Plná verze textu řešení'),
|
||||
),
|
||||
|
||||
]
|
|
@ -6,7 +6,7 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0066_problem_polymorphic_ctype'),
|
||||
('seminar', '0064_auto_20190610_2358'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -7,7 +7,7 @@ import django.db.models.deletion
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0076_auto_20200228_2013'),
|
||||
('seminar', '0066b_orgtextnode'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
# Generated by Django 2.2.9 on 2020-03-18 23:59
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0077_auto_20200318_2146'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='OtisteneReseniNode',
|
||||
fields=[
|
||||
('treenode_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='seminar.TreeNode')),
|
||||
('reseni', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='seminar.Reseni', verbose_name='reseni')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Otištěné řešení (Node)',
|
||||
'verbose_name_plural': 'Otištěná řešení (Node)',
|
||||
'db_table': 'seminar_nodes_otistene_reseni',
|
||||
},
|
||||
bases=('seminar.treenode',),
|
||||
),
|
||||
]
|
|
@ -6,7 +6,7 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0078_otistenereseninode'),
|
||||
('seminar', '0076_auto_20200228_2013'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -31,28 +31,10 @@ class Migration(migrations.Migration):
|
|||
model_name='konfera',
|
||||
name='ucastnici',
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='OrgTextNode',
|
||||
fields=[
|
||||
('treenode_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='seminar.TreeNode')),
|
||||
('org_verejny', models.BooleanField(default=True, help_text='Pokud ano, bude org pod článkem podepsaný', verbose_name='Org je veřejný?')),
|
||||
('organizator', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='seminar.Organizator', verbose_name='Organizátor')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Organizátorský článek (Node)',
|
||||
'verbose_name_plural': 'Organizátorské články (Node)',
|
||||
'db_table': 'seminar_nodes_orgtextnode',
|
||||
},
|
||||
bases=('seminar.treenode',),
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='konfera',
|
||||
name='id',
|
||||
),
|
||||
migrations.RenameModel(
|
||||
old_name='OtisteneReseniNode',
|
||||
new_name='ReseniNode',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='clanek',
|
||||
name='cislo',
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# Generated by Django 2.2.13 on 2020-06-24 22:57
|
||||
|
||||
# V současné době nepoužíván
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
def smaz_prezdivku(apps, schema_editor):
|
||||
|
@ -26,7 +28,7 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='osoba',
|
||||
name='prezdivka',
|
||||
field=models.CharField(blank=True, max_length=256, null=True, verbose_name='přezdívka'),
|
||||
field=models.CharField(blank=True, max_length=256, verbose_name='přezdívka'),
|
||||
),
|
||||
migrations.RunPython(smaz_prezdivku, pridej_prezdivku),
|
||||
]
|
|
@ -6,7 +6,7 @@ from django.db import migrations
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0085_nepovinna_prezdivka'),
|
||||
('seminar', '0084_clanek_cislo'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -41,7 +41,7 @@ def fix_problem(apps, schema_editor):
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('seminar', '0086_auto_20200819_0959'),
|
||||
('seminar', '0077_auto_20200318_2146'),
|
||||
]
|
||||
operations = [
|
||||
migrations.RunPython(fix_treenode, migrations.RunPython.noop),
|
||||
|
|
|
@ -27,7 +27,7 @@ def add_perms(apps, schema_editor):
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0087_fix_polymorphism'),
|
||||
('seminar', '0086_auto_20200819_0959'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
|
28
seminar/migrations/0093_auto_20210330_2105.py
Normal file
28
seminar/migrations/0093_auto_20210330_2105.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Generated by Django 2.2.12 on 2021-03-30 19:05
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0092_auto_20210309_2049'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name='VysledkyCelkemKCislu',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='VysledkyKCislu',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='VysledkyKCisluOdjakziva',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='VysledkyKCisluZaRocnik',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='VysledkyZaCislo',
|
||||
),
|
||||
]
|
18
seminar/migrations/0094_auto_20210701_0149.py
Normal file
18
seminar/migrations/0094_auto_20210701_0149.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.2.24 on 2021-06-30 23:49
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0093_auto_20210330_2105'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='osoba',
|
||||
name='prezdivka',
|
||||
field=models.CharField(blank=True, max_length=256, null=True, verbose_name='přezdívka'),
|
||||
),
|
||||
]
|
|
@ -9,6 +9,7 @@
|
|||
{{ filtr.problemy }}
|
||||
Od: {{ filtr.reseni_od }}
|
||||
Do: {{ filtr.reseni_do }}
|
||||
<span title="Jen neobodovaná řešení">🔨?</span> {{ filtr.neobodovane }}
|
||||
<input type=submit value="→">
|
||||
</form>
|
||||
|
||||
|
|
|
@ -27,7 +27,12 @@
|
|||
<li><a href="/admin/korektury/korekturovanepdf/add/">přidat pdf k opravám</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="{% url 'odevzdavatko_tabulka' %}"><strong>zadávání bodů</strong></a></li>
|
||||
<li>
|
||||
<a href="{% url 'odevzdavatko_tabulka' %}"><strong>zadávání bodů</strong></a>
|
||||
{% if pocet_neobodovanych_reseni > 0 or pocet_reseni_mimo_cislo > 0 %}
|
||||
<span style="color: red;">({{pocet_neobodovanych_reseni}} řešení nemá body, {{pocet_reseni_mimo_cislo}} není v žádném čísle!)</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
<li><a href='{{ posledni_cislo_url }}'><strong>poslední vydané číslo </strong></a></li>
|
||||
</ul>
|
||||
<hr />
|
||||
|
|
|
@ -59,12 +59,14 @@ class TabulkaOdevzdanychReseniView(ListView):
|
|||
problemy = fcd["problemy"]
|
||||
reseni_od = fcd["reseni_od"]
|
||||
reseni_do = fcd["reseni_do"]
|
||||
jen_neobodovane = fcd["neobodovane"]
|
||||
else:
|
||||
initial = FiltrForm.gen_initial()
|
||||
resitele = initial['resitele']
|
||||
problemy = initial['problemy']
|
||||
reseni_od = initial['reseni_od'][0]
|
||||
reseni_do = initial['reseni_do'][0]
|
||||
jen_neobodovane = initial["neobodovane"]
|
||||
|
||||
|
||||
# Filtrujeme!
|
||||
|
@ -87,6 +89,8 @@ class TabulkaOdevzdanychReseniView(ListView):
|
|||
self.problemy = self.problemy.non_polymorphic()
|
||||
|
||||
self.reseni = self.reseni.filter(cas_doruceni__date__gte=reseni_od, cas_doruceni__date__lte=reseni_do)
|
||||
if jen_neobodovane:
|
||||
self.reseni = self.reseni.filter(hodnoceni__body__isnull=True)
|
||||
|
||||
def get_queryset(self):
|
||||
self.inicializuj_osy_tabulky()
|
||||
|
|
|
@ -843,20 +843,23 @@ class OrgoRozcestnikView(TemplateView):
|
|||
# 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)
|
||||
|
||||
context['pocet_neobodovanych_reseni'] = s.Hodnoceni.objects.filter(body__isnull=True).count()
|
||||
context['pocet_reseni_mimo_cislo'] = s.Hodnoceni.objects.filter(cislo_body__isnull=True).count()
|
||||
|
||||
u = self.request.user
|
||||
os = s.Osoba.objects.get(user=u)
|
||||
organizator = s.Organizator.objects.get(osoba=os)
|
||||
temata_garant = s.Tema.objects.filter(garant=organizator,
|
||||
rocnik=aktualni_rocnik)
|
||||
#FIXME: přidat opravovatel, stav='STAV_ZADANY'
|
||||
ulohy_garant = s.Uloha.objects.filter(garant=organizator,
|
||||
cislo_zadani__rocnik=aktualni_rocnik)
|
||||
clanky_garant = s.Clanek.objects.filter(garant=organizator,
|
||||
cislo__rocnik=aktualni_rocnik)
|
||||
#FIXME: přidat stav='STAV_ZADANY'
|
||||
temata = s.Tema.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]),
|
||||
rocnik=aktualni_rocnik).distinct()
|
||||
ulohy = s.Uloha.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]),
|
||||
cislo_zadani__rocnik=aktualni_rocnik).distinct()
|
||||
clanky = s.Clanek.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]),
|
||||
cislo__rocnik=aktualni_rocnik).distinct()
|
||||
|
||||
context['temata'] = temata_garant
|
||||
context['ulohy'] = ulohy_garant
|
||||
context['clanky'] = clanky_garant
|
||||
context['temata'] = temata
|
||||
context['ulohy'] = ulohy
|
||||
context['clanky'] = clanky
|
||||
context['organizator'] = organizator
|
||||
return context
|
||||
|
||||
|
|
Loading…
Reference in a new issue