vysledkovka | funkce pro vysledkovku presunuty do samostatneho souboru.
This commit is contained in:
		
							parent
							
								
									fe1cc4ac39
								
							
						
					
					
						commit
						1f0460e3c9
					
				
					 3 changed files with 523 additions and 516 deletions
				
			
		|  | @ -219,3 +219,87 @@ def viewMethodSwitch(get, post): | |||
| 	 | ||||
| 	return NewView.as_view() | ||||
| 
 | ||||
| def cisla_rocniku(rocnik, jen_verejne=True): | ||||
| 	""" | ||||
| 	Vrátí všechna čísla daného ročníku. | ||||
| 	Parametry: | ||||
| 		rocnik (Rocnik): ročník semináře | ||||
| 		jen_verejne (bool): zda se mají vrátit jen veřejná, nebo všechna čísla | ||||
| 	Vrátí: | ||||
| 		seznam objektů typu Cislo | ||||
| 	"""	 | ||||
| 	if jen_verejne: | ||||
| 		return rocnik.verejna_cisla() | ||||
| 	else: | ||||
| 		return rocnik.cisla.all() | ||||
| 
 | ||||
| def hlavni_problem(problem): | ||||
| 	""" Pro daný problém vrátí jeho nejvyšší nadproblém.""" | ||||
| 	while not(problem.nadproblem == None): | ||||
| 		problem = problem.nadproblem | ||||
| 	return problem | ||||
| 
 | ||||
| def hlavni_problemy_rocniku(rocnik, jen_verejne=True): | ||||
| 	""" Pro zadaný ročník vrátí hlavní problémy ročníku, | ||||
| 	tj. ty, které už nemají nadproblém.""" | ||||
| 	hlavni_problemy = [] | ||||
| 	for cislo in cisla_rocniku(rocnik, jen_verejne): | ||||
| 		for problem in hlavni_problemy_cisla(cislo): | ||||
| 			hlavni_problemy.append(problem) | ||||
| 	hlavni_problemy_set = set(hlavni_problemy) | ||||
| 	hlavni_problemy = list(hlavni_problemy_set) | ||||
| 	hlavni_problemy.sort(key=lambda k:k.kod_v_rocniku()) # setřídit podle pořadí | ||||
| 	 | ||||
| 	return hlavni_problemy | ||||
| 
 | ||||
| def problemy_cisla(cislo): | ||||
| 	""" Vrátí seznam všech problémů s body v daném čísle. """ | ||||
| 	hodnoceni = cislo.hodnoceni.select_related('problem', 'reseni').all() | ||||
| 	# hodnocení, která se vážou k danému číslu | ||||
| 	 | ||||
| 	reseni = [h.reseni for h in hodnoceni] | ||||
| 	problemy = [h.problem for h in hodnoceni] | ||||
| 	problemy_set = set(problemy)  # chceme každý problém unikátně, | ||||
| 	problemy = (list(problemy_set))  # převedení na množinu a zpět to zaručí | ||||
| 
 | ||||
| 	return problemy | ||||
| 
 | ||||
| 
 | ||||
| def hlavni_problemy_cisla(cislo, problemy=None): | ||||
| 	""" Vrátí seznam všech problémů s body v daném čísle, které již nemají nadproblém. """ | ||||
| 	if problemy is None: | ||||
| 		problemy = problemy_cisla(cislo) | ||||
| 
 | ||||
| 	# hlavní problémy čísla  | ||||
| 	# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém) | ||||
| 	hlavni_problemy = [] | ||||
| 	for p in problemy: | ||||
| 		hlavni_problemy.append(hlavni_problem(p)) | ||||
| 		 | ||||
| 	# zunikátnění | ||||
| 	hlavni_problemy_set = set(hlavni_problemy) | ||||
| 	hlavni_problemy = list(hlavni_problemy_set) | ||||
| 	hlavni_problemy.sort(key=lambda k: k.kod_v_rocniku()) # setřídit podle t1, t2, c3, ... | ||||
| 	 | ||||
| 	return hlavni_problemy | ||||
| 
 | ||||
| 
 | ||||
| def podproblemy_v_cislu(cislo, problemy=None, hlavni_problemy=None): | ||||
| 	""" Vrátí seznam všech problémů s body v daném čísle v poli 'indexovaném' tématy. """ | ||||
| 	if problemy is None: | ||||
| 		problemy = problemy_cisla(cislo) | ||||
| 	if hlavni_problemy is None: | ||||
| 		hlavni_problemy = hlavni_problemy_cisla(cislo, problemy) | ||||
| 
 | ||||
| 	podproblemy = dict((hp.id, []) for hp in hlavni_problemy) | ||||
| 	hlavni_problemy = set(hlavni_problemy) | ||||
| 	podproblemy[-1] = [] | ||||
| 
 | ||||
| 	for problem in problemy: | ||||
| 		h_problem = hlavni_problem(problem) | ||||
| 		if h_problem in hlavni_problemy: | ||||
| 			podproblemy[h_problem.id].append(problem) | ||||
| 		else: | ||||
| 			podproblemy[-1].append(problem) | ||||
| 
 | ||||
| 	return podproblemy | ||||
|  |  | |||
|  | @ -29,6 +29,7 @@ from seminar.forms import PrihlaskaForm, LoginForm, ProfileEditForm | |||
| import seminar.forms as f | ||||
| import seminar.templatetags.treenodes as tnltt | ||||
| import seminar.views.views_rest as vr | ||||
| from seminar.views.vysledkovka import vysledkovka_rocniku, vysledkovka_cisla  | ||||
| 
 | ||||
| from datetime import timedelta, date, datetime, MAXYEAR | ||||
| from django.utils import timezone | ||||
|  | @ -48,7 +49,7 @@ import csv | |||
| import logging | ||||
| import time | ||||
| 
 | ||||
| from seminar.utils import aktivniResitele, resi_v_rocniku | ||||
| from seminar.utils import aktivniResitele, resi_v_rocniku, hlavni_problemy_rocniku, cisla_rocniku, hlavni_problemy_cisla | ||||
| 
 | ||||
| # ze starého modelu | ||||
| #def verejna_temata(rocnik): | ||||
|  | @ -631,265 +632,8 @@ class ArchivView(generic.ListView): | |||
| 		 | ||||
| 		return context | ||||
| 
 | ||||
| ### Výsledky | ||||
| 
 | ||||
| def sloupec_s_poradim(setrizene_body): | ||||
| 	""" | ||||
| 	Ze seznamu obsahujícího sestupně setřízené body řešitelů za daný ročník  | ||||
| 	vytvoří seznam s pořadími (včetně 3.-5. a pak 2 volná místa atp.), | ||||
| 	podle toho, jak jdou za sebou ve výsledkovce. | ||||
| 	Parametr: | ||||
| 		setrizene_body (seznam integerů): sestupně setřízená čísla | ||||
| 
 | ||||
| 	Výstup: | ||||
| 		sloupec_s_poradim (seznam stringů) | ||||
| 	""" | ||||
| 
 | ||||
| 	# ze seznamu obsahujícího setřízené body spočítáme sloupec s pořadím	 | ||||
| 	aktualni_poradi = 1 | ||||
| 	sloupec_s_poradim = [] | ||||
| 
 | ||||
| 	# seskupíme seznam všech bodů podle hodnot | ||||
| 	for index in range(0, len(setrizene_body)): | ||||
| 		# pokud je pořadí větší než číslo řádku, tak jsme vypsali větší rozsah a chceme | ||||
| 		# vypsat už jen prázdné místo, než dojdeme na správný řádek | ||||
| 		if (index + 1) < aktualni_poradi: | ||||
| 			sloupec_s_poradim.append("") | ||||
| 			continue | ||||
| 		velikost_skupiny = 0 | ||||
| 		# zjistíme počet po sobě jdoucích stejných hodnot | ||||
| 		while setrizene_body[index] == setrizene_body[index + velikost_skupiny]: | ||||
| 			velikost_skupiny = velikost_skupiny + 1 | ||||
| 			# na konci musíme ošetřit přetečení seznamu | ||||
| 			if (index + velikost_skupiny) > len(setrizene_body) - 1: | ||||
| 				break | ||||
| 		# pokud je velikost skupiny 1, vypíšu pořadí | ||||
| 		if velikost_skupiny == 1: | ||||
| 			sloupec_s_poradim.append("{}.".format(aktualni_poradi)) | ||||
| 		# pokud je skupina větší, vypíšu rozsah | ||||
| 		else: | ||||
| 			sloupec_s_poradim.append("{}.–{}.".format(aktualni_poradi,  | ||||
| 						aktualni_poradi+velikost_skupiny-1)) | ||||
| 		# zvětšíme aktuální pořadí o tolik, kolik pozic bylo přeskočeno | ||||
| 		aktualni_poradi = aktualni_poradi + velikost_skupiny | ||||
| 	return sloupec_s_poradim | ||||
| 
 | ||||
| def cisla_rocniku(rocnik, jen_verejne=True): | ||||
| 	""" | ||||
| 	Vrátí všechna čísla daného ročníku. | ||||
| 	Parametry: | ||||
| 		rocnik (Rocnik): ročník semináře | ||||
| 		jen_verejne (bool): zda se mají vrátit jen veřejná, nebo všechna čísla | ||||
| 	Vrátí: | ||||
| 		seznam objektů typu Cislo | ||||
| 	"""	 | ||||
| 	if jen_verejne: | ||||
| 		return rocnik.verejna_cisla() | ||||
| 	else: | ||||
| 		return rocnik.cisla.all() | ||||
| 
 | ||||
| def hlavni_problem(problem): | ||||
| 	""" Pro daný problém vrátí jeho nejvyšší nadproblém.""" | ||||
| 	while not(problem.nadproblem == None): | ||||
| 		problem = problem.nadproblem | ||||
| 	return problem | ||||
| 
 | ||||
| def hlavni_problemy_rocniku(rocnik, jen_verejne=True): | ||||
| 	""" Pro zadaný ročník vrátí hlavní problémy ročníku, | ||||
| 	tj. ty, které už nemají nadproblém.""" | ||||
| 	hlavni_problemy = [] | ||||
| 	for cislo in cisla_rocniku(rocnik, jen_verejne): | ||||
| 		for problem in hlavni_problemy_cisla(cislo): | ||||
| 			hlavni_problemy.append(problem) | ||||
| 	hlavni_problemy_set = set(hlavni_problemy) | ||||
| 	hlavni_problemy = list(hlavni_problemy_set) | ||||
| 	hlavni_problemy.sort(key=lambda k:k.kod_v_rocniku()) # setřídit podle pořadí | ||||
| 	 | ||||
| 	return hlavni_problemy | ||||
| 	 | ||||
| 
 | ||||
| def problemy_cisla(cislo): | ||||
| 	""" Vrátí seznam všech problémů s body v daném čísle. """ | ||||
| 	hodnoceni = cislo.hodnoceni.select_related('problem', 'reseni').all() | ||||
| 	# hodnocení, která se vážou k danému číslu | ||||
| 	 | ||||
| 	reseni = [h.reseni for h in hodnoceni] | ||||
| 	problemy = [h.problem for h in hodnoceni] | ||||
| 	problemy_set = set(problemy)  # chceme každý problém unikátně, | ||||
| 	problemy = (list(problemy_set))  # převedení na množinu a zpět to zaručí | ||||
| 
 | ||||
| 	return problemy | ||||
| 
 | ||||
| 
 | ||||
| def hlavni_problemy_cisla(cislo, problemy=None): | ||||
| 	""" Vrátí seznam všech problémů s body v daném čísle, které již nemají nadproblém. """ | ||||
| 	if problemy is None: | ||||
| 		problemy = problemy_cisla(cislo) | ||||
| 
 | ||||
| 	# hlavní problémy čísla  | ||||
| 	# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém) | ||||
| 	hlavni_problemy = [] | ||||
| 	for p in problemy: | ||||
| 		hlavni_problemy.append(hlavni_problem(p)) | ||||
| 		 | ||||
| 	# zunikátnění | ||||
| 	hlavni_problemy_set = set(hlavni_problemy) | ||||
| 	hlavni_problemy = list(hlavni_problemy_set) | ||||
| 	hlavni_problemy.sort(key=lambda k: k.kod_v_rocniku()) # setřídit podle t1, t2, c3, ... | ||||
| 	 | ||||
| 	return hlavni_problemy | ||||
| 
 | ||||
| 
 | ||||
| def podproblemy_v_cislu(cislo, problemy=None, hlavni_problemy=None): | ||||
| 	""" Vrátí seznam všech problémů s body v daném čísle v poli 'indexovaném' tématy. """ | ||||
| 	if problemy is None: | ||||
| 		problemy = problemy_cisla(cislo) | ||||
| 	if hlavni_problemy is None: | ||||
| 		hlavni_problemy = hlavni_problemy_cisla(cislo, problemy) | ||||
| 
 | ||||
| 	podproblemy = dict((hp.id, []) for hp in hlavni_problemy) | ||||
| 	hlavni_problemy = set(hlavni_problemy) | ||||
| 	podproblemy[-1] = [] | ||||
| 
 | ||||
| 	for problem in problemy: | ||||
| 		h_problem = hlavni_problem(problem) | ||||
| 		if h_problem in hlavni_problemy: | ||||
| 			podproblemy[h_problem.id].append(problem) | ||||
| 		else: | ||||
| 			podproblemy[-1].append(problem) | ||||
| 
 | ||||
| 	return podproblemy | ||||
| 
 | ||||
| def body_resitelu(resitele, za, odjakziva=True): | ||||
| 	""" Funkce počítající počty bodů pro zadané řešitele,  | ||||
| 	buď odjakživa do daného ročníku/čísla anebo za daný ročník/číslo. | ||||
| 	Parametry: | ||||
| 		resitele (seznam obsahující položky typu Resitel): aktivní řešitelé | ||||
| 		za (Rocnik/Cislo): za co se mají počítat body  | ||||
| 			(generování starších výsledkovek) | ||||
| 		odjakziva (bool): zda se mají počítat body odjakživa, nebo jen za číslo/ročník | ||||
| 			zadané v "za" | ||||
| 	Výstup: | ||||
| 		slovník (Resitel.id):body | ||||
| 	"""		 | ||||
| 	resitele_id = [r.id for r in resitele] | ||||
| 	# Zjistíme, typ objektu v parametru "za" | ||||
| 	if isinstance(za, Rocnik): | ||||
| 		cislo = None | ||||
| 		rocnik = za | ||||
| 		rok = rocnik.prvni_rok | ||||
| 	elif isinstance(za, Cislo): | ||||
| 		cislo = za | ||||
| 		rocnik = None | ||||
| 		rok = cislo.rocnik.prvni_rok | ||||
| 	else: | ||||
| 		assert True, "body_resitelu: za není ani číslo ani ročník."	 | ||||
| 
 | ||||
| 
 | ||||
| 	# Kvůli rychlosti používáme sčítáme body už v databázi, viz  | ||||
| 	# https://docs.djangoproject.com/en/3.0/topics/db/aggregation/,  | ||||
| 	# sekce Filtering on annotations (protože potřebujeme filtrovat výsledky | ||||
| 	# jen do nějakého data, abychom uměli správně nagenerovat výsledkovky i  | ||||
| 	# za historická čísla. | ||||
| 
 | ||||
| 	# Níže se vytváří dotaz na součet bodů za správně vyfiltrovaná hodnocení,  | ||||
| 	# který se použije ve výsledném dotazu. | ||||
| 	if cislo and odjakziva: # Body se sčítají odjakživa do zadaného čísla. | ||||
| 		# Vyfiltrujeme všechna hodnocení, která jsou buď ze starších ročníků,  | ||||
| 		# anebo ze stejného ročníku, jak je zadané číslo, tam ale sčítáme jen | ||||
| 		# pro čísla s pořadím nejvýše stejným, jako má zadané číslo. | ||||
| 		body_k_zapocteni = Sum('reseni__hodnoceni__body',  | ||||
| 			filter=( Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lt=rok) | | ||||
| 				 Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok=rok, | ||||
| 				   reseni__hodnoceni__cislo_body__poradi__lte=cislo.poradi) )) | ||||
| 	elif cislo and not odjakziva: # Body se sčítají za dané číslo. | ||||
| 		body_k_zapocteni = Sum('reseni__hodnoceni__body', | ||||
| 			filter=( Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok=rok, | ||||
| 					reseni__hodnoceni__cislo_body__poradi__lte=cislo.poradi) )) | ||||
| 	elif rocnik and odjakziva: # Spočítáme body za starší ročníky až do zadaného včetně.	 | ||||
| 		body_k_zapocteni = Sum('reseni__hodnoceni__body',  | ||||
| 			filter= Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lte=rok)) | ||||
| 	elif rocnik and not odjakziva: # Spočítáme body za daný ročník. | ||||
| 		body_k_zapocteni = Sum('reseni__hodnoceni__body', | ||||
| 			filter= Q(reseni__hodnoceni__cislo_body__rocnik=rocnik)) | ||||
| 	else:  | ||||
| 		assert True, "body_resitelu: Neplatná kombinace za a odjakživa." | ||||
| 
 | ||||
| 	# Následující řádek přidá ke každému řešiteli údaj ".body" se součtem jejich bodů | ||||
| 	resitele_s_body = Resitel.objects.filter(id__in=resitele_id).annotate( | ||||
| 		body=body_k_zapocteni) | ||||
| 	# Teď jen z QuerySetu řešitelů anotovaných body vygenerujeme slovník  | ||||
| 	# indexovaný řešitelským id obsahující body.  | ||||
| 	# Pokud jsou body None, nahradíme za 0. | ||||
| 	slovnik = {int(res.id) : (res.body if res.body else 0) for res in resitele_s_body} | ||||
| 	return slovnik | ||||
| 
 | ||||
| class RadekVysledkovkyRocniku(object): | ||||
| 	""" Obsahuje věci, které se hodí vědět při konstruování výsledkovky. | ||||
| 	Umožňuje snazší práci v templatu (lepší, než seznam).""" | ||||
| 
 | ||||
| 	def __init__(self, poradi, resitel, body_cisla_sezn, body_rocnik, body_odjakziva, rok): | ||||
| 		self.poradi = poradi | ||||
| 		self.resitel = resitel | ||||
| 		self.rocnik_resitele = resitel.rocnik(rok) | ||||
| 		self.body_rocnik = body_rocnik | ||||
| 		self.body_celkem_odjakziva = body_odjakziva | ||||
| 		self.body_cisla_sezn = body_cisla_sezn | ||||
| 		self.titul = resitel.get_titul(body_odjakziva) | ||||
| 
 | ||||
| def setrid_resitele_a_body(slov_resitel_body): | ||||
| 	setrizeni_resitele_id = [dvojice[0] for dvojice in slov_resitel_body] | ||||
| 	setrizeni_resitele = [Resitel.objects.get(id=i) for i in setrizeni_resitele_id] | ||||
| 	setrizene_body = [dvojice[1] for dvojice in slov_resitel_body] | ||||
| 	return setrizeni_resitele_id, setrizeni_resitele, setrizene_body | ||||
| 
 | ||||
| def vysledkovka_rocniku(rocnik, jen_verejne=True): | ||||
| 	""" Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve | ||||
| 	formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html" | ||||
| 	""" | ||||
| 	 | ||||
| 	## TODO možná chytřeji vybírat aktivní řešitele | ||||
| 	# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají  | ||||
| 	# u alespoň jedné hodnoty něco jiného než NULL | ||||
| 	aktivni_resitele = list(resi_v_rocniku(rocnik)) | ||||
| 	cisla = cisla_rocniku(rocnik, jen_verejne) | ||||
| 	body_cisla_slov = {} | ||||
| 	for cislo in cisla: | ||||
| 		# získáme body za číslo | ||||
| 		_, cislobody = secti_body_za_cislo(cislo, aktivni_resitele) | ||||
| 		body_cisla_slov[cislo.id] = cislobody | ||||
| 
 | ||||
| 	# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně | ||||
| 	resitel_rocnikbody_sezn = secti_body_za_rocnik(rocnik, aktivni_resitele) | ||||
| 
 | ||||
| 	# setřídíme řešitele podle počtu bodů a získáme seznam s body od nejvyšších po nenižší | ||||
| 	setrizeni_resitele_id, setrizeni_resitele, setrizene_body = setrid_resitele_a_body(resitel_rocnikbody_sezn) | ||||
| 	poradi = sloupec_s_poradim(setrizene_body) | ||||
| 
 | ||||
| 	# získáme body odjakživa | ||||
| 	resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, rocnik) | ||||
| 
 | ||||
| 	# vytvoříme jednotlivé sloupce výsledkovky | ||||
| 	radky_vysledkovky = [] | ||||
| 	i = 0 | ||||
| 	for ar_id in setrizeni_resitele_id: | ||||
| 		# seznam počtu bodů daného řešitele pro jednotlivá čísla | ||||
| 		body_cisla_sezn = [] | ||||
| 		for cislo in cisla: | ||||
| 			body_cisla_sezn.append(body_cisla_slov[cislo.id][ar_id]) | ||||
| 
 | ||||
| 		# vytáhneme informace pro daného řešitele | ||||
| 		radek = RadekVysledkovkyRocniku( | ||||
| 			poradi[i], # pořadí | ||||
| 			Resitel.objects.get(id=ar_id), # řešitel (z id) | ||||
| 			body_cisla_sezn, # seznam bodů za čísla | ||||
| 			setrizene_body[i], # body za ročník (spočítané výše s pořadím) | ||||
| 			resitel_odjakzivabody_slov[ar_id], # body odjakživa | ||||
| 			rocnik) # ročník semináře pro získání ročníku řešitele | ||||
| 		radky_vysledkovky.append(radek) | ||||
| 		i += 1 | ||||
| 
 | ||||
| 	return radky_vysledkovky | ||||
| 
 | ||||
| 
 | ||||
| class RocnikView(generic.DetailView): | ||||
|  | @ -952,264 +696,6 @@ class ProblemView(generic.DetailView): | |||
| 		return context | ||||
| 
 | ||||
| 
 | ||||
| class RadekVysledkovkyCisla(object): | ||||
| 	"""Obsahuje věci, které se hodí vědět při konstruování výsledkovky. | ||||
| 	Umožňuje snazší práci v templatu (lepší, než seznam).""" | ||||
| 
 | ||||
| 	def __init__(self, poradi, resitel, body_problemy_sezn, | ||||
| 				body_cislo, body_rocnik, body_odjakziva, rok, body_podproblemy, body_podproblemy_iter): | ||||
| 		self.resitel = resitel | ||||
| 		self.rocnik_resitele = resitel.rocnik(rok) | ||||
| 		self.body_cislo = body_cislo | ||||
| 		self.body_rocnik = body_rocnik | ||||
| 		self.body_celkem_odjakziva = body_odjakziva | ||||
| 		self.poradi = poradi | ||||
| 		self.body_problemy_sezn = body_problemy_sezn | ||||
| 		self.titul = resitel.get_titul(body_odjakziva) | ||||
| 		self.body_podproblemy = body_podproblemy | ||||
| 		self.body_podproblemy_iter = body_podproblemy_iter  # TODELETE | ||||
| 
 | ||||
| 
 | ||||
| def pricti_body(slovnik, resitel, body): | ||||
| 	""" Přiřazuje danému řešiteli body do slovníku. """ | ||||
| 	# testujeme na None (""), pokud je to první řešení  | ||||
| 	# daného řešitele, předěláme na 0 | ||||
| 	# (v dalším kroku přičteme reálný počet bodů), | ||||
| 	# rozlišujeme tím mezi 0 a neodevzdaným řešením | ||||
| 	if slovnik[resitel.id] == "": | ||||
| 		slovnik[resitel.id] = 0 | ||||
| 	 | ||||
| 	slovnik[resitel.id] += body | ||||
| 
 | ||||
| def secti_body_za_rocnik(za, aktivni_resitele): | ||||
| 	""" Spočítá body za ročník (celý nebo do daného čísla),  | ||||
| 		setřídí je sestupně a vrátí jako seznam. | ||||
| 	Parametry: | ||||
| 		za (typu Rocnik nebo Cislo)	spočítá za ročník, nebo za ročník až do  | ||||
| 						daného čísla | ||||
| 	""" | ||||
| 	# spočítáme všem řešitelům jejich body za ročník (False => ne odjakživa) | ||||
| 	resitel_rocnikbody_slov = body_resitelu(aktivni_resitele, za, False) | ||||
| 	# zeptáme se na dvojice (řešitel, body) za ročník a setřídíme sestupně | ||||
| 	resitel_rocnikbody_sezn = sorted(resitel_rocnikbody_slov.items(), | ||||
| 					key = lambda x: x[1], reverse = True) | ||||
| 	return resitel_rocnikbody_sezn | ||||
| 
 | ||||
| def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy=None): | ||||
| 	""" Spočítá u řešitelů body za číslo a za jednotlivé hlavní problémy (témata).""" | ||||
| 	# TODO setřídit hlavní problémy čísla podle id, ať jsou ve stejném pořadí pokaždé | ||||
| 	# pro každý hlavní problém zavedeme slovník s body za daný hlavní problém  | ||||
| 	# pro jednotlivé řešitele (slovník slovníků hlavních problémů) | ||||
| 	if hlavni_problemy is None: | ||||
| 		hlavni_problemy = hlavni_problemy_cisla(cislo) | ||||
| 
 | ||||
| 	def ne_clanek_ne_konfera(problem): | ||||
| 		return not(isinstance(problem.get_real_instance(), m.Clanek) or isinstance(problem.get_real_instance(), m.Konfera)) | ||||
| 
 | ||||
| 	temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy)) | ||||
| 
 | ||||
| 	def cosi(problem): | ||||
| 		return problem.id | ||||
| 
 | ||||
| 	hlavni_problemy_slovnik = {} | ||||
| 	for hp in temata_a_spol: | ||||
| 		hlavni_problemy_slovnik[hp.id] = {} | ||||
| 
 | ||||
| 	hlavni_problemy_slovnik[-1] = {} | ||||
| 
 | ||||
| 	# zakládání prázdných záznamů pro řešitele | ||||
| 	cislobody = {} | ||||
| 	for ar in aktivni_resitele: | ||||
| 		# řešitele převedeme na řetězec pomocí unikátního id | ||||
| 		cislobody[ar.id] = "" | ||||
| 		for hp in temata_a_spol: | ||||
| 			slovnik = hlavni_problemy_slovnik[hp.id] | ||||
| 			slovnik[ar.id] = "" | ||||
| 
 | ||||
| 		hlavni_problemy_slovnik[-1][ar.id] = "" | ||||
| 	 | ||||
| 	# vezmeme všechna řešení s body do daného čísla | ||||
| 	reseni_do_cisla = Reseni.objects.prefetch_related('problem', 'resitele',  | ||||
| 				'hodnoceni_set').filter(hodnoceni__cislo_body=cislo) | ||||
| 	 | ||||
| 	# projdeme všechna řešení do čísla a přičteme body každému řešiteli do celkových | ||||
| 	# bodů i do bodů za problém | ||||
| 	for reseni in reseni_do_cisla: | ||||
| 		 | ||||
| 		# řešení může řešit více problémů | ||||
| 		for prob in list(reseni.problem.all()): | ||||
| 			nadproblem = hlavni_problem(prob) | ||||
| 			if ne_clanek_ne_konfera(nadproblem): | ||||
| 				nadproblem_slovnik = hlavni_problemy_slovnik[nadproblem.id] | ||||
| 			else: | ||||
| 				nadproblem_slovnik = hlavni_problemy_slovnik[-1] | ||||
| 			 | ||||
| 			# a mít více hodnocení | ||||
| 			for hodn in list(reseni.hodnoceni_set.all()): | ||||
| 				body = hodn.body	 | ||||
| 			 | ||||
| 				# a mít více řešitelů | ||||
| 				for resitel in list(reseni.resitele.all()): | ||||
| 					if resitel not in aktivni_resitele: | ||||
| 						print("Skipping {}".format(resitel.id)) | ||||
| 						continue | ||||
| 					pricti_body(cislobody, resitel, body) | ||||
| 					pricti_body(nadproblem_slovnik, resitel, body) | ||||
| 	return hlavni_problemy_slovnik, cislobody | ||||
| 
 | ||||
| 
 | ||||
| def secti_body_za_cislo_podle_temat(cislo, aktivni_resitele, podproblemy=None, temata=None): | ||||
| 	""" Spočítá u řešitelů body za číslo za úlohy v jednotlivých hlavních problémech (témata).""" | ||||
| 	if temata is None: | ||||
| 		temata = hlavni_problemy_cisla(cislo) | ||||
| 
 | ||||
| 	if podproblemy is None: | ||||
| 		podproblemy_v_cislu(cislo, hlavni_problemy=temata) | ||||
| 
 | ||||
| 	body_slovnik = {} | ||||
| 	for tema in temata: | ||||
| 		body_slovnik[tema.id] = {} | ||||
| 		for problem in podproblemy[tema.id]: | ||||
| 			body_slovnik[tema.id][problem.id] = {} | ||||
| 	body_slovnik[-1] = {} | ||||
| 	for problem in podproblemy[-1]: | ||||
| 		body_slovnik[-1][problem.id] = {} | ||||
| 
 | ||||
| 	# zakládání prázdných záznamů pro řešitele | ||||
| 	for ar in aktivni_resitele: | ||||
| 		for tema in temata: | ||||
| 			for problem in podproblemy[tema.id]: | ||||
| 				body_slovnik[tema.id][problem.id][ar.id] = "" | ||||
| 
 | ||||
| 		for problem in podproblemy[-1]: | ||||
| 			body_slovnik[-1][problem.id][ar.id] = "" | ||||
| 
 | ||||
| 	temata = set(t.id for t in temata) | ||||
| 
 | ||||
| 	# vezmeme všechna řešení s body do daného čísla | ||||
| 	reseni_do_cisla = Reseni.objects.prefetch_related('problem', 'resitele', | ||||
| 		'hodnoceni_set').filter(hodnoceni__cislo_body=cislo) | ||||
| 
 | ||||
| 	# projdeme všechna řešení do čísla a přičteme body každému řešiteli do celkových | ||||
| 	# bodů i do bodů za problém | ||||
| 	for reseni in reseni_do_cisla: | ||||
| 
 | ||||
| 		# řešení může řešit více problémů | ||||
| 		for prob in list(reseni.problem.all()): | ||||
| 			nadproblem = hlavni_problem(prob) | ||||
| 			if nadproblem.id in temata: | ||||
| 				nadproblem_slovnik = body_slovnik[nadproblem.id] | ||||
| 			else: | ||||
| 				nadproblem_slovnik = body_slovnik[-1] | ||||
| 
 | ||||
| 			problem_slovnik = nadproblem_slovnik[prob.id] | ||||
| 
 | ||||
| 			# a mít více hodnocení | ||||
| 			for hodn in list(reseni.hodnoceni_set.all()): | ||||
| 				body = hodn.body | ||||
| 
 | ||||
| 				# a mít více řešitelů | ||||
| 				for resitel in list(reseni.resitele.all()): | ||||
| 					if resitel not in aktivni_resitele: | ||||
| 						print("Skipping {}".format(resitel.id)) | ||||
| 						continue | ||||
| 					pricti_body(problem_slovnik, resitel, body) | ||||
| 	return body_slovnik | ||||
| 
 | ||||
| 
 | ||||
| # TODELETE | ||||
| class FixedIterator: | ||||
| 	def next(self): | ||||
| 		return self.niter.__next__() | ||||
| 
 | ||||
| 	def __init__(self, niter): | ||||
| 		self.niter = niter | ||||
| # TODELETE | ||||
| 
 | ||||
| 
 | ||||
| def vysledkovka_cisla(cislo, context=None): | ||||
| 	if context is None: | ||||
| 		context = {} | ||||
| 	problemy = problemy_cisla(cislo) | ||||
| 	hlavni_problemy = hlavni_problemy_cisla(cislo, problemy) | ||||
| 	## TODO možná chytřeji vybírat aktivní řešitele | ||||
| 	# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají  | ||||
| 	# u alespoň jedné hodnoty něco jiného než NULL | ||||
| 	aktivni_resitele = list(aktivniResitele(cislo)) | ||||
| 
 | ||||
| 	# získáme body za číslo | ||||
| 	hlavni_problemy_slovnik, cislobody = secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy) | ||||
| 
 | ||||
| 	# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně | ||||
| 	resitel_rocnikbody_sezn = secti_body_za_rocnik(cislo, aktivni_resitele) | ||||
| 
 | ||||
| 	# získáme body odjakživa | ||||
| 	resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, cislo) | ||||
| 
 | ||||
| 	# řešitelé setřídění podle bodů za číslo sestupně | ||||
| 	setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn] | ||||
| 	setrizeni_resitele = [Resitel.objects.get(id=i) for i in setrizeni_resitele_id] | ||||
| 			 | ||||
| 	# spočítáme pořadí řešitelů | ||||
| 	setrizeni_resitele_body = [dvojice[1] for dvojice in resitel_rocnikbody_sezn] | ||||
| 	poradi = sloupec_s_poradim(setrizeni_resitele_body) | ||||
| 
 | ||||
| 	# vytvoříme jednotlivé sloupce výsledkovky | ||||
| 	radky_vysledkovky = [] | ||||
| 	i = 0 | ||||
| 
 | ||||
| 	def ne_clanek_ne_konfera(problem): | ||||
| 		return not(isinstance(problem.get_real_instance(), m.Clanek) or isinstance(problem.get_real_instance(), m.Konfera)) | ||||
| 
 | ||||
| 	temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy)) | ||||
| 
 | ||||
| 	# získáme body u jednotlivých témat | ||||
| 	podproblemy = podproblemy_v_cislu(cislo, problemy, temata_a_spol) | ||||
| 	problemy_slovnik = secti_body_za_cislo_podle_temat(cislo, aktivni_resitele, podproblemy, temata_a_spol) | ||||
| 
 | ||||
| 	# def not_empty(value): | ||||
| 	# 	return value != '' | ||||
| 	# | ||||
| 	# je_nejake_ostatni = any(filter(not_empty, hlavni_problemy_slovnik[-1].values())) > 0 | ||||
| 
 | ||||
| 	je_nejake_ostatni = len(hlavni_problemy) - len(temata_a_spol) > 0 | ||||
| 
 | ||||
| 	for ar_id in setrizeni_resitele_id: | ||||
| 		# získáme seznam bodů za problémy pro daného řešitele | ||||
| 		body_problemy = [] | ||||
| 		body_podproblemy = [] | ||||
| 		for hp in temata_a_spol: | ||||
| 			body_problemy.append(hlavni_problemy_slovnik[hp.id][ar_id]) | ||||
| 			body_podproblemy.append([problemy_slovnik[hp.id][it.id][ar_id] for it in podproblemy[hp.id]]) | ||||
| 		if je_nejake_ostatni: | ||||
| 			body_problemy.append(hlavni_problemy_slovnik[-1][ar_id]) | ||||
| 			body_podproblemy.append([problemy_slovnik[-1][it.id][ar_id] for it in podproblemy[-1]]) | ||||
| 		# vytáhneme informace pro daného řešitele | ||||
| 		radek = RadekVysledkovkyCisla( | ||||
| 			poradi[i], # pořadí | ||||
| 			Resitel.objects.get(id=ar_id), # řešitel (z id) | ||||
| 			body_problemy, # seznam bodů za hlavní problémy čísla | ||||
| 			cislobody[ar_id], # body za číslo | ||||
| 			setrizeni_resitele_body[i], # body za ročník (spočítané výše s pořadím) | ||||
| 			resitel_odjakzivabody_slov[ar_id], # body odjakživa | ||||
| 			cislo.rocnik, | ||||
| 			body_podproblemy,  # body všech podproblémů | ||||
| 			FixedIterator(body_podproblemy.__iter__())  # TODELETE | ||||
| 		)  # ročník semináře pro zjištění ročníku řešitele | ||||
| 		radky_vysledkovky.append(radek) | ||||
| 		i += 1 | ||||
| 
 | ||||
| 	# vytahané informace předáváme do kontextu | ||||
| 	context['cislo'] = cislo | ||||
| 	context['radky_vysledkovky'] = radky_vysledkovky | ||||
| 	context['problemy'] = temata_a_spol | ||||
| 	context['ostatni'] = je_nejake_ostatni | ||||
| 	pt = [podproblemy[it.id] for it in temata_a_spol]+[podproblemy[-1]] | ||||
| 	context['podproblemy'] = pt | ||||
| 	context['podproblemy_iter'] = FixedIterator(pt.__iter__())  # TODELETE | ||||
| 	#context['v_cisle_zadane'] = TODO | ||||
| 	#context['resene_problemy'] = resene_problemy | ||||
| 	return context | ||||
| 
 | ||||
| class CisloView(generic.DetailView): | ||||
| 	# FIXME zobrazování témátek a vůbec, teď je tam jen odkaz na číslo v pdf | ||||
|  |  | |||
							
								
								
									
										437
									
								
								seminar/views/vysledkovka.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										437
									
								
								seminar/views/vysledkovka.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,437 @@ | |||
| import seminar.models as m | ||||
| from django.db.models import Q, Sum, Count | ||||
| from seminar.utils import aktivniResitele, resi_v_rocniku, cisla_rocniku, hlavni_problemy_rocniku, hlavni_problem, hlavni_problemy_cisla, problemy_cisla, podproblemy_v_cislu | ||||
| ### Výsledky | ||||
| 
 | ||||
| def sloupec_s_poradim(setrizene_body): | ||||
| 	""" | ||||
| 	Ze seznamu obsahujícího sestupně setřízené body řešitelů za daný ročník  | ||||
| 	vytvoří seznam s pořadími (včetně 3.-5. a pak 2 volná místa atp.), | ||||
| 	podle toho, jak jdou za sebou ve výsledkovce. | ||||
| 	Parametr: | ||||
| 		setrizene_body (seznam integerů): sestupně setřízená čísla | ||||
| 
 | ||||
| 	Výstup: | ||||
| 		sloupec_s_poradim (seznam stringů) | ||||
| 	""" | ||||
| 
 | ||||
| 	# ze seznamu obsahujícího setřízené body spočítáme sloupec s pořadím	 | ||||
| 	aktualni_poradi = 1 | ||||
| 	sloupec_s_poradim = [] | ||||
| 
 | ||||
| 	# seskupíme seznam všech bodů podle hodnot | ||||
| 	for index in range(0, len(setrizene_body)): | ||||
| 		# pokud je pořadí větší než číslo řádku, tak jsme vypsali větší rozsah a chceme | ||||
| 		# vypsat už jen prázdné místo, než dojdeme na správný řádek | ||||
| 		if (index + 1) < aktualni_poradi: | ||||
| 			sloupec_s_poradim.append("") | ||||
| 			continue | ||||
| 		velikost_skupiny = 0 | ||||
| 		# zjistíme počet po sobě jdoucích stejných hodnot | ||||
| 		while setrizene_body[index] == setrizene_body[index + velikost_skupiny]: | ||||
| 			velikost_skupiny = velikost_skupiny + 1 | ||||
| 			# na konci musíme ošetřit přetečení seznamu | ||||
| 			if (index + velikost_skupiny) > len(setrizene_body) - 1: | ||||
| 				break | ||||
| 		# pokud je velikost skupiny 1, vypíšu pořadí | ||||
| 		if velikost_skupiny == 1: | ||||
| 			sloupec_s_poradim.append("{}.".format(aktualni_poradi)) | ||||
| 		# pokud je skupina větší, vypíšu rozsah | ||||
| 		else: | ||||
| 			sloupec_s_poradim.append("{}.–{}.".format(aktualni_poradi,  | ||||
| 						aktualni_poradi+velikost_skupiny-1)) | ||||
| 		# zvětšíme aktuální pořadí o tolik, kolik pozic bylo přeskočeno | ||||
| 		aktualni_poradi = aktualni_poradi + velikost_skupiny | ||||
| 	return sloupec_s_poradim | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| def body_resitelu(resitele, za, odjakziva=True): | ||||
| 	""" Funkce počítající počty bodů pro zadané řešitele,  | ||||
| 	buď odjakživa do daného ročníku/čísla anebo za daný ročník/číslo. | ||||
| 	Parametry: | ||||
| 		resitele (seznam obsahující položky typu Resitel): aktivní řešitelé | ||||
| 		za (Rocnik/Cislo): za co se mají počítat body  | ||||
| 			(generování starších výsledkovek) | ||||
| 		odjakziva (bool): zda se mají počítat body odjakživa, nebo jen za číslo/ročník | ||||
| 			zadané v "za" | ||||
| 	Výstup: | ||||
| 		slovník (Resitel.id):body | ||||
| 	"""		 | ||||
| 	resitele_id = [r.id for r in resitele] | ||||
| 	# Zjistíme, typ objektu v parametru "za" | ||||
| 	if isinstance(za, m.Rocnik): | ||||
| 		cislo = None | ||||
| 		rocnik = za | ||||
| 		rok = rocnik.prvni_rok | ||||
| 	elif isinstance(za, m.Cislo): | ||||
| 		cislo = za | ||||
| 		rocnik = None | ||||
| 		rok = cislo.rocnik.prvni_rok | ||||
| 	else: | ||||
| 		assert True, "body_resitelu: za není ani číslo ani ročník."	 | ||||
| 
 | ||||
| 
 | ||||
| 	# Kvůli rychlosti používáme sčítáme body už v databázi, viz  | ||||
| 	# https://docs.djangoproject.com/en/3.0/topics/db/aggregation/,  | ||||
| 	# sekce Filtering on annotations (protože potřebujeme filtrovat výsledky | ||||
| 	# jen do nějakého data, abychom uměli správně nagenerovat výsledkovky i  | ||||
| 	# za historická čísla. | ||||
| 
 | ||||
| 	# Níže se vytváří dotaz na součet bodů za správně vyfiltrovaná hodnocení,  | ||||
| 	# který se použije ve výsledném dotazu. | ||||
| 	if cislo and odjakziva: # Body se sčítají odjakživa do zadaného čísla. | ||||
| 		# Vyfiltrujeme všechna hodnocení, která jsou buď ze starších ročníků,  | ||||
| 		# anebo ze stejného ročníku, jak je zadané číslo, tam ale sčítáme jen | ||||
| 		# pro čísla s pořadím nejvýše stejným, jako má zadané číslo. | ||||
| 		body_k_zapocteni = Sum('reseni__hodnoceni__body',  | ||||
| 			filter=( Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lt=rok) | | ||||
| 				 Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok=rok, | ||||
| 				   reseni__hodnoceni__cislo_body__poradi__lte=cislo.poradi) )) | ||||
| 	elif cislo and not odjakziva: # Body se sčítají za dané číslo. | ||||
| 		body_k_zapocteni = Sum('reseni__hodnoceni__body', | ||||
| 			filter=( Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok=rok, | ||||
| 					reseni__hodnoceni__cislo_body__poradi__lte=cislo.poradi) )) | ||||
| 	elif rocnik and odjakziva: # Spočítáme body za starší ročníky až do zadaného včetně.	 | ||||
| 		body_k_zapocteni = Sum('reseni__hodnoceni__body',  | ||||
| 			filter= Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lte=rok)) | ||||
| 	elif rocnik and not odjakziva: # Spočítáme body za daný ročník. | ||||
| 		body_k_zapocteni = Sum('reseni__hodnoceni__body', | ||||
| 			filter= Q(reseni__hodnoceni__cislo_body__rocnik=rocnik)) | ||||
| 	else:  | ||||
| 		assert True, "body_resitelu: Neplatná kombinace za a odjakživa." | ||||
| 
 | ||||
| 	# Následující řádek přidá ke každému řešiteli údaj ".body" se součtem jejich bodů | ||||
| 	resitele_s_body = m.Resitel.objects.filter(id__in=resitele_id).annotate( | ||||
| 		body=body_k_zapocteni) | ||||
| 	# Teď jen z QuerySetu řešitelů anotovaných body vygenerujeme slovník  | ||||
| 	# indexovaný řešitelským id obsahující body.  | ||||
| 	# Pokud jsou body None, nahradíme za 0. | ||||
| 	slovnik = {int(res.id) : (res.body if res.body else 0) for res in resitele_s_body} | ||||
| 	return slovnik | ||||
| 
 | ||||
| class RadekVysledkovkyRocniku(object): | ||||
| 	""" Obsahuje věci, které se hodí vědět při konstruování výsledkovky. | ||||
| 	Umožňuje snazší práci v templatu (lepší, než seznam).""" | ||||
| 
 | ||||
| 	def __init__(self, poradi, resitel, body_cisla_sezn, body_rocnik, body_odjakziva, rok): | ||||
| 		self.poradi = poradi | ||||
| 		self.resitel = resitel | ||||
| 		self.rocnik_resitele = resitel.rocnik(rok) | ||||
| 		self.body_rocnik = body_rocnik | ||||
| 		self.body_celkem_odjakziva = body_odjakziva | ||||
| 		self.body_cisla_sezn = body_cisla_sezn | ||||
| 		self.titul = resitel.get_titul(body_odjakziva) | ||||
| 
 | ||||
| def setrid_resitele_a_body(slov_resitel_body): | ||||
| 	setrizeni_resitele_id = [dvojice[0] for dvojice in slov_resitel_body] | ||||
| 	setrizeni_resitele = [m.Resitel.objects.get(id=i) for i in setrizeni_resitele_id] | ||||
| 	setrizene_body = [dvojice[1] for dvojice in slov_resitel_body] | ||||
| 	return setrizeni_resitele_id, setrizeni_resitele, setrizene_body | ||||
| 
 | ||||
| def vysledkovka_rocniku(rocnik, jen_verejne=True): | ||||
| 	""" Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve | ||||
| 	formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html" | ||||
| 	""" | ||||
| 	 | ||||
| 	## TODO možná chytřeji vybírat aktivní řešitele | ||||
| 	# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají  | ||||
| 	# u alespoň jedné hodnoty něco jiného než NULL | ||||
| 	aktivni_resitele = list(resi_v_rocniku(rocnik)) | ||||
| 	cisla = cisla_rocniku(rocnik, jen_verejne) | ||||
| 	body_cisla_slov = {} | ||||
| 	for cislo in cisla: | ||||
| 		# získáme body za číslo | ||||
| 		_, cislobody = secti_body_za_cislo(cislo, aktivni_resitele) | ||||
| 		body_cisla_slov[cislo.id] = cislobody | ||||
| 
 | ||||
| 	# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně | ||||
| 	resitel_rocnikbody_sezn = secti_body_za_rocnik(rocnik, aktivni_resitele) | ||||
| 
 | ||||
| 	# setřídíme řešitele podle počtu bodů a získáme seznam s body od nejvyšších po nenižší | ||||
| 	setrizeni_resitele_id, setrizeni_resitele, setrizene_body = setrid_resitele_a_body(resitel_rocnikbody_sezn) | ||||
| 	poradi = sloupec_s_poradim(setrizene_body) | ||||
| 
 | ||||
| 	# získáme body odjakživa | ||||
| 	resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, rocnik) | ||||
| 
 | ||||
| 	# vytvoříme jednotlivé sloupce výsledkovky | ||||
| 	radky_vysledkovky = [] | ||||
| 	i = 0 | ||||
| 	for ar_id in setrizeni_resitele_id: | ||||
| 		# seznam počtu bodů daného řešitele pro jednotlivá čísla | ||||
| 		body_cisla_sezn = [] | ||||
| 		for cislo in cisla: | ||||
| 			body_cisla_sezn.append(body_cisla_slov[cislo.id][ar_id]) | ||||
| 
 | ||||
| 		# vytáhneme informace pro daného řešitele | ||||
| 		radek = RadekVysledkovkyRocniku( | ||||
| 			poradi[i], # pořadí | ||||
| 			m.Resitel.objects.get(id=ar_id), # řešitel (z id) | ||||
| 			body_cisla_sezn, # seznam bodů za čísla | ||||
| 			setrizene_body[i], # body za ročník (spočítané výše s pořadím) | ||||
| 			resitel_odjakzivabody_slov[ar_id], # body odjakživa | ||||
| 			rocnik) # ročník semináře pro získání ročníku řešitele | ||||
| 		radky_vysledkovky.append(radek) | ||||
| 		i += 1 | ||||
| 
 | ||||
| 	return radky_vysledkovky | ||||
| class RadekVysledkovkyCisla(object): | ||||
| 	"""Obsahuje věci, které se hodí vědět při konstruování výsledkovky. | ||||
| 	Umožňuje snazší práci v templatu (lepší, než seznam).""" | ||||
| 
 | ||||
| 	def __init__(self, poradi, resitel, body_problemy_sezn, | ||||
| 				body_cislo, body_rocnik, body_odjakziva, rok, body_podproblemy, body_podproblemy_iter): | ||||
| 		self.resitel = resitel | ||||
| 		self.rocnik_resitele = resitel.rocnik(rok) | ||||
| 		self.body_cislo = body_cislo | ||||
| 		self.body_rocnik = body_rocnik | ||||
| 		self.body_celkem_odjakziva = body_odjakziva | ||||
| 		self.poradi = poradi | ||||
| 		self.body_problemy_sezn = body_problemy_sezn | ||||
| 		self.titul = resitel.get_titul(body_odjakziva) | ||||
| 		self.body_podproblemy = body_podproblemy | ||||
| 		self.body_podproblemy_iter = body_podproblemy_iter  # TODELETE | ||||
| 
 | ||||
| 
 | ||||
| def pricti_body(slovnik, resitel, body): | ||||
| 	""" Přiřazuje danému řešiteli body do slovníku. """ | ||||
| 	# testujeme na None (""), pokud je to první řešení  | ||||
| 	# daného řešitele, předěláme na 0 | ||||
| 	# (v dalším kroku přičteme reálný počet bodů), | ||||
| 	# rozlišujeme tím mezi 0 a neodevzdaným řešením | ||||
| 	if slovnik[resitel.id] == "": | ||||
| 		slovnik[resitel.id] = 0 | ||||
| 	 | ||||
| 	slovnik[resitel.id] += body | ||||
| 
 | ||||
| def secti_body_za_rocnik(za, aktivni_resitele): | ||||
| 	""" Spočítá body za ročník (celý nebo do daného čísla),  | ||||
| 		setřídí je sestupně a vrátí jako seznam. | ||||
| 	Parametry: | ||||
| 		za (typu Rocnik nebo Cislo)	spočítá za ročník, nebo za ročník až do  | ||||
| 						daného čísla | ||||
| 	""" | ||||
| 	# spočítáme všem řešitelům jejich body za ročník (False => ne odjakživa) | ||||
| 	resitel_rocnikbody_slov = body_resitelu(aktivni_resitele, za, False) | ||||
| 	# zeptáme se na dvojice (řešitel, body) za ročník a setřídíme sestupně | ||||
| 	resitel_rocnikbody_sezn = sorted(resitel_rocnikbody_slov.items(), | ||||
| 					key = lambda x: x[1], reverse = True) | ||||
| 	return resitel_rocnikbody_sezn | ||||
| 
 | ||||
| def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy=None): | ||||
| 	""" Spočítá u řešitelů body za číslo a za jednotlivé hlavní problémy (témata).""" | ||||
| 	# TODO setřídit hlavní problémy čísla podle id, ať jsou ve stejném pořadí pokaždé | ||||
| 	# pro každý hlavní problém zavedeme slovník s body za daný hlavní problém  | ||||
| 	# pro jednotlivé řešitele (slovník slovníků hlavních problémů) | ||||
| 	if hlavni_problemy is None: | ||||
| 		hlavni_problemy = hlavni_problemy_cisla(cislo) | ||||
| 
 | ||||
| 	def ne_clanek_ne_konfera(problem): | ||||
| 		return not(isinstance(problem.get_real_instance(), m.Clanek) or isinstance(problem.get_real_instance(), m.Konfera)) | ||||
| 
 | ||||
| 	temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy)) | ||||
| 
 | ||||
| 	def cosi(problem): | ||||
| 		return problem.id | ||||
| 
 | ||||
| 	hlavni_problemy_slovnik = {} | ||||
| 	for hp in temata_a_spol: | ||||
| 		hlavni_problemy_slovnik[hp.id] = {} | ||||
| 
 | ||||
| 	hlavni_problemy_slovnik[-1] = {} | ||||
| 
 | ||||
| 	# zakládání prázdných záznamů pro řešitele | ||||
| 	cislobody = {} | ||||
| 	for ar in aktivni_resitele: | ||||
| 		# řešitele převedeme na řetězec pomocí unikátního id | ||||
| 		cislobody[ar.id] = "" | ||||
| 		for hp in temata_a_spol: | ||||
| 			slovnik = hlavni_problemy_slovnik[hp.id] | ||||
| 			slovnik[ar.id] = "" | ||||
| 
 | ||||
| 		hlavni_problemy_slovnik[-1][ar.id] = "" | ||||
| 	 | ||||
| 	# vezmeme všechna řešení s body do daného čísla | ||||
| 	reseni_do_cisla = m.Reseni.objects.prefetch_related('problem', 'resitele',  | ||||
| 				'hodnoceni_set').filter(hodnoceni__cislo_body=cislo) | ||||
| 	 | ||||
| 	# projdeme všechna řešení do čísla a přičteme body každému řešiteli do celkových | ||||
| 	# bodů i do bodů za problém | ||||
| 	for reseni in reseni_do_cisla: | ||||
| 		 | ||||
| 		# řešení může řešit více problémů | ||||
| 		for prob in list(reseni.problem.all()): | ||||
| 			nadproblem = hlavni_problem(prob) | ||||
| 			if ne_clanek_ne_konfera(nadproblem): | ||||
| 				nadproblem_slovnik = hlavni_problemy_slovnik[nadproblem.id] | ||||
| 			else: | ||||
| 				nadproblem_slovnik = hlavni_problemy_slovnik[-1] | ||||
| 			 | ||||
| 			# a mít více hodnocení | ||||
| 			for hodn in list(reseni.hodnoceni_set.all()): | ||||
| 				body = hodn.body	 | ||||
| 			 | ||||
| 				# a mít více řešitelů | ||||
| 				for resitel in list(reseni.resitele.all()): | ||||
| 					if resitel not in aktivni_resitele: | ||||
| 						print("Skipping {}".format(resitel.id)) | ||||
| 						continue | ||||
| 					pricti_body(cislobody, resitel, body) | ||||
| 					pricti_body(nadproblem_slovnik, resitel, body) | ||||
| 	return hlavni_problemy_slovnik, cislobody | ||||
| 
 | ||||
| 
 | ||||
| def secti_body_za_cislo_podle_temat(cislo, aktivni_resitele, podproblemy=None, temata=None): | ||||
| 	""" Spočítá u řešitelů body za číslo za úlohy v jednotlivých hlavních problémech (témata).""" | ||||
| 	if temata is None: | ||||
| 		temata = hlavni_problemy_cisla(cislo) | ||||
| 
 | ||||
| 	if podproblemy is None: | ||||
| 		podproblemy_v_cislu(cislo, hlavni_problemy=temata) | ||||
| 
 | ||||
| 	body_slovnik = {} | ||||
| 	for tema in temata: | ||||
| 		body_slovnik[tema.id] = {} | ||||
| 		for problem in podproblemy[tema.id]: | ||||
| 			body_slovnik[tema.id][problem.id] = {} | ||||
| 	body_slovnik[-1] = {} | ||||
| 	for problem in podproblemy[-1]: | ||||
| 		body_slovnik[-1][problem.id] = {} | ||||
| 
 | ||||
| 	# zakládání prázdných záznamů pro řešitele | ||||
| 	for ar in aktivni_resitele: | ||||
| 		for tema in temata: | ||||
| 			for problem in podproblemy[tema.id]: | ||||
| 				body_slovnik[tema.id][problem.id][ar.id] = "" | ||||
| 
 | ||||
| 		for problem in podproblemy[-1]: | ||||
| 			body_slovnik[-1][problem.id][ar.id] = "" | ||||
| 
 | ||||
| 	temata = set(t.id for t in temata) | ||||
| 
 | ||||
| 	# vezmeme všechna řešení s body do daného čísla | ||||
| 	reseni_do_cisla = m.Reseni.objects.prefetch_related('problem', 'resitele', | ||||
| 		'hodnoceni_set').filter(hodnoceni__cislo_body=cislo) | ||||
| 
 | ||||
| 	# projdeme všechna řešení do čísla a přičteme body každému řešiteli do celkových | ||||
| 	# bodů i do bodů za problém | ||||
| 	for reseni in reseni_do_cisla: | ||||
| 
 | ||||
| 		# řešení může řešit více problémů | ||||
| 		for prob in reseni.problem.all(): | ||||
| 			nadproblem = hlavni_problem(prob) | ||||
| 			if nadproblem.id in temata: | ||||
| 				nadproblem_slovnik = body_slovnik[nadproblem.id] | ||||
| 			else: | ||||
| 				nadproblem_slovnik = body_slovnik[-1] | ||||
| 
 | ||||
| 			problem_slovnik = nadproblem_slovnik[prob.id] | ||||
| 
 | ||||
| 			# a mít více hodnocení | ||||
| 			for hodn in reseni.hodnoceni_set.all(): | ||||
| 				body = hodn.body | ||||
| 
 | ||||
| 				# a mít více řešitelů | ||||
| 				for resitel in reseni.resitele.all(): | ||||
| 					if resitel not in aktivni_resitele: | ||||
| 						print("Skipping {}".format(resitel.id)) | ||||
| 						continue | ||||
| 					pricti_body(problem_slovnik, resitel, body) | ||||
| 	return body_slovnik | ||||
| 
 | ||||
| 
 | ||||
| # TODELETE | ||||
| class FixedIterator: | ||||
| 	def next(self): | ||||
| 		return self.niter.__next__() | ||||
| 
 | ||||
| 	def __init__(self, niter): | ||||
| 		self.niter = niter | ||||
| # TODELETE | ||||
| 
 | ||||
| 
 | ||||
| def vysledkovka_cisla(cislo, context=None): | ||||
| 	if context is None: | ||||
| 		context = {} | ||||
| 	problemy = problemy_cisla(cislo) | ||||
| 	hlavni_problemy = hlavni_problemy_cisla(cislo, problemy) | ||||
| 	## TODO možná chytřeji vybírat aktivní řešitele | ||||
| 	# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají  | ||||
| 	# u alespoň jedné hodnoty něco jiného než NULL | ||||
| 	aktivni_resitele = list(aktivniResitele(cislo)) | ||||
| 
 | ||||
| 	# získáme body za číslo | ||||
| 	hlavni_problemy_slovnik, cislobody = secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy) | ||||
| 
 | ||||
| 	# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně | ||||
| 	resitel_rocnikbody_sezn = secti_body_za_rocnik(cislo, aktivni_resitele) | ||||
| 
 | ||||
| 	# získáme body odjakživa | ||||
| 	resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, cislo) | ||||
| 
 | ||||
| 	# řešitelé setřídění podle bodů za číslo sestupně | ||||
| 	setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn] | ||||
| 	setrizeni_resitele = [m.Resitel.objects.get(id=i) for i in setrizeni_resitele_id] | ||||
| 			 | ||||
| 	# spočítáme pořadí řešitelů | ||||
| 	setrizeni_resitele_body = [dvojice[1] for dvojice in resitel_rocnikbody_sezn] | ||||
| 	poradi = sloupec_s_poradim(setrizeni_resitele_body) | ||||
| 
 | ||||
| 	# vytvoříme jednotlivé sloupce výsledkovky | ||||
| 	radky_vysledkovky = [] | ||||
| 	i = 0 | ||||
| 
 | ||||
| 	def ne_clanek_ne_konfera(problem): | ||||
| 		return not(isinstance(problem.get_real_instance(), m.Clanek) or isinstance(problem.get_real_instance(), m.Konfera)) | ||||
| 
 | ||||
| 	temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy)) | ||||
| 
 | ||||
| 	# získáme body u jednotlivých témat | ||||
| 	podproblemy = podproblemy_v_cislu(cislo, problemy, temata_a_spol) | ||||
| 	problemy_slovnik = secti_body_za_cislo_podle_temat(cislo, aktivni_resitele, podproblemy, temata_a_spol) | ||||
| 
 | ||||
| 	# def not_empty(value): | ||||
| 	# 	return value != '' | ||||
| 	# | ||||
| 	# je_nejake_ostatni = any(filter(not_empty, hlavni_problemy_slovnik[-1].values())) > 0 | ||||
| 
 | ||||
| 	je_nejake_ostatni = len(hlavni_problemy) - len(temata_a_spol) > 0 | ||||
| 
 | ||||
| 	for ar_id in setrizeni_resitele_id: | ||||
| 		# získáme seznam bodů za problémy pro daného řešitele | ||||
| 		body_problemy = [] | ||||
| 		body_podproblemy = [] | ||||
| 		for hp in temata_a_spol: | ||||
| 			body_problemy.append(hlavni_problemy_slovnik[hp.id][ar_id]) | ||||
| 			body_podproblemy.append([problemy_slovnik[hp.id][it.id][ar_id] for it in podproblemy[hp.id]]) | ||||
| 		if je_nejake_ostatni: | ||||
| 			body_problemy.append(hlavni_problemy_slovnik[-1][ar_id]) | ||||
| 			body_podproblemy.append([problemy_slovnik[-1][it.id][ar_id] for it in podproblemy[-1]]) | ||||
| 		# vytáhneme informace pro daného řešitele | ||||
| 		radek = RadekVysledkovkyCisla( | ||||
| 			poradi[i], # pořadí | ||||
| 			m.Resitel.objects.get(id=ar_id), # řešitel (z id) | ||||
| 			body_problemy, # seznam bodů za hlavní problémy čísla | ||||
| 			cislobody[ar_id], # body za číslo | ||||
| 			setrizeni_resitele_body[i], # body za ročník (spočítané výše s pořadím) | ||||
| 			resitel_odjakzivabody_slov[ar_id], # body odjakživa | ||||
| 			cislo.rocnik, | ||||
| 			body_podproblemy,  # body všech podproblémů | ||||
| 			FixedIterator(body_podproblemy.__iter__())  # TODELETE | ||||
| 		)  # ročník semináře pro zjištění ročníku řešitele | ||||
| 		radky_vysledkovky.append(radek) | ||||
| 		i += 1 | ||||
| 
 | ||||
| 	# vytahané informace předáváme do kontextu | ||||
| 	context['cislo'] = cislo | ||||
| 	context['radky_vysledkovky'] = radky_vysledkovky | ||||
| 	context['problemy'] = temata_a_spol | ||||
| 	context['ostatni'] = je_nejake_ostatni | ||||
| 	pt = [podproblemy[it.id] for it in temata_a_spol]+[podproblemy[-1]] | ||||
| 	context['podproblemy'] = pt | ||||
| 	context['podproblemy_iter'] = FixedIterator(pt.__iter__())  # TODELETE | ||||
| 	#context['v_cisle_zadane'] = TODO | ||||
| 	#context['resene_problemy'] = resene_problemy | ||||
| 	return context | ||||
		Loading…
	
		Reference in a new issue