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, | 			'problemy': cls.PROBLEMY_MOJE, | ||||||
| 			'reseni_od': terminy[-2], | 			'reseni_od': terminy[-2], | ||||||
| 			'reseni_do': terminy[-1], | 			'reseni_do': terminy[-1], | ||||||
|  | 			'neobodovane': False, | ||||||
| 		} | 		} | ||||||
| 		return initial | 		return initial | ||||||
| 
 | 
 | ||||||
|  | @ -426,3 +427,4 @@ class OdevzdavatkoTabulkaFiltrForm(forms.Form): | ||||||
| 	 | 	 | ||||||
| 	reseni_od = forms.DateField(input_formats=[DATE_FORMAT]) | 	reseni_od = forms.DateField(input_formats=[DATE_FORMAT]) | ||||||
| 	reseni_do = 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)), |                 ('id', models.AutoField(primary_key=True, serialize=False)), | ||||||
|                 ('jmeno', models.CharField(max_length=256, verbose_name='jméno')), |                 ('jmeno', models.CharField(max_length=256, verbose_name='jméno')), | ||||||
|                 ('prijmeni', models.CharField(max_length=256, verbose_name='příjmení')), |                 ('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ž)')), |                 ('pohlavi_muz', models.BooleanField(default=False, verbose_name='pohlaví (muž)')), | ||||||
|                 ('email', models.EmailField(blank=True, default='', max_length=256, verbose_name='e-mail')), |                 ('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')), |                 ('telefon', models.CharField(blank=True, default='', max_length=256, verbose_name='telefon')), | ||||||
|  | @ -480,11 +480,6 @@ class Migration(migrations.Migration): | ||||||
|             name='resitele', |             name='resitele', | ||||||
|             field=models.ManyToManyField(help_text='Seznam autorů řešení', through='seminar.Reseni_Resitele', to='seminar.Resitel', verbose_name='autoři řešení'), |             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( |         migrations.AddField( | ||||||
|             model_name='reseni', |             model_name='reseni', | ||||||
|             name='text_zkraceny', |             name='text_zkraceny', | ||||||
|  |  | ||||||
|  | @ -23,11 +23,6 @@ class Migration(migrations.Migration): | ||||||
|             name='osoba', |             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'), |             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( |         migrations.AlterField( | ||||||
|             model_name='resitel', |             model_name='resitel', | ||||||
|             name='osoba', |             name='osoba', | ||||||
|  |  | ||||||
|  | @ -81,7 +81,7 @@ def osoba_to_resitel(apps, schema_editor): | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
| 
 | 
 | ||||||
| 	dependencies = [ | 	dependencies = [ | ||||||
| 		('seminar', '0050_auto_20190510_2228'), | 		('seminar', '0066c_reseninode'), | ||||||
| 	] | 	] | ||||||
| 
 | 
 | ||||||
| 	operations = [ | 	operations = [ | ||||||
|  |  | ||||||
|  | @ -2,9 +2,10 @@ | ||||||
| # Generated by Django 1.11.20 on 2019-05-17 17:44 | # Generated by Django 1.11.20 on 2019-05-17 17:44 | ||||||
| from __future__ import unicode_literals | from __future__ import unicode_literals | ||||||
| 
 | 
 | ||||||
| from django.db import migrations | from django.db import migrations, models | ||||||
| 
 | 
 | ||||||
| from django.db.models import Q | from django.db.models import Q | ||||||
|  | import django.db.models.deletion | ||||||
| 
 | 
 | ||||||
| def poskladej_strom(apps, rodic, *texty): | def poskladej_strom(apps, rodic, *texty): | ||||||
| 	Text = apps.get_model('seminar', 'Text') | 	Text = apps.get_model('seminar', 'Text') | ||||||
|  | @ -43,21 +44,21 @@ def uloha_to_Uloha(apps,schema_editor): | ||||||
| 	ulohy = Problem.objects.filter(typ = 'uloha') | 	ulohy = Problem.objects.filter(typ = 'uloha') | ||||||
| 	for uold in ulohy: | 	for uold in ulohy: | ||||||
| 		unew = Uloha.objects.create( | 		unew = Uloha.objects.create( | ||||||
| 			problem_ptr = uold, |  | ||||||
| 			# Zakomentované fieldy by se už měly nacházet v příslušném problému | 			# Zakomentované fieldy by se už měly nacházet v příslušném problému | ||||||
| 			#nazev = uold.nazev, | 			problem_ptr = uold, | ||||||
| 			#stav = uold.stav, | 			nazev = uold.nazev, | ||||||
| 			#zamereni = uold.zamereni, | 			stav = uold.stav, | ||||||
| 			#poznamka = uold.poznamka, | 			zamereni = uold.zamereni, | ||||||
| 			#autor = uold.autor, | 			poznamka = uold.poznamka, | ||||||
| 			#kod = uold.kod, | 			autor = uold.autor, | ||||||
|  | 			kod = uold.kod, | ||||||
| 			cislo_zadani = uold.cislo_zadani_old, | 			cislo_zadani = uold.cislo_zadani_old, | ||||||
| 			cislo_reseni = uold.cislo_reseni_old, | 			cislo_reseni = uold.cislo_reseni_old, | ||||||
| 			max_body = uold.body, | 			max_body = uold.body, | ||||||
| 			#vytvoreno = uold.vytvoreno, | 			vytvoreno = uold.vytvoreno, | ||||||
| 			) | 			) | ||||||
| #		unew.opravovatele.add(*uold.opravovatele.all()) | 		uold.save() # DULEZITE!!! Jinak Uloha.objects.create() přepíše všechny atributy Problému | ||||||
| 		unew.save() | 		unew.opravovatele.add(*uold.opravovatele.all()) | ||||||
| 		 | 		 | ||||||
| 		# Nody: | 		# Nody: | ||||||
| 		zadani_node = UlohaZadaniNode.objects.create(uloha = unew) | 		zadani_node = UlohaZadaniNode.objects.create(uloha = unew) | ||||||
|  | @ -77,11 +78,12 @@ def konfery_rucne(apps, schema_editor): | ||||||
| def clanek_to_Clanek(apps,schema_editor): | def clanek_to_Clanek(apps,schema_editor): | ||||||
| 	Problem = apps.get_model('seminar', 'Problem') | 	Problem = apps.get_model('seminar', 'Problem') | ||||||
| 	Clanek = apps.get_model('seminar', 'Clanek') | 	Clanek = apps.get_model('seminar', 'Clanek') | ||||||
| 	ClanekNode = apps.get_model('seminar', 'ClanekNode') | 	ReseniNode = apps.get_model('seminar', 'ReseniNode') | ||||||
| 	Text = apps.get_model('seminar', 'Text') | 	Text = apps.get_model('seminar', 'Text') | ||||||
| 	TextNode = apps.get_model('seminar', 'TextNode') | 	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: | 	for cl in clanky: | ||||||
| 		# Vybereme vhodné číslo pro článek z čísla zadání a čísla řešení: | 		# Vybereme vhodné číslo pro článek z čísla zadání a čísla řešení: | ||||||
| 		if cl.cislo_zadani_old is None: | 		if cl.cislo_zadani_old is None: | ||||||
|  | @ -99,18 +101,64 @@ def clanek_to_Clanek(apps,schema_editor): | ||||||
| 			cislo = cislo, | 			cislo = cislo, | ||||||
| 			# Body ignorujeme, protože už jsou v hodnocení | 			# 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: | 		# Vyrobíme nody: | ||||||
| 		clnode = ClanekNode(clanek = clnew) | 		# Clanek nema vlastni node, ma (prave jedno) Reseni a to ma text_cely -- ReseniNode | ||||||
| 		poskladej_strom(apps, clnode, cl.text_zadani, cl.text_reseni) | 		reseni = cl.reseni_set.all() | ||||||
| 		clnode.save() | 		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): | def tema_to_Tema(apps, schema_editor): | ||||||
| 	Problem = apps.get_model('seminar', 'Problem') | 	Problem = apps.get_model('seminar', 'Problem') | ||||||
|  | @ -138,7 +186,7 @@ def tema_to_Tema(apps, schema_editor): | ||||||
| 			tema_typ = t.typ, | 			tema_typ = t.typ, | ||||||
| 			rocnik = rocnik, | 			rocnik = rocnik, | ||||||
| 			) | 			) | ||||||
| 		tnew.save() | 		t.save() # DULEZITE!!! Jinak Tema.objects.create() přepíše všechny atributy Problému | ||||||
| 
 | 
 | ||||||
| 		# Nody: | 		# Nody: | ||||||
| 		tnode = TemaVCisleNode(tema = tnew) | 		tnode = TemaVCisleNode(tema = tnew) | ||||||
|  | @ -149,7 +197,7 @@ def tema_to_Tema(apps, schema_editor): | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
| 
 | 
 | ||||||
| 	dependencies = [ | 	dependencies = [ | ||||||
| 		('seminar', '0057_reseni_to_reseni_hodnoceni'), | 		('seminar', '0087_fix_polymorphism'), | ||||||
| 	] | 	] | ||||||
| 
 | 
 | ||||||
| 	operations = [ | 	operations = [ | ||||||
|  | @ -157,5 +205,7 @@ class Migration(migrations.Migration): | ||||||
| 		migrations.RunPython(uloha_to_Uloha, migrations.RunPython.noop), | 		migrations.RunPython(uloha_to_Uloha, migrations.RunPython.noop), | ||||||
| 		migrations.RunPython(tema_to_Tema, migrations.RunPython.noop), | 		migrations.RunPython(tema_to_Tema, migrations.RunPython.noop), | ||||||
| 		migrations.RunPython(clanek_to_Clanek, 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), | 		migrations.RunPython(konfery_rucne, migrations.RunPython.noop), | ||||||
| 	] | 	] | ||||||
|  |  | ||||||
|  | @ -6,6 +6,15 @@ from django.db import migrations | ||||||
| 
 | 
 | ||||||
| from django.db.models import Q | 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): | def pridej_potomka(rodic, potomek): | ||||||
| 	# Daný vrchol bude posledním potomkem rodiče | 	# Daný vrchol bude posledním potomkem rodiče | ||||||
| 	uz_ma_deti = False | 	uz_ma_deti = False | ||||||
|  | @ -23,8 +32,7 @@ def pridej_potomka(rodic, potomek): | ||||||
| 			posledni = posledni.succ | 			posledni = posledni.succ | ||||||
| 
 | 
 | ||||||
| 	# Nastavíme kořen: | 	# Nastavíme kořen: | ||||||
| 	potomek.root = rodic.root | 	nastav_koren(rodic.root, potomek) | ||||||
| 	potomek.save() |  | ||||||
| 
 | 
 | ||||||
| 	# Připojíme vrchol: | 	# Připojíme vrchol: | ||||||
| 	if uz_ma_deti: | 	if uz_ma_deti: | ||||||
|  | @ -56,13 +64,17 @@ def pokacej_les(apps, schema_editor): | ||||||
| 		relevantni_temata = Tema.objects.filter(Q(cislo_zadani_old = c) | Q(cislo_reseni_old = c)).order_by('kod') | 		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 | 		# Téma dáme do prvního čísla, kde se vyskytne | ||||||
| 		for t in relevantni_temata: | 		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: | 			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: | 				if t.cislo_reseni_old == c: | ||||||
| 					# Už by mělo být přidané do čísla zadání | 					# Už by mělo být přidané do čísla zadání | ||||||
| 					continue | 					continue | ||||||
| 			else: |  | ||||||
| 			# Patří sem (buď je to jediné číslo, nebo je to číslo zadání) | 			# Patří sem (buď je to jediné číslo, nebo je to číslo zadání) | ||||||
| 			pridej_potomka(cnode, tnode) | 			pridej_potomka(cnode, tnode) | ||||||
| 
 | 
 | ||||||
|  | @ -85,8 +97,12 @@ def pokacej_les(apps, schema_editor): | ||||||
| 
 | 
 | ||||||
| 		# Články | 		# Články | ||||||
| 		for cl in Clanek.objects.filter(cislo = c).order_by('kod'): | 		for cl in Clanek.objects.filter(cislo = c).order_by('kod'): | ||||||
| 			clnode = cl.claneknode | 			# Zmena: Clanky nemaji vlastni Node, ale pouziva se ReseniNode v text_cely | ||||||
| 			pridej_potomka(cnode, clnode) | 			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 | 		# Konfery | ||||||
| 		for k in Konfera.objects.all(): | 		for k in Konfera.objects.all(): | ||||||
|  |  | ||||||
|  | @ -92,7 +92,7 @@ class Migration(migrations.Migration): | ||||||
|         migrations.AlterField( |         migrations.AlterField( | ||||||
|             model_name='reseni', |             model_name='reseni', | ||||||
|             name='text_cely', |             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( |         migrations.AlterField( | ||||||
|             model_name='reseni_resitele', |             model_name='reseni_resitele', | ||||||
|  |  | ||||||
|  | @ -18,7 +18,7 @@ class Migration(migrations.Migration): | ||||||
| 	 | 	 | ||||||
| 	dependencies = [ | 	dependencies = [ | ||||||
| 		('contenttypes', '0002_remove_content_type_name'), | 		('contenttypes', '0002_remove_content_type_name'), | ||||||
| 		('seminar', '0064_auto_20190610_2358'), | 		('seminar', '0057_reseni_to_reseni_hodnoceni'), | ||||||
| 	] | 	] | ||||||
| 	 | 	 | ||||||
| 	operations = [ | 	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): | class Migration(migrations.Migration): | ||||||
| 
 | 
 | ||||||
|     dependencies = [ |     dependencies = [ | ||||||
|         ('seminar', '0066_problem_polymorphic_ctype'), |         ('seminar', '0064_auto_20190610_2358'), | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     operations = [ |     operations = [ | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ import django.db.models.deletion | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
| 
 | 
 | ||||||
|     dependencies = [ |     dependencies = [ | ||||||
|         ('seminar', '0076_auto_20200228_2013'), |         ('seminar', '0066b_orgtextnode'), | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     operations = [ |     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): | class Migration(migrations.Migration): | ||||||
| 
 | 
 | ||||||
|     dependencies = [ |     dependencies = [ | ||||||
|         ('seminar', '0078_otistenereseninode'), |         ('seminar', '0076_auto_20200228_2013'), | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     operations = [ |     operations = [ | ||||||
|  |  | ||||||
|  | @ -31,28 +31,10 @@ class Migration(migrations.Migration): | ||||||
|             model_name='konfera', |             model_name='konfera', | ||||||
|             name='ucastnici', |             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( |         migrations.RemoveField( | ||||||
|             model_name='konfera', |             model_name='konfera', | ||||||
|             name='id', |             name='id', | ||||||
|         ), |         ), | ||||||
|         migrations.RenameModel( |  | ||||||
|             old_name='OtisteneReseniNode', |  | ||||||
|             new_name='ReseniNode', |  | ||||||
|         ), |  | ||||||
|         migrations.RemoveField( |         migrations.RemoveField( | ||||||
|             model_name='clanek', |             model_name='clanek', | ||||||
|             name='cislo', |             name='cislo', | ||||||
|  |  | ||||||
|  | @ -1,5 +1,7 @@ | ||||||
| # Generated by Django 2.2.13 on 2020-06-24 22:57 | # 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 | from django.db import migrations, models | ||||||
| 
 | 
 | ||||||
| def smaz_prezdivku(apps, schema_editor): | def smaz_prezdivku(apps, schema_editor): | ||||||
|  | @ -26,7 +28,7 @@ class Migration(migrations.Migration): | ||||||
|         migrations.AlterField( |         migrations.AlterField( | ||||||
|             model_name='osoba', |             model_name='osoba', | ||||||
|             name='prezdivka', |             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), |     migrations.RunPython(smaz_prezdivku, pridej_prezdivku), | ||||||
|     ] |     ] | ||||||
|  | @ -6,7 +6,7 @@ from django.db import migrations | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
| 
 | 
 | ||||||
|     dependencies = [ |     dependencies = [ | ||||||
|         ('seminar', '0085_nepovinna_prezdivka'), |         ('seminar', '0084_clanek_cislo'), | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     operations = [ |     operations = [ | ||||||
|  |  | ||||||
|  | @ -41,7 +41,7 @@ def fix_problem(apps, schema_editor): | ||||||
| 
 | 
 | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
| 	dependencies = [ | 	dependencies = [ | ||||||
| 		('seminar', '0086_auto_20200819_0959'), | 		('seminar', '0077_auto_20200318_2146'), | ||||||
| 	] | 	] | ||||||
| 	operations = [ | 	operations = [ | ||||||
| 		migrations.RunPython(fix_treenode, migrations.RunPython.noop), | 		migrations.RunPython(fix_treenode, migrations.RunPython.noop), | ||||||
|  |  | ||||||
|  | @ -27,7 +27,7 @@ def add_perms(apps, schema_editor): | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
| 
 | 
 | ||||||
|     dependencies = [ |     dependencies = [ | ||||||
|         ('seminar', '0087_fix_polymorphism'), |         ('seminar', '0086_auto_20200819_0959'), | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     operations = [ |     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 }} | {{ filtr.problemy }} | ||||||
| Od: {{ filtr.reseni_od }} | Od: {{ filtr.reseni_od }} | ||||||
| Do: {{ filtr.reseni_do }} | Do: {{ filtr.reseni_do }} | ||||||
|  | <span title="Jen neobodovaná řešení">🔨?</span> {{ filtr.neobodovane }} | ||||||
| <input type=submit value="→"> | <input type=submit value="→"> | ||||||
| </form> | </form> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -27,7 +27,12 @@ | ||||||
| 		<li><a href="/admin/korektury/korekturovanepdf/add/">přidat pdf k opravám</a></li> | 		<li><a href="/admin/korektury/korekturovanepdf/add/">přidat pdf k opravám</a></li> | ||||||
| 	</ul> | 	</ul> | ||||||
| 	</li> | 	</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> | 	<li><a href='{{ posledni_cislo_url }}'><strong>poslední vydané číslo </strong></a></li> | ||||||
| </ul> | </ul> | ||||||
| <hr /> | <hr /> | ||||||
|  |  | ||||||
|  | @ -59,12 +59,14 @@ class TabulkaOdevzdanychReseniView(ListView): | ||||||
| 			problemy = fcd["problemy"] | 			problemy = fcd["problemy"] | ||||||
| 			reseni_od = fcd["reseni_od"] | 			reseni_od = fcd["reseni_od"] | ||||||
| 			reseni_do = fcd["reseni_do"] | 			reseni_do = fcd["reseni_do"] | ||||||
|  | 			jen_neobodovane = fcd["neobodovane"] | ||||||
| 		else: | 		else: | ||||||
| 			initial = FiltrForm.gen_initial() | 			initial = FiltrForm.gen_initial() | ||||||
| 			resitele = initial['resitele'] | 			resitele = initial['resitele'] | ||||||
| 			problemy = initial['problemy'] | 			problemy = initial['problemy'] | ||||||
| 			reseni_od = initial['reseni_od'][0] | 			reseni_od = initial['reseni_od'][0] | ||||||
| 			reseni_do = initial['reseni_do'][0] | 			reseni_do = initial['reseni_do'][0] | ||||||
|  | 			jen_neobodovane = initial["neobodovane"] | ||||||
| 			 | 			 | ||||||
| 
 | 
 | ||||||
| 		# Filtrujeme! | 		# Filtrujeme! | ||||||
|  | @ -87,6 +89,8 @@ class TabulkaOdevzdanychReseniView(ListView): | ||||||
| 		self.problemy = self.problemy.non_polymorphic() | 		self.problemy = self.problemy.non_polymorphic() | ||||||
| 
 | 
 | ||||||
| 		self.reseni = self.reseni.filter(cas_doruceni__date__gte=reseni_od, cas_doruceni__date__lte=reseni_do) | 		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): | 	def get_queryset(self): | ||||||
| 		self.inicializuj_osy_tabulky() | 		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  | 		# pokud nechceme haluzit kód (= poradi) dalšího čísla, bude asi potřeba jít  | ||||||
| 		# přes treenody (a dát si přitom pozor na MezicisloNode) | 		# přes treenody (a dát si přitom pozor na MezicisloNode) | ||||||
| 
 | 
 | ||||||
|  | 		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 | 		u = self.request.user | ||||||
| 		os = s.Osoba.objects.get(user=u) | 		os = s.Osoba.objects.get(user=u) | ||||||
| 		organizator = s.Organizator.objects.get(osoba=os) | 		organizator = s.Organizator.objects.get(osoba=os) | ||||||
| 		temata_garant = s.Tema.objects.filter(garant=organizator,  | 		#FIXME: přidat stav='STAV_ZADANY' | ||||||
| 			rocnik=aktualni_rocnik) | 		temata = s.Tema.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]),  | ||||||
| 		#FIXME: přidat opravovatel, stav='STAV_ZADANY' | 			rocnik=aktualni_rocnik).distinct() | ||||||
| 		ulohy_garant = s.Uloha.objects.filter(garant=organizator, | 		ulohy = s.Uloha.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]), | ||||||
| 			cislo_zadani__rocnik=aktualni_rocnik) | 			cislo_zadani__rocnik=aktualni_rocnik).distinct() | ||||||
| 		clanky_garant = s.Clanek.objects.filter(garant=organizator, | 		clanky = s.Clanek.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]), | ||||||
| 			cislo__rocnik=aktualni_rocnik) | 			cislo__rocnik=aktualni_rocnik).distinct() | ||||||
| 
 | 
 | ||||||
| 		context['temata'] = temata_garant | 		context['temata'] = temata | ||||||
| 		context['ulohy'] = ulohy_garant | 		context['ulohy'] = ulohy | ||||||
| 		context['clanky'] = clanky_garant | 		context['clanky'] = clanky | ||||||
| 		context['organizator'] = organizator | 		context['organizator'] = organizator | ||||||
| 		return context | 		return context | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Pavel "LEdoian" Turinsky
						Pavel "LEdoian" Turinsky