Merge branch 'data_migrations' of gimli.ms.mff.cuni.cz:/akce/mam/git/mamweb into data_migrations
This commit is contained in:
		
						commit
						ad2a93117f
					
				
					 11 changed files with 226 additions and 57 deletions
				
			
		|  | @ -337,8 +337,8 @@ | ||||||
| 			"sort_order": 33, | 			"sort_order": 33, | ||||||
| 			"title": "Výsledková listina", | 			"title": "Výsledková listina", | ||||||
| 			"tree": 1, | 			"tree": 1, | ||||||
| 			"url": "zadani/vysledkova-listina/", | 			"url": "seminar_aktualni_vysledky", | ||||||
| 			"urlaspattern": false | 			"urlaspattern": true | ||||||
| 		}, | 		}, | ||||||
| 		"model": "sitetree.treeitem", | 		"model": "sitetree.treeitem", | ||||||
| 		"pk": 16 | 		"pk": 16 | ||||||
|  |  | ||||||
|  | @ -1 +0,0 @@ | ||||||
| ,anet,erebus,25.03.2020 22:21,file:///home/anet/.config/libreoffice/4; |  | ||||||
|  | @ -1,7 +1,8 @@ | ||||||
| from django.contrib import admin | from django.contrib import admin | ||||||
| from django.contrib.auth.models import Group | from django.contrib.auth.models import Group | ||||||
| from django.db import models | from django.db import models | ||||||
| from django.forms import widgets | from django.forms import widgets, ModelForm | ||||||
|  | from django.core.exceptions import ValidationError | ||||||
| 
 | 
 | ||||||
| from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter | from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter | ||||||
| from reversion.admin import VersionAdmin | from reversion.admin import VersionAdmin | ||||||
|  | @ -12,11 +13,43 @@ from solo.admin import SingletonModelAdmin | ||||||
| # Todo: reversion | # Todo: reversion | ||||||
| 
 | 
 | ||||||
| import seminar.models as m | import seminar.models as m | ||||||
|  | import seminar.treelib as tl | ||||||
| 
 | 
 | ||||||
| admin.site.register(m.Skola) | admin.site.register(m.Skola) | ||||||
| admin.site.register(m.Prijemce) | admin.site.register(m.Prijemce) | ||||||
| admin.site.register(m.Rocnik) | admin.site.register(m.Rocnik) | ||||||
| admin.site.register(m.Cislo) | 
 | ||||||
|  | class CisloForm(ModelForm): | ||||||
|  | 	class Meta: | ||||||
|  | 		model = m.Cislo | ||||||
|  | 		fields = '__all__' | ||||||
|  | 		 | ||||||
|  | 	def clean(self): | ||||||
|  | 		print("Cleaning...") | ||||||
|  | 		print(self.cleaned_data) | ||||||
|  | 		if self.cleaned_data.get('verejne_db') == False: | ||||||
|  | 			return self.cleaned_data | ||||||
|  | 		cn = m.CisloNode.objects.get(cislo=self.instance) | ||||||
|  | 		for ch in tl.all_children(cn): | ||||||
|  | 			if isinstance(ch, m.TemaVCisleNode): | ||||||
|  | 				if ch.tema.stav not in \ | ||||||
|  | 					(m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): | ||||||
|  | 						raise ValidationError('Téma %(tema)s není zadané ani vyřešené', params={'tema':ch.tema}) | ||||||
|  | 				 | ||||||
|  | 			if isinstance(ch, m.UlohaZadaniNode) or isinstance(ch, m.UlohaVzorakNode): | ||||||
|  | 				if ch.uloha.stav not in \ | ||||||
|  | 					(m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): | ||||||
|  | 					raise ValidationError('Úloha %(uloha)s není zadaná ani vyřešená', params={'uloha':ch.uloha}) | ||||||
|  | 			if isinstance(ch, m.ReseniNode): | ||||||
|  | 				for problem in ch.reseni.problem_set: | ||||||
|  | 					if problem not in \ | ||||||
|  | 						(m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): | ||||||
|  | 						raise ValidationError('Problém %s není zadaný ani vyřešený', code=problem) | ||||||
|  | 		return self.cleaned_data | ||||||
|  | 
 | ||||||
|  | @admin.register(m.Cislo) | ||||||
|  | class CisloAdmin(admin.ModelAdmin): | ||||||
|  | 	form = CisloForm | ||||||
| 
 | 
 | ||||||
| @admin.register(m.Osoba) | @admin.register(m.Osoba) | ||||||
| class OsobaAdmin(admin.ModelAdmin): | class OsobaAdmin(admin.ModelAdmin): | ||||||
|  |  | ||||||
|  | @ -316,3 +316,85 @@ class JednoHodnoceniForm(forms.ModelForm): | ||||||
| OhodnoceniReseniFormSet = formset_factory(JednoHodnoceniForm, | OhodnoceniReseniFormSet = formset_factory(JednoHodnoceniForm, | ||||||
| 		extra = 0, | 		extra = 0, | ||||||
| 		) | 		) | ||||||
|  | 
 | ||||||
|  | # FIXME: Ideálně by mělo být součástí třídy níž, ale neumím to udělat | ||||||
|  | DATE_FORMAT = '%Y-%m-%d' | ||||||
|  | 
 | ||||||
|  | class OdevzdavatkoTabulkaFiltrForm(forms.Form): | ||||||
|  | 	"""Form pro filtrování přehledové odevzdávátkové tabulky | ||||||
|  | 
 | ||||||
|  | 	Inspirováno https://kam.mff.cuni.cz/mffzoom/""" | ||||||
|  | 
 | ||||||
|  | 	# Věci definované níž se importují i ve views pro odevzdávátko (Inspirováno https://docs.djangoproject.com/en/3.1/ref/models/fields/#field-choices) | ||||||
|  | 
 | ||||||
|  | 	RESITELE_RELEVANTNI = 'relevantni' | ||||||
|  | 	RESITELE_LETOSNI = 'letosni' | ||||||
|  | 	RESITELE_CHOICES = [ | ||||||
|  | 		(RESITELE_RELEVANTNI, 'Relevantní řešitelé'), # I.e. nezobrazovat prázdné řádky tabulky | ||||||
|  | 		(RESITELE_LETOSNI, 'Všichni letošní'), | ||||||
|  | 		# Možná: všechny vč. historických? | ||||||
|  | 		] | ||||||
|  | 
 | ||||||
|  | 	PROBLEMY_MOJE = 'moje' | ||||||
|  | 	PROBLEMY_LETOSNI = 'letosni' | ||||||
|  | 	PROBLEMY_CHOICES = [ | ||||||
|  | 		(PROBLEMY_MOJE, 'Moje problémy'), # Letošní problémy, které mají v sobě nebo v nadproblémech přiřazeného daného orga | ||||||
|  | 		(PROBLEMY_LETOSNI, 'Všechny letošní'), | ||||||
|  | 		# TODO: *hlavní problémy, možná všechny... | ||||||
|  | 		# XXX: Chtělo by to i "aktuálně zadané... | ||||||
|  | 		] | ||||||
|  | 
 | ||||||
|  | 	# TODO: Typy problémů (problémy, úlohy, ostatní, všechny)? Jen některá řešení (obodovaná/neobodovaná, víc řešitelů, ...)? | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	def gen_terminy(): | ||||||
|  | 		import datetime | ||||||
|  | 		from time import strftime | ||||||
|  | 		 | ||||||
|  | 		from django.db.utils import OperationalError | ||||||
|  | 		try: | ||||||
|  | 			aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik | ||||||
|  | 			aktualni_cislo = m.Nastaveni.get_solo().aktualni_cislo | ||||||
|  | 		except OperationalError: | ||||||
|  | 			# django.db.utils.OperationalError: no such table: seminar_nastaveni | ||||||
|  | 			# Nemáme databázi, takže to selhalo. Pro jistotu vrátíme aspoň dvě možnosti, ať to nepadá dál | ||||||
|  | 			logger = logging.getLogger(__name__) | ||||||
|  | 			logger.error("Rozbitá databáze (před počátečními migracemi?)") | ||||||
|  | 			return [('broken', 'Je to rozbitý'), ('fubar', 'Nefunguje to')] | ||||||
|  | 
 | ||||||
|  | 		result = [] | ||||||
|  | 
 | ||||||
|  | 		for cislo in m.Cislo.objects.filter( | ||||||
|  | 				rocnik=aktualni_rocnik, | ||||||
|  | 				poradi__lte=aktualni_cislo.poradi, | ||||||
|  | 				).reverse():	# Standardně se řadí od nejnovějšího čísla | ||||||
|  | 			# Předem je mi líto kohokoliv, kdo tyhle řádky bude číst... | ||||||
|  | 			if cislo.datum_vydani is not None and cislo.datum_vydani <= datetime.date.today(): | ||||||
|  | 				result.append(( | ||||||
|  | 					strftime(DATE_FORMAT, cislo.datum_vydani.timetuple()), | ||||||
|  | 					f"Vydání {cislo.poradi}. čísla")) | ||||||
|  | 			if cislo.datum_preddeadline is not None and cislo.datum_preddeadline <= datetime.date.today(): | ||||||
|  | 				result.append(( | ||||||
|  | 					strftime(DATE_FORMAT, cislo.datum_preddeadline.timetuple()), | ||||||
|  | 					f"Předdeadline {cislo.poradi}. čísla")) | ||||||
|  | 			if cislo.datum_deadline_soustredeni is not None and cislo.datum_deadline_soustredeni <= datetime.date.today(): | ||||||
|  | 				result.append(( | ||||||
|  | 					strftime(DATE_FORMAT, cislo.datum_deadline_soustredeni.timetuple()), | ||||||
|  | 					f"Sous. deadline {cislo.poradi}. čísla")) | ||||||
|  | 			if cislo.datum_deadline is not None and cislo.datum_deadline <= datetime.date.today(): | ||||||
|  | 				result.append(( | ||||||
|  | 					strftime(DATE_FORMAT, cislo.datum_deadline.timetuple()), | ||||||
|  | 					f"Finální deadline {cislo.poradi}. čísla")) | ||||||
|  | 		result.append(( | ||||||
|  | 			strftime(DATE_FORMAT, datetime.date.today().timetuple()), f"Dnes")) | ||||||
|  | 
 | ||||||
|  | 		return result | ||||||
|  | 
 | ||||||
|  | 	# NOTE: Initial definuji pro jednotlivé fieldy, aby to bylo tady a nebylo potřeba to řešit ve views... | ||||||
|  | 	resitele = forms.ChoiceField(choices=RESITELE_CHOICES, initial=RESITELE_RELEVANTNI) | ||||||
|  | 	problemy = forms.ChoiceField(choices=PROBLEMY_CHOICES, initial=PROBLEMY_MOJE) | ||||||
|  | 	 | ||||||
|  | 	# choices jako parametr Select widgetu neumí brát callable, jen iterable, takže si pro jednoduchost můžu rovnou uložit výsledek sem... | ||||||
|  | 	terminy = gen_terminy() | ||||||
|  | 	reseni_od = forms.DateField(input_formats=[DATE_FORMAT], widget=forms.Select(choices=terminy), initial=terminy[-2]) | ||||||
|  | 	reseni_do = forms.DateField(input_formats=[DATE_FORMAT], widget=forms.Select(choices=terminy), initial=terminy[-1]) | ||||||
|  |  | ||||||
|  | @ -864,31 +864,31 @@ class Problem(SeminarModelBase,PolymorphicModel): | ||||||
| 			return str(self.kod) | 			return str(self.kod) | ||||||
| 		return '<Není zadaný>' | 		return '<Není zadaný>' | ||||||
| 
 | 
 | ||||||
| 	def verejne(self): | #	def verejne(self): | ||||||
| 		# aktuálně podle stavu problému | #		# aktuálně podle stavu problému | ||||||
| 		# FIXME pro některé problémy možná chceme override | #		# FIXME pro některé problémy možná chceme override | ||||||
| 		# FIXME vrací veřejnost čistě problému, nezávisle na čísle, ve kterém je.  | #		# FIXME vrací veřejnost čistě problému, nezávisle na čísle, ve kterém je.  | ||||||
| 		# Je to tak správně? Podle aktuální představy ano. | #		# Je to tak správně? Podle aktuální představy ano. | ||||||
| 		stav_verejny = False | #		stav_verejny = False | ||||||
| 		if self.stav == 'zadany' or self.stav == 'vyreseny': | #		if self.stav == 'zadany' or self.stav == 'vyreseny': | ||||||
| 			stav_verejny = True | #			stav_verejny = True | ||||||
| 			print("stav_verejny: {}".format(stav_verejny))		 | #			print("stav_verejny: {}".format(stav_verejny))		 | ||||||
| 
 | # | ||||||
| 		cislo_verejne = False | #		cislo_verejne = False | ||||||
| 		cislonode = self.cislo_node() | #		cislonode = self.cislo_node() | ||||||
| 		if cislonode is None: | #		if cislonode is None: | ||||||
| 			# problém nemá vlastní node, veřejnost posuzujeme jen podle stavu | #			# problém nemá vlastní node, veřejnost posuzujeme jen podle stavu | ||||||
| 			print("empty node")		 | #			print("empty node")		 | ||||||
| 			return stav_verejny | #			return stav_verejny | ||||||
| 		else:	 | #		else:	 | ||||||
| 			cislo_zadani = cislonode.cislo | #			cislo_zadani = cislonode.cislo | ||||||
| 			if (cislo_zadani and cislo_zadani.verejne()): | #			if (cislo_zadani and cislo_zadani.verejne()): | ||||||
| 				print("cislo: {}".format(cislo_zadani)) | #				print("cislo: {}".format(cislo_zadani)) | ||||||
| 				cislo_verejne = True | #				cislo_verejne = True | ||||||
| 			print("stav_verejny: {}".format(stav_verejny))		 | #			print("stav_verejny: {}".format(stav_verejny))		 | ||||||
| 			print("cislo_verejne: {}".format(cislo_verejne))		 | #			print("cislo_verejne: {}".format(cislo_verejne))		 | ||||||
| 			return (stav_verejny and cislo_verejne) | #			return (stav_verejny and cislo_verejne) | ||||||
| 	verejne.boolean = True | #	verejne.boolean = True | ||||||
| 
 | 
 | ||||||
| 	def verejne_url(self): | 	def verejne_url(self): | ||||||
| 		return reverse('seminar_problem', kwargs={'pk': self.id}) | 		return reverse('seminar_problem', kwargs={'pk': self.id}) | ||||||
|  | @ -962,6 +962,7 @@ class Clanek(Problem): | ||||||
| 	cislo = models.ForeignKey(Cislo, blank=True, null=True, on_delete=models.PROTECT, | 	cislo = models.ForeignKey(Cislo, blank=True, null=True, on_delete=models.PROTECT, | ||||||
| 		verbose_name='číslo vydání', related_name='vydane_clanky') | 		verbose_name='číslo vydání', related_name='vydane_clanky') | ||||||
| 
 | 
 | ||||||
|  | 	@cached_property | ||||||
| 	def kod_v_rocniku(self): | 	def kod_v_rocniku(self): | ||||||
| 		if self.stav == 'zadany': | 		if self.stav == 'zadany': | ||||||
| # Nemělo by být potřeba | # Nemělo by být potřeba | ||||||
|  |  | ||||||
|  | @ -4,6 +4,14 @@ | ||||||
| 
 | 
 | ||||||
| {% block content %} | {% block content %} | ||||||
| 
 | 
 | ||||||
|  | <form method=get action=.> | ||||||
|  | {{ filtr.resitele }} | ||||||
|  | {{ filtr.problemy }} | ||||||
|  | Od: {{ filtr.reseni_od }} | ||||||
|  | Do: {{ filtr.reseni_do }} | ||||||
|  | <input type=submit value="→"> | ||||||
|  | </form> | ||||||
|  | 
 | ||||||
| <table> | <table> | ||||||
| 	<tr> | 	<tr> | ||||||
| 		<td></td> {# Prázdná buňka v levém horním rohu #} | 		<td></td> {# Prázdná buňka v levém horním rohu #} | ||||||
|  |  | ||||||
|  | @ -24,7 +24,9 @@ | ||||||
|     <div class='mam-org-only'> |     <div class='mam-org-only'> | ||||||
|     <h1>Výsledky včetně neveřejných</h1> |     <h1>Výsledky včetně neveřejných</h1> | ||||||
|     {% with vysledkovka_s_neverejnymi as radky_vysledkovky %} |     {% with vysledkovka_s_neverejnymi as radky_vysledkovky %} | ||||||
|  |         {% with cisla_s_neverejnymi as cisla %} | ||||||
|       {% include "seminar/vysledkovka_rocnik.html" %} |       {% include "seminar/vysledkovka_rocnik.html" %} | ||||||
|  |         {% endwith %} | ||||||
|     {% endwith %} |     {% endwith %} | ||||||
|     </div> |     </div> | ||||||
|   {% endif %} |   {% endif %} | ||||||
|  |  | ||||||
|  | @ -230,9 +230,9 @@ def cisla_rocniku(rocnik, jen_verejne=True): | ||||||
| 		seznam objektů typu Cislo | 		seznam objektů typu Cislo | ||||||
| 	"""	 | 	"""	 | ||||||
| 	if jen_verejne: | 	if jen_verejne: | ||||||
| 		return rocnik.verejna_cisla() | 		return rocnik.verejne_vysledkovky_cisla() | ||||||
| 	else: | 	else: | ||||||
| 		return rocnik.cisla.all() | 		return rocnik.cisla.all().order_by('poradi') | ||||||
| 
 | 
 | ||||||
| def hlavni_problem(problem): | def hlavni_problem(problem): | ||||||
| 	""" Pro daný problém vrátí jeho nejvyšší nadproblém.""" | 	""" Pro daný problém vrátí jeho nejvyšší nadproblém.""" | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ import logging | ||||||
| 
 | 
 | ||||||
| import seminar.models as m | import seminar.models as m | ||||||
| import seminar.forms as f | import seminar.forms as f | ||||||
|  | from seminar.forms import OdevzdavatkoTabulkaFiltrForm as FiltrForm | ||||||
| from seminar.utils import aktivniResitele, resi_v_rocniku | from seminar.utils import aktivniResitele, resi_v_rocniku | ||||||
| 
 | 
 | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
|  | @ -41,28 +42,55 @@ class TabulkaOdevzdanychReseniView(ListView): | ||||||
| 
 | 
 | ||||||
| 	def inicializuj_osy_tabulky(self): | 	def inicializuj_osy_tabulky(self): | ||||||
| 		"""Vyrobí prvotní querysety pro sloupce a řádky, tj. seznam všech řešitelů a problémů""" | 		"""Vyrobí prvotní querysety pro sloupce a řádky, tj. seznam všech řešitelů a problémů""" | ||||||
|  | 		# FIXME: jméno metody není vypovídající... | ||||||
| 		# NOTE: Tenhle blok nemůže být přímo ve třídě, protože před vyrobením databáze neexistují ty objekty (?). TODO: Otestovat | 		# NOTE: Tenhle blok nemůže být přímo ve třídě, protože před vyrobením databáze neexistují ty objekty (?). TODO: Otestovat | ||||||
| 		# TODO: Prefetches, Select related, ... | 		# TODO: Prefetches, Select related, ... | ||||||
| 		self.resitele = m.Resitel.objects.all() | 		self.resitele = m.Resitel.objects.all() | ||||||
| 		self.problemy = m.Problem.objects.all() | 		self.problemy = m.Problem.objects.all() | ||||||
|  | 		self.reseni = m.Reseni.objects.all() | ||||||
|  | 
 | ||||||
|  | 		form = FiltrForm(self.request.GET) | ||||||
|  | 		if form.is_valid(): | ||||||
|  | 			fcd = form.cleaned_data | ||||||
|  | 			resitele = fcd["resitele"] | ||||||
|  | 			problemy = fcd["problemy"] | ||||||
|  | 			reseni_od = fcd["reseni_od"] | ||||||
|  | 			reseni_do = fcd["reseni_do"] | ||||||
|  | 		else: | ||||||
|  | 			resitele = FiltrForm.get_initial_for_field(FormFiltr.resitele, "resitele") | ||||||
|  | 			problemy = FiltrForm.get_initial_for_field(FormFiltr.problemy, "problemy") | ||||||
|  | 			resitele_od = FiltrForm.get_initial_for_field(FormFiltr.resitele_od, "resitele_od") | ||||||
|  | 			resitele_do = FiltrForm.get_initial_for_field(FormFiltr.resitele_do, "resitele_do") | ||||||
|  | 			 | ||||||
|  | 
 | ||||||
|  | 		# Filtrujeme! | ||||||
|  | 		aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik	# .get_solo() vrátí tu jedinou instanci | ||||||
|  | 		if resitele == FiltrForm.RESITELE_RELEVANTNI: | ||||||
|  | 			logger.warning("Někdo chtěl v tabulce jen relevantní řešitele a měl smůlu :-(") | ||||||
|  | 			resitele = FiltrForm.RESITELE_LETOSNI # Fall-through | ||||||
|  | 		elif resitele == FiltrForm.RESITELE_LETOSNI: | ||||||
|  | 			self.resitele = resi_v_rocniku(aktualni_rocnik) | ||||||
|  | 
 | ||||||
|  | 		if problemy == FiltrForm.PROBLEMY_MOJE: | ||||||
|  | 			org = m.Organizator.objects.get(osoba__user=self.request.user) | ||||||
|  | 			from django.db.models import Q | ||||||
|  | 			self.problemy = self.problemy.filter(Q(autor=org)|Q(garant=org)|Q(opravovatele=org), stav=m.Problem.STAV_ZADANY) | ||||||
|  | 		elif problemy == FiltrForm.PROBLEMY_LETOSNI: | ||||||
|  | 			self.problemy = self.problemy.filter(stav=m.Problem.STAV_ZADANY) | ||||||
|  | 			#self.problemy = list(filter(lambda problem: problem.rocnik() == aktualni_rocnik, self.problemy)) # DB HOG? # FIXME: některé problémy nemají ročník.... | ||||||
|  | 		# NOTE: Protože řešení odkazuje přímo na Problém a QuerySet na Hodnocení je nepolymorfní, musíme porovnávat taky s nepolymorfními Problémy. | ||||||
|  | 		self.problemy = self.problemy.non_polymorphic() | ||||||
|  | 
 | ||||||
|  | 		self.reseni = self.reseni.filter(cas_doruceni__date__gte=reseni_od, cas_doruceni__date__lte=reseni_do) | ||||||
| 
 | 
 | ||||||
| 	def get_queryset(self): | 	def get_queryset(self): | ||||||
| 		self.inicializuj_osy_tabulky() | 		self.inicializuj_osy_tabulky() | ||||||
| 		self.akt_rocnik = m.Nastaveni.get_solo().aktualni_rocnik	# .get_solo() vrátí tu jedinou instanci, asi... |  | ||||||
| 		self.resitele = resi_v_rocniku(self.akt_rocnik) |  | ||||||
| 		# NOTE: Protože řešení odkazuje přímo na Problém a QuerySet na Hodnocení je nepolymorfní, musíme porovnávat taky s nepolymorfními Problémy. |  | ||||||
| 		self.problemy = m.Problem.objects.filter(stav=m.Problem.STAV_ZADANY).non_polymorphic() |  | ||||||
| 
 |  | ||||||
| 		qs = super().get_queryset() | 		qs = super().get_queryset() | ||||||
| 		qs = qs.filter(problem__in=self.problemy).select_related('reseni', 'problem').prefetch_related('reseni__resitele__osoba') | 		qs = qs.filter(problem__in=self.problemy, reseni__in=self.reseni, reseni__resitele__in=self.resitele).select_related('reseni', 'problem').prefetch_related('reseni__resitele__osoba') | ||||||
| 		return qs | 		return qs | ||||||
| 
 | 
 | ||||||
| 	def get_context_data(self, *args, **kwargs): | 	def get_context_data(self, *args, **kwargs): | ||||||
| 		# FIXME: Tenhle blok nemůže být přímo ve třídě, protože před vyrobením databáze neexistuje Nastavení. | 		# self.resitele, self.reseni a self.problemy jsou již nastavené | ||||||
| 		self.akt_rocnik = m.Nastaveni.get_solo().aktualni_rocnik	# .get_solo() vrátí tu jedinou instanci, asi... |  | ||||||
| 		self.resitele = resi_v_rocniku(self.akt_rocnik) |  | ||||||
| 		# NOTE: Protože řešení odkazuje přímo na Problém a QuerySet na Hodnocení je nepolymorfní, musíme porovnávat taky s nepolymorfními Problémy. |  | ||||||
| 		self.problemy = m.Problem.objects.filter(stav=m.Problem.STAV_ZADANY).non_polymorphic() |  | ||||||
| 
 | 
 | ||||||
| 		ctx = super().get_context_data(*args, **kwargs) | 		ctx = super().get_context_data(*args, **kwargs) | ||||||
| 		ctx['problemy'] = self.problemy | 		ctx['problemy'] = self.problemy | ||||||
|  | @ -100,6 +128,10 @@ class TabulkaOdevzdanychReseniView(ListView): | ||||||
| 			hodnoty.append(resiteluv_radek) | 			hodnoty.append(resiteluv_radek) | ||||||
| 		ctx['radky'] = list(zip(self.resitele, hodnoty)) | 		ctx['radky'] = list(zip(self.resitele, hodnoty)) | ||||||
| 
 | 
 | ||||||
|  | 		ctx['filtr'] = FiltrForm(initial=self.request.GET) | ||||||
|  | 		# Pro použití hacku na automatické {{form.media}} v template: | ||||||
|  | 		ctx['form'] = ctx['filtr'] | ||||||
|  | 
 | ||||||
| 		return ctx | 		return ctx | ||||||
| 
 | 
 | ||||||
| # Velmi silně inspirováno zdrojáky, FIXME: Nedá se to udělat smysluplněji? | # Velmi silně inspirováno zdrojáky, FIXME: Nedá se to udělat smysluplněji? | ||||||
|  |  | ||||||
|  | @ -29,7 +29,7 @@ from seminar.forms import PrihlaskaForm, LoginForm, ProfileEditForm | ||||||
| import seminar.forms as f | import seminar.forms as f | ||||||
| import seminar.templatetags.treenodes as tnltt | import seminar.templatetags.treenodes as tnltt | ||||||
| import seminar.views.views_rest as vr | import seminar.views.views_rest as vr | ||||||
| from seminar.views.vysledkovka import vysledkovka_rocniku, vysledkovka_cisla  | from seminar.views.vysledkovka import vysledkovka_rocniku, vysledkovka_cisla, body_resitelu | ||||||
| 
 | 
 | ||||||
| from datetime import timedelta, date, datetime, MAXYEAR | from datetime import timedelta, date, datetime, MAXYEAR | ||||||
| from django.utils import timezone | from django.utils import timezone | ||||||
|  | @ -185,7 +185,7 @@ class TNLData(object): | ||||||
| 			return [cls.from_treenode(treenode)] | 			return [cls.from_treenode(treenode)] | ||||||
| 		else: | 		else: | ||||||
| 			found = [] | 			found = [] | ||||||
| 			for tn in all_children(treenode): | 			for tn in treelib.all_children(treenode): | ||||||
| 				result = cls.filter_treenode(tn, predicate) | 				result = cls.filter_treenode(tn, predicate) | ||||||
| 				# Result by v tuhle chvíli měl být seznam TNLDat odpovídající treenodům, jež matchnuly predikát. | 				# Result by v tuhle chvíli měl být seznam TNLDat odpovídající treenodům, jež matchnuly predikát. | ||||||
| 				for tnl in result: | 				for tnl in result: | ||||||
|  | @ -503,6 +503,7 @@ def ZadaniAktualniVysledkovkaView(request): | ||||||
| 			pass | 			pass | ||||||
| 	# vysledkovka s neverejnyma vysledkama | 	# vysledkovka s neverejnyma vysledkama | ||||||
| 	vysledkovka_s_neverejnymi = vysledkovka_rocniku(nastaveni.aktualni_rocnik, jen_verejne=False) | 	vysledkovka_s_neverejnymi = vysledkovka_rocniku(nastaveni.aktualni_rocnik, jen_verejne=False) | ||||||
|  | 	cisla_s_neverejnymi = cisla_rocniku(nastaveni.aktualni_rocnik, jen_verejne=False) | ||||||
| 	return render( | 	return render( | ||||||
| 		request, | 		request, | ||||||
| 		'seminar/zadani/AktualniVysledkovka.html', | 		'seminar/zadani/AktualniVysledkovka.html', | ||||||
|  | @ -511,6 +512,7 @@ def ZadaniAktualniVysledkovkaView(request): | ||||||
| 			'radky_vysledkovky': vysledkovka, | 			'radky_vysledkovky': vysledkovka, | ||||||
| 			'cisla': cisla, | 			'cisla': cisla, | ||||||
| 			'vysledkovka_s_neverejnymi': vysledkovka_s_neverejnymi, | 			'vysledkovka_s_neverejnymi': vysledkovka_s_neverejnymi, | ||||||
|  | 			'cisla_s_neverejnymi': cisla_s_neverejnymi, | ||||||
| 		} | 		} | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -48,7 +48,7 @@ def sloupec_s_poradim(setrizene_body): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def body_resitelu(resitele, za, odjakziva=True): | def body_resitelu(resitele, za, odjakziva=True, jen_verejne=False): | ||||||
| 	""" Funkce počítající počty bodů pro zadané řešitele,  | 	""" 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. | 	buď odjakživa do daného ročníku/čísla anebo za daný ročník/číslo. | ||||||
| 	Parametry: | 	Parametry: | ||||||
|  | @ -95,11 +95,21 @@ def body_resitelu(resitele, za, odjakziva=True): | ||||||
| 			filter=( Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok=rok, | 			filter=( Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok=rok, | ||||||
| 					reseni__hodnoceni__cislo_body__poradi__lte=cislo.poradi) )) | 					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ě. | 	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',  | 		if jen_verejne: | ||||||
| 			filter= Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lte=rok)) | 			body_k_zapocteni = Sum('reseni__hodnoceni__body', | ||||||
|  | 									filter= Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lte=rok, | ||||||
|  | 											reseni__hodnoceni__cislo_body__verejna_vysledkovka=True)) | ||||||
|  | 		else: | ||||||
|  | 			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. | 	elif rocnik and not odjakziva: # Spočítáme body za daný ročník. | ||||||
| 		body_k_zapocteni = Sum('reseni__hodnoceni__body', | 		if jen_verejne: | ||||||
| 			filter= Q(reseni__hodnoceni__cislo_body__rocnik=rocnik)) | 			body_k_zapocteni = Sum('reseni__hodnoceni__body', | ||||||
|  | 								   filter=Q(reseni__hodnoceni__cislo_body__rocnik=rocnik, | ||||||
|  | 											reseni__hodnoceni__cislo_body__verejna_vysledkovka=True)) | ||||||
|  | 		else: | ||||||
|  | 			body_k_zapocteni = Sum('reseni__hodnoceni__body', | ||||||
|  | 								   filter=Q(reseni__hodnoceni__cislo_body__rocnik=rocnik)) | ||||||
| 	else:  | 	else:  | ||||||
| 		assert True, "body_resitelu: Neplatná kombinace za a odjakživa." | 		assert True, "body_resitelu: Neplatná kombinace za a odjakživa." | ||||||
| 
 | 
 | ||||||
|  | @ -149,14 +159,14 @@ def vysledkovka_rocniku(rocnik, jen_verejne=True): | ||||||
| 		body_cisla_slov[cislo.id] = cislobody | 		body_cisla_slov[cislo.id] = cislobody | ||||||
| 
 | 
 | ||||||
| 	# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně | 	# 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) | 	resitel_rocnikbody_sezn = secti_body_za_rocnik(rocnik, aktivni_resitele, jen_verejne=jen_verejne) | ||||||
| 
 | 
 | ||||||
| 	# setřídíme řešitele podle počtu bodů a získáme seznam s body od nejvyšších po nenižší | 	# setřídíme řešitele podle počtu bodů a získáme seznam s body od nejvyšších po nenižší | ||||||
| 	setrizeni_resitele_id, setrizene_body = setrid_resitele_a_body(resitel_rocnikbody_sezn) | 	setrizeni_resitele_id, setrizene_body = setrid_resitele_a_body(resitel_rocnikbody_sezn) | ||||||
| 	poradi = sloupec_s_poradim(setrizene_body) | 	poradi = sloupec_s_poradim(setrizene_body) | ||||||
| 
 | 
 | ||||||
| 	# získáme body odjakživa | 	# získáme body odjakživa | ||||||
| 	resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, rocnik) | 	resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, rocnik, jen_verejne=jen_verejne) | ||||||
| 
 | 
 | ||||||
| 	# vytvoříme jednotlivé sloupce výsledkovky | 	# vytvoříme jednotlivé sloupce výsledkovky | ||||||
| 	radky_vysledkovky = [] | 	radky_vysledkovky = [] | ||||||
|  | @ -216,7 +226,7 @@ def pricti_body(slovnik, resitel, body): | ||||||
| 	 | 	 | ||||||
| 	slovnik[resitel.id] += body | 	slovnik[resitel.id] += body | ||||||
| 
 | 
 | ||||||
| def secti_body_za_rocnik(za, aktivni_resitele): | def secti_body_za_rocnik(za, aktivni_resitele, jen_verejne): | ||||||
| 	""" Spočítá body za ročník (celý nebo do daného čísla),  | 	""" Spočítá body za ročník (celý nebo do daného čísla),  | ||||||
| 		setřídí je sestupně a vrátí jako seznam. | 		setřídí je sestupně a vrátí jako seznam. | ||||||
| 	Parametry: | 	Parametry: | ||||||
|  | @ -224,7 +234,7 @@ def secti_body_za_rocnik(za, aktivni_resitele): | ||||||
| 						daného čísla | 						daného čísla | ||||||
| 	""" | 	""" | ||||||
| 	# spočítáme všem řešitelům jejich body za ročník (False => ne odjakživa) | 	# 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) | 	resitel_rocnikbody_slov = body_resitelu(aktivni_resitele, za, False, jen_verejne=jen_verejne) | ||||||
| 	# zeptáme se na dvojice (řešitel, body) za ročník a setřídíme sestupně | 	# 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(), | 	resitel_rocnikbody_sezn = sorted(resitel_rocnikbody_slov.items(), | ||||||
| 					key = lambda x: x[1], reverse = True) | 					key = lambda x: x[1], reverse = True) | ||||||
|  | @ -380,10 +390,10 @@ def vysledkovka_cisla(cislo, context=None): | ||||||
| 	hlavni_problemy_slovnik, cislobody = secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy) | 	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ě | 	# 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) | 	resitel_rocnikbody_sezn = secti_body_za_rocnik(cislo, aktivni_resitele, jen_verejne=True) | ||||||
| 
 | 
 | ||||||
| 	# získáme body odjakživa | 	# získáme body odjakživa | ||||||
| 	resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, cislo) | 	resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, cislo, jen_verejne=True) | ||||||
| 
 | 
 | ||||||
| 	# řešitelé setřídění podle bodů za číslo sestupně | 	# řešitelé setřídění podle bodů za číslo sestupně | ||||||
| 	setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn] | 	setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn] | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Kateřina Č
						Kateřina Č