From caa274460d79ca9033c10242f752ab1c412759f1 Mon Sep 17 00:00:00 2001
From: Anet <anet@erebus>
Date: Wed, 11 Mar 2020 23:00:54 +0100
Subject: [PATCH] =?UTF-8?q?nepadaj=C3=ADc=C3=AD=20(ale=20nezobrazuj=C3=ADc?=
 =?UTF-8?q?=C3=AD=20se)=20verze=20v=C3=BDsledkovky=20v2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 seminar/templates/seminar/archiv/cislo.html |  16 +-
 seminar/views.py                            | 229 +++++++++++++-------
 2 files changed, 154 insertions(+), 91 deletions(-)

diff --git a/seminar/templates/seminar/archiv/cislo.html b/seminar/templates/seminar/archiv/cislo.html
index d8b030c8..63788e06 100644
--- a/seminar/templates/seminar/archiv/cislo.html
+++ b/seminar/templates/seminar/archiv/cislo.html
@@ -67,21 +67,21 @@
         {% endfor %}
         <th class='border-r'>Za číslo</sup>
         <th class='border-r'>Za ročník
-        <th class='border-r'>Odjakživa
-    {% for rv in vysledkovka %}
+        {#<th class='border-r'>Odjakživa#}
+    {% for rv in radky_vysledkovky %}
       <tr>
         <td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %}
             <th class='border-r'>
-            {% if rv.titul %}
-              {{ rv.titul }}<sup>MM</sup>
+            {% if rv.resitel.get_titul != "" %}
+              {{ rv.resitel.get_titul }}<sup>MM</sup>
             {% endif %}
-            {{ rv.resitel.plne_jmeno }}
-        {% for b in rv.body_ulohy %}
+            {{ rv.resitel.osoba.plne_jmeno }}
+        {% for b in rv.hlavni_problemy_body %}
         <td class='border-r'>{{ b }}
         {% endfor %}
         <td class='border-r'>{{ rv.body_cislo }}
-        <td class='border-r'><b>{{ rv.body_celkem_rocnik }}</b>
-        <td class='border-r'>{{ rv.body_celkem_odjakziva }}
+        <td class='border-r'><b>{{ rv.body_rocnik }}</b>
+        {# <td class='border-r'>{{ rv.body_celkem_odjakziva }}#}
       </tr>
     {% endfor %}
     </table>
diff --git a/seminar/views.py b/seminar/views.py
index abb1ad77..298e8e0e 100644
--- a/seminar/views.py
+++ b/seminar/views.py
@@ -426,28 +426,34 @@ def sloupec_s_poradim(seznam_s_body):
 		aktualni_poradi = aktualni_poradi + velikost_skupiny
 	return sloupec_s_poradim
 
-# spočítá součet bodů získaných daným řešitelem za zadaný problém a všechny jeho podproblémy
-def __soucet_resitele_problemu(problem, resitel, cislo, soucet):
-	# sečteme body za daný problém přes všechna řešení daného problému 
-	# od daného řešitele
-	reseni_resitele = Reseni.objects.filter(resitele__in=resitel)
-	hodnoceni_resitele = problem.hodnoceni.filter(reseni__in=reseni_resitele, 
-				cislo_body=cislo)
-	# XXX chyba na řádku výše - řešení může mít více řešitelů, asi chceme contains
-	# nebo in
-	for r in hodnoceni_resitele:
-		soucet += r.body
+## spočítá součet bodů získaných daným řešitelem za zadaný problém a všechny jeho podproblémy
+#def __soucet_resitele_problemu(problem, resitel, cislo, soucet):
+#	# sečteme body za daný problém přes všechna řešení daného problému 
+#	# od daného řešitele
+#	reseni_resitele = s.Reseni_Resitele.objects.filter(resitele=resitel)
+#	hodnoceni_resitele = problem.hodnoceni.filter(reseni__in=reseni_resitele, 
+#				cislo_body=cislo)
+#	# XXX chyba na řádku výše - řešení může mít více řešitelů, asi chceme contains
+#	# nebo in
+#	for r in hodnoceni_resitele:
+#		soucet += r.body
+#
+#	# a přičteme k tomu hodnocení všech podproblémů
+#	for p in problem.podproblem.all(): 
+#	# i přes jméno by to měla být množina jeho podproblémů
+#		soucet += __soucet_resitele_problemu(p, resitel, soucet)
+#	return soucet
 
-	# a přičteme k tomu hodnocení všech podproblémů
-	for p in problem.podproblem.all(): 
-	# i přes jméno by to měla být množina jeho podproblémů
-		soucet += __soucet_resitele_problemu(p, resitel, soucet)
-	return soucet
+## spočítá součet všech bodů ze všech podproblémů daného problému daného řešitele
+#def body_resitele_problemu_v_cisle(problem, resitel, cislo):
+#	# probably FIXED: nezohledňuje číslo, do kterého se body počítají
+#	return __soucet_resitele_problemu(problem, resitel, cislo, 0)
 
-# spočítá součet všech bodů ze všech podproblémů daného problému daného řešitele
-def body_resitele_problemu_v_cisle(problem, resitel, cislo):
-	# probably FIXED: nezohledňuje číslo, do kterého se body počítají
-	return __soucet_resitele_problemu(problem, resitel, cislo, 0)
+# pro daný problém vrátí jeho nejvyšší nadproblém
+def hlavni_problem(problem):
+	while not(problem.nadproblem == None):
+		problem = problem.nadproblem
+	return problem
 
 # vrátí list všech problémů s body v daném čísle, které již nemají nadproblém
 def hlavni_problemy_cisla(cislo):
@@ -463,9 +469,7 @@ def hlavni_problemy_cisla(cislo):
 	# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém)
 	hlavni_problemy = []
 	for p in problemy:
-		while not(p.nadproblem == None):
-			p = p.nadproblem
-		hlavni_problemy.append(p)
+		hlavni_problemy.append(hlavni_problem(p))
 		
 	# zunikátnění
 	hlavni_problemy_set = set(hlavni_problemy)
@@ -474,38 +478,54 @@ def hlavni_problemy_cisla(cislo):
 	
 	return hlavni_problemy
 
-def body_resitele_odjakziva(resitel):
-	body = 0
-	resitelova_hodnoceni = Hodnoceni.objects.select_related('body').all().filter(reseni_resitele=resitel)
-	# TODO: v radku nahore chceme _in nebo _contains
-	for hodnoceni in resitelova_hodnoceni:
-		body = body + hodnoceni.body
-	return body
+# vrátí slovník řešitel:body obsahující počty bodů zadaných řešitelů za daný ročník
+def body_resitelu_za_rocnik(rocnik, aktivni_resitele):
+	body_za_rocnik = {}
+	# inicializujeme na 0 pro všechny aktivní řešitele
+	for ar in aktivni_resitele:
+		body_za_rocnik[str(ar.id)] = 0
+	
+	# spočítáme body řešitelům přes všechna řešení s hodnocením v daném ročníku
+	reseni = Reseni.objects.filter(hodnoceni__cislo_body__rocnik=rocnik)
+	for res in reseni:
+		for resitel in res.resitele.all():
+			for hodn in res.hodnoceni.all():
+				body_za_rocnik[str(resitel.id)] += hodn.body
+	return body_za_rocnik
+
+#def body_resitele_odjakziva(resitel):
+#	body = 0
+#	resitelova_hodnoceni = Hodnoceni.objects.select_related('body').all().filter(reseni_resitele=resitel)
+#	# TODO: v radku nahore chceme _in nebo _contains
+#	for hodnoceni in resitelova_hodnoceni:
+#		body = body + hodnoceni.body
+#	return body
 
 # spočítá součet všech bodů řešitele za dané číslo
-def body_resitele_v_cisle(resitel, cislo):
-	hlavni_problemy = hlavni_problemy_cisla(cislo)
-	body_resitele = 0
-	for h in hlavni_problemy:
-		body_resitele = body_resitele + body_resitele_problemu_v_cisle(h, resitel, cislo)
-	# TODO: je rozdíl mezi odevzdanou úlohou za 0 a tím, když řešitel nic neodevzdal
-	# řešit přes kontrolu velikosti množiny řešení daného problému do daného čísla?
-	# Tady to ale nevadí, tady se počítá součet za číslo.
-	return body_resitele
+#def body_resitele_v_cisle(resitel, cislo):
+#	hlavni_problemy = hlavni_problemy_cisla(cislo)
+#	body_resitele = 0
+#	for h in hlavni_problemy:
+#		body_resitele = body_resitele + body_resitele_problemu_v_cisle(h, resitel, cislo)
+#	# TODO: je rozdíl mezi odevzdanou úlohou za 0 a tím, když řešitel nic neodevzdal
+#	# řešit přes kontrolu velikosti množiny řešení daného problému do daného čísla?
+#	# Tady to ale nevadí, tady se počítá součet za číslo.
+#	return body_resitele
 
 # spočítá součet všech bodů řešitele za daný rok (nebo jen do daného čísla včetně)
-def body_resitele_v_rocniku(resitel, rocnik, do_cisla=None):
-	# pokud do_cisla=None, tak do posledního čísla v ročníku
-	# do_cisla je objekt Cislo
-	cisla = rocnik.cisla.all() # funkce vrátí pole objektů 
-	# Cislo už lexikograficky setřízené, viz models
-	body = 0
-	for cislo in cisla:
-		if cislo.poradi == do_cisla.poradi: break
-		# druhá část zaručuje, že máme výsledky do daného čísla včetně
-		body = body + body_resitele_v_cisle(resitel, cislo)
-	return body
+#def body_resitele_v_rocniku(resitel, rocnik, do_cisla=None):
+#	# pokud do_cisla=None, tak do posledního čísla v ročníku
+#	# do_cisla je objekt Cislo
+#	cisla = rocnik.cisla.all() # funkce vrátí pole objektů 
+#	# Cislo už lexikograficky setřízené, viz models
+#	body = 0
+#	for cislo in cisla:
+#		if cislo.poradi == do_cisla.poradi: break
+#		# druhá část zaručuje, že máme výsledky do daného čísla včetně
+#		body = body + body_resitele_v_cisle(resitel, cislo)
+#	return body
 
+# TODO: předělat na nový model
 #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"
@@ -612,19 +632,17 @@ class ProblemView(generic.DetailView):
 		return context
 
 
-class VysledkyResitele(object):
-	"""Pro daného řešitele ukládá počet bodů za jednotlivé úlohy a celkový
-	počet bodů za konkrétní ročník do daného čísla a za dané číslo."""
+class RadekVysledkovky(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, resitel, cislo, rocnik):
+	def __init__(self, poradi, resitel, body_problemy_sezn, body_cislo, body_rocnik):
 		self.resitel = resitel
-		self.cislo = cislo
-		self.body_cislo = body_resitele_v_cisle(resitel, cislo)
-		self.body = []
-		self.rocnik = rocnik
-		self.body_rocnik = body_resitele_v_rocniku(resitel, rocnik, cislo)
-		self.body_celkem_odjakziva = resitel.vsechny_body()
-		self.poradi = 0
+		self.body_cislo = body_cislo
+		self.body_rocnik = body_rocnik
+#		TODO self.body_celkem_odjakziva = odjakziva
+		self.poradi = poradi
+		self.body_problemy_sezn = body_problemy_sezn
 
 class CisloView(generic.DetailView):
 	model = Cislo
@@ -648,48 +666,93 @@ class CisloView(generic.DetailView):
 	def get_context_data(self, **kwargs):
 		context = super(CisloView, self).get_context_data(**kwargs)
 
-		## TODO upravit dle nového modelu
 		cislo = context['cislo']
 		hlavni_problemy = hlavni_problemy_cisla(cislo)
+		# 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ů)
+		hlavni_problemy_slovnik = {}
+		for hp in hlavni_problemy:
+			hlavni_problemy_slovnik[str(hp.id)] = {}
 
 		## TODO dostat pro tyto problémy součet v daném čísle pro daného řešitele
 		## TODO možná chytřeji vybírat aktivní řešitele
-		## chceme letos něco poslal
+		# 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 = Resitel.objects.filter(
 				rok_maturity__gte=cislo.rocnik.druhy_rok())
 				# TODO: zkusit hodnoceni__rocnik...
 				#.filter(hodnoceni_set__rocnik__eq=cislo_rocnik)
-		radky_vysledkovky = []
+		# zakládání prázdných záznamů pro řešitele
+		cislobody = {}
 		for ar in aktivni_resitele:
-			# získáme výsledky řešitele - součty přes číslo a ročník
-			vr = VysledkyResitele(ar, cislo, cislo.rocnik)
+			# řešitele převedeme na řetězec pomocí unikátního id
+			cislobody[str(ar.id)] = ""
 			for hp in hlavni_problemy:
-				vr.body.append(
-				body_resitele_problemu_v_cisle(hp, ar, cislo))
-			radky_vysledkovky.append(vr)
-
-		# setřídíme řádky výsledkovky/objekty VysledkyResitele podle bodů
-		radky_vysledkovky.sort(key=lambda vr: vr.body_rocnik, reverse=True)
-
-		# generujeme sloupec s pořadím pomocí stejně zvané funkce
-		pocty_bodu = [rv.body_rocnik for rv in radky_vysledkovky]
-		sloupec_poradi = sloupec_s_poradim(pocty_bodu)
+				slovnik = hlavni_problemy_slovnik[str(hp.id)]
+				slovnik[str(ar.id)] = ""
 		
-		# každému řádku výsledkovky přidáme jeho pořadí
-		i = 0
-		for rv in radky_vysledkovky:
-			rv.poradi = sloupec_poradi[i]
-			i = i + 1
+		# vezmeme všechna řešení s body do daného čísla
+		reseni_do_cisla = Reseni.objects.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:
+			body = reseni.hodnoceni.body	
+			problem = reseni.problem
+			nadproblem = hlavni_problem(problem)
+			nadproblem_slovnik = hlavni_problemy_slovnik[str(nadproblem.id)]
+			for resitel in reseni.resitele:
+				# testujeme na None, pokud 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 cislobody[str(resitel.id)] == "":
+					cislobody[str(resitel.id)] = 0 
+				cislobody[str(resitel.id)] += body
+
+				if nadproblem_slovnik[str(resitel.id)] == "":
+					nadproblem_slovnik[str(resitel.id)] = 0  
+				nadproblem_slovnik[str(resitel.id)] += body 
+
+		# zeptáme se na dvojice (řešitel, body) za ročník a setřídíme sestupně
+		resitel_rocnikbody_slov = body_resitelu_za_rocnik(cislo.rocnik, aktivni_resitele)
+		resitel_rocnikbody_sezn = sorted(resitel_rocnikbody_slov.items(),
+						key = lambda x: x[1], reverse = True)
+		
+		# ř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]
+				
+		# vytvoříme jednotlivé sloupce výsledkovky
+		radky_vysledkovky = []
+		rocnik_body = []
+		cislo_body = []
+		hlavni_problemy_body = []
+		for ar_id in setrizeni_resitele_id:
+			# vytáhneme ze slovníků body pro daného řešitele
+			rocnik_body.append(resitel_rocnikbody_slov[ar_id])
+			cislo_body.append(cislobody[ar_id])
+			problemy = []
+			for hp in hlavni_problemy:
+				problemy.append(hlavni_problemy_slovnik[str(hp.id)][ar_id])
+			hlavni_problemy_body.append(problemy)
+		# pořadí určíme pomocí funkce, které dáme celkové body za ročník vzestupně
+		poradi = sloupec_s_poradim(rocnik_body)
+
+		radky_vysledkovky = []
+		for i in range(0, len(setrizeni_resitele_id)):
+			radek = RadekVysledkovky(poradi[i], setrizeni_resitele[i],
+				hlavni_problemy_body[i], cislo_body[i], rocnik_body[i])
+			radky_vysledkovky.append(radek)
 
 		# vytahané informace předáváme do kontextu
 		context['cislo'] = cislo
-		context['radky_vysledkovky'] = radky_vysledkovky	
+		context['radky_vysledkovky'] = radky_vysledkovky
 		context['problemy'] = hlavni_problemy
 #		context['v_cisle_zadane'] = TODO
 #		context['resene_problemy'] = resene_problemy
 		#XXX testovat
 		#XXX opravit to, že se nezobrazují body za jednotlivé úlohy
-
 		return context
 
 #		problemy = sorted(set(r.problem for r in reseni), key=lambda x:(poradi_typu[x.typ], x.kod_v_rocniku()))