Merge remote-tracking branch 'refs/remotes/Gimli/data_migrations' into data_migrations
This commit is contained in:
		
						commit
						0635006193
					
				
					 3 changed files with 92 additions and 77 deletions
				
			
		|  | @ -9,6 +9,7 @@ from .models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni | |||
| #from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva | ||||
| from .ovvpfile import OvvpFile | ||||
| from seminar import views | ||||
| from seminar.utils import aktivniResitele | ||||
| 
 | ||||
| class ExportIndexView(generic.View): | ||||
| 	def get(self, request): | ||||
|  | @ -76,7 +77,7 @@ class ExportRocnikView(generic.View): | |||
| 
 | ||||
| 		rocnik = get_object_or_404(Rocnik, prvni_rok=pr, exportovat=True) | ||||
| 		cislo = rocnik.posledni_zverejnena_vysledkovka_cislo() | ||||
| 		resitele = views.aktivniResitele(cislo.rocnik.rocnik, cislo.poradi, True) | ||||
| 		resitele = aktivniResitele(cislo, True) | ||||
| 		slovnik_body = views.secti_body_za_rocnik(cislo, resitele) | ||||
| 		_, setrizeni_resitele, setrizene_body = views.setrid_resitele_a_body(slovnik_body) | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,22 +2,26 @@ | |||
| 
 | ||||
| import datetime | ||||
| from django.contrib.auth.decorators import user_passes_test | ||||
| from html.parser import HTMLParser  | ||||
| from html.parser import HTMLParser | ||||
| from django.contrib.contenttypes.models import ContentType | ||||
| from django.core.exceptions import ObjectDoesNotExist | ||||
| 
 | ||||
| import seminar.models as m | ||||
| import seminar.treelib as t | ||||
| 
 | ||||
| staff_member_required = user_passes_test(lambda u: u.is_staff) | ||||
| 
 | ||||
| 
 | ||||
| class FirstTagParser(HTMLParser): | ||||
| 	def __init__(self, *args, **kwargs): | ||||
| 		self.firstTag = None | ||||
| 		super().__init__(*args, **kwargs) | ||||
| 
 | ||||
| 	def handle_data(self, data): | ||||
| 		if self.firstTag == None: | ||||
| 			self.firstTag = data | ||||
| 	 | ||||
| 
 | ||||
| 
 | ||||
| def histogram(seznam): | ||||
| 	d = {} | ||||
| 	for i in seznam: | ||||
|  | @ -26,9 +30,10 @@ def histogram(seznam): | |||
| 		d[i] += 1 | ||||
| 	return d | ||||
| 
 | ||||
| # Pozor: zarovnáno velmi netradičně pro přehlednost | ||||
| roman_numerals = zip((1000, 900, 500, 400,  100, 90,   50,  40,   10,  9,    5,   4,    1), | ||||
|                      ('M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I')) | ||||
| 
 | ||||
| roman_numerals = zip((1000, 900,  500, 400, 100,  90, 50,  40, 10,  9,   5,  4,   1), | ||||
| 					 ('M',  'CM', 'D', 'CD','C', 'XC','L','XL','X','IX','V','IV','I')) | ||||
| 
 | ||||
| def roman(num): | ||||
| 	res = "" | ||||
|  | @ -37,6 +42,7 @@ def roman(num): | |||
| 		num %= i | ||||
| 	return res | ||||
| 
 | ||||
| 
 | ||||
| def from_roman(rom): | ||||
| 	if not rom: | ||||
| 		return 0 | ||||
|  | @ -60,9 +66,9 @@ def seznam_problemu(): | |||
| 				except: | ||||
| 					url = None | ||||
| 				if url: | ||||
| 					s += '<a href="%s">%s</a>, ' % (url, o.pk, ) | ||||
| 					s += '<a href="%s">%s</a>, ' % (url, o.pk,) | ||||
| 				else: | ||||
| 					s += '%s, ' % (o.pk, ) | ||||
| 					s += '%s, ' % (o.pk,) | ||||
| 			s = s[:-2] + ']' | ||||
| 		problemy.append(s) | ||||
| 
 | ||||
|  | @ -75,7 +81,7 @@ def seznam_problemu(): | |||
| 		jmena[j].append(r) | ||||
| 	for j in jmena: | ||||
| 		if len(jmena[j]) > 1: | ||||
| 			prb(m.Resitel, 'Duplicitní jméno "%s"' % (j, ), jmena[j]) | ||||
| 			prb(m.Resitel, 'Duplicitní jméno "%s"' % (j,), jmena[j]) | ||||
| 
 | ||||
| 	# Data maturity a narození | ||||
| 	for r in m.Resitel.objects.all(): | ||||
|  | @ -83,13 +89,14 @@ def seznam_problemu(): | |||
| 			prb(m.Resitel, 'Neznámý rok maturity', [r]) | ||||
| 		if r.rok_maturity and (r.rok_maturity < 1990 or r.rok_maturity > datetime.date.today().year + 10): | ||||
| 			prb(m.Resitel, 'Podezřelé datum maturity', [r]) | ||||
| 		if r.osoba.datum_narozeni and (r.osoba.datum_narozeni.year < 1970 or r.osoba.datum_narozeni.year > datetime.date.today().year - 12): | ||||
| 		if r.osoba.datum_narozeni and ( | ||||
| 				r.osoba.datum_narozeni.year < 1970 or r.osoba.datum_narozeni.year > datetime.date.today().year - 12): | ||||
| 			prb(m.Resitel, 'Podezřelé datum narození', [r]) | ||||
| #        if not r.email: | ||||
| #            prb(Resitel, u'Neznámý email', [r]) | ||||
| #		if not r.email: | ||||
| #			prb(Resitel, u'Neznámý email', [r]) | ||||
| 
 | ||||
| 	## Kontroly konzistence databáze a TreeNodů | ||||
| 	 | ||||
| 
 | ||||
| 	# Články | ||||
| 	for clanek in m.Clanek.objects.all(): | ||||
| 		# získáme řešení svázané se článkem a z něj node ve stromě | ||||
|  | @ -97,7 +104,7 @@ def seznam_problemu(): | |||
| 		if (reseni.count() != 1): | ||||
| 			raise ValueError("Článek k sobě má nejedno řešení!") | ||||
| 		r = reseni.first() | ||||
| 		clanek_node = r.text_cely # vazba na ReseniNode z Reseni | ||||
| 		clanek_node = r.text_cely	# vazba na ReseniNode z Reseni | ||||
| 		# content type je věc pomáhající rozeznávat různé typy objektů v django-polymorphic | ||||
| 		# protože isinstance vrátí vždy jen TreeNode | ||||
| 		# https://django-polymorphic.readthedocs.io/en/stable/migrating.html | ||||
|  | @ -105,7 +112,7 @@ def seznam_problemu(): | |||
| 		node = clanek_node | ||||
| 		while node is not None: | ||||
| 			node_ct = node.polymorphic_ctype | ||||
| 			if node_ct == cislonode_ct: # dostali jsme se k CisloNode | ||||
| 			if node_ct == cislonode_ct:	# dostali jsme se k CisloNode | ||||
| 				# zkontrolujeme, že stromové číslo odpovídá atributu | ||||
| 				# .cislonode je opačná vazba k treenode_ptr, abychom z TreeNode dostali | ||||
| 				# CisloNode | ||||
|  | @ -114,5 +121,67 @@ def seznam_problemu(): | |||
| 						"číslem otištění podle struktury treenodů.", [clanek]) | ||||
| 				break | ||||
| 			node = t.get_parent(node) | ||||
| 		 | ||||
| 
 | ||||
| 	return problemy | ||||
| 
 | ||||
| 
 | ||||
| ### Generovani obalek | ||||
| def resi_v_rocniku(rocnik, cislo=None): | ||||
| 	""" Vrátí seznam řešitelů, co vyřešili nějaký problém v daném ročníku, do daného čísla. | ||||
| 	Parametry: | ||||
| 		rocnik (typu Rocnik)	ročník, ze kterého chci řešitele, co něco odevzdali | ||||
| 		cislo (typu Cislo)	číslo, do kterého včetně se počítá, že v daném | ||||
| 					ročníku řešitel něco poslal. | ||||
| 					Pokud není zadané, počítají se všechna řešení z daného ročníku. | ||||
| 	Výstup: | ||||
| 		QuerySet objektů typu Resitel """ | ||||
| 
 | ||||
| 	if cislo is None: | ||||
| 		# filtrujeme pouze podle ročníku | ||||
| 		letosni_reseni = m.Reseni.objects.filter(hodnoceni__cislo_body__rocnik=rocnik) | ||||
| 	else:  # filtrujeme podle ročníku i čísla | ||||
| 		letosni_reseni = m.Reseni.objects.filter(hodnoceni__cislo_body__rocnik=rocnik, | ||||
| 												 hodnoceni__cislo_body__poradi__lte=cislo.poradi) | ||||
| 
 | ||||
| 	# vygenerujeme queryset řešitelů, co letos něco poslali | ||||
| 	letosni_resitele = m.Resitel.objects.none() | ||||
| 	for reseni in letosni_reseni: | ||||
| 		letosni_resitele = letosni_resitele | reseni.resitele.filter(rok_maturity__gte=rocnik.druhy_rok()) | ||||
| 	return letosni_resitele.distinct() | ||||
| 
 | ||||
| 
 | ||||
| def aktivniResitele(cislo, pouze_letosni=False): | ||||
| 	""" Vrací QuerySet aktivních řešitelů, což jsou ti, co ještě neodmaturovali | ||||
| 	a letos něco poslali (anebo loni něco poslali, pokud jde o první tři čísla). | ||||
| 	Parametry: | ||||
| 		cislo (typu Cislo)	číslo, o které se jedná | ||||
| 		pouze_letosni		jen řešitelé, kteří tento rok něco poslali | ||||
| 
 | ||||
| 	""" | ||||
| 	letos = cislo.rocnik | ||||
| 
 | ||||
| 	# detekujeme, zda jde o první tři čísla či nikoli (tj. zda spamovat řešitele z minulého roku) | ||||
| 	zacatek_rocniku = True | ||||
| 	try: | ||||
| 		if int(cislo.poradi) > 3: | ||||
| 			zacatek_rocniku = False | ||||
| 	except ValueError: | ||||
| 		if cislo.poradi != '7-8': | ||||
| 			raise ValueError(f'{cislo} je neplatné číslo čísla (není int a není 7-8)') | ||||
| 		zacatek_rocniku = False | ||||
| 
 | ||||
| 	# nehledě na číslo chceme jen řešitele, kteří letos něco odevzdali | ||||
| 	if pouze_letosni: | ||||
| 		zacatek_rocniku = False | ||||
| 
 | ||||
| 	try: | ||||
| 		loni = m.Rocnik.objects.get(rocnik=letos.rocnik - 1) | ||||
| 	except ObjectDoesNotExist: | ||||
| 		# Pro první ročník neexistuje ročník předchozí | ||||
| 		zacatek_rocniku = False | ||||
| 
 | ||||
| 	if not zacatek_rocniku: | ||||
| 		return resi_v_rocniku(letos, cislo) | ||||
| 	else: | ||||
| 		# spojíme querysety s řešiteli loni a letos do daného čísla | ||||
| 		return (resi_v_rocniku(loni) | resi_v_rocniku(letos, cislo)).distinct() | ||||
|  |  | |||
|  | @ -41,6 +41,8 @@ import csv | |||
| import logging | ||||
| import time | ||||
| 
 | ||||
| from seminar.utils import aktivniResitele, resi_v_rocniku | ||||
| 
 | ||||
| 
 | ||||
| def verejna_temata(rocnik): | ||||
| 	"""Vrací queryset zveřejněných témat v daném ročníku. | ||||
|  | @ -51,7 +53,7 @@ def temata_v_rocniku(rocnik): | |||
| 	return Problem.objects.filter(typ=Problem.TYP_TEMA, rocnik=rocnik) | ||||
| 
 | ||||
| def get_problemy_k_tematu(tema): | ||||
| 	return Problemy.objects.filter(nadproblem = tema) | ||||
| 	return Problem.objects.filter(nadproblem = tema) | ||||
| 
 | ||||
| 
 | ||||
| class VlozBodyView(generic.ListView): | ||||
|  | @ -723,7 +725,7 @@ def vysledkovka_cisla(cislo, context=None): | |||
| 	## 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.rocnik.rocnik, cislo.poradi)) | ||||
| 	aktivni_resitele = list(aktivniResitele(cislo)) | ||||
| 
 | ||||
| 	# získáme body za číslo | ||||
| 	hlavni_problemy_slovnik, cislobody = secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy) | ||||
|  | @ -774,7 +776,7 @@ class CisloView(generic.DetailView): | |||
| 	model = Cislo | ||||
| 	template_name = 'seminar/archiv/cislo.html' | ||||
| 
 | ||||
|        # Vlastni ziskavani objektu z databaze podle (Rocnik.rocnik) | ||||
| 	# Vlastni ziskavani objektu z databaze podle (Rocnik.rocnik) | ||||
| 	def get_object(self, queryset=None): | ||||
| 		if queryset is None: | ||||
| 			queryset = self.get_queryset() | ||||
|  | @ -830,66 +832,9 @@ class RocnikVysledkovkaView(RocnikView): | |||
| 	content_type = 'text/plain; charset=UTF8' | ||||
| 	#vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani | ||||
| 
 | ||||
| ### Generovani obalek | ||||
| def resi_v_rocniku(rocnik, cislo=None): | ||||
| 	""" Vrátí seznam řešitelů, co vyřešili nějaký problém v daném ročníku, do daného čísla. | ||||
| 	Parametry: | ||||
| 		rocnik (typu Rocnik)	ročník, ze kterého chci řešitele, co něco odevzdali | ||||
| 		cislo (typu Cislo)	číslo, do kterého včetně se počítá, že v daném  | ||||
| 					ročníku řešitel něco poslal. | ||||
| 					Pokud není zadané, počítají se všechna řešení z daného ročníku. | ||||
| 	Výstup: | ||||
| 		QuerySet objektů typu Resitel """ | ||||
| 
 | ||||
| 	if cislo is None: | ||||
| 		# filtrujeme pouze podle ročníku | ||||
| 		letosni_reseni = Reseni.objects.filter(hodnoceni__cislo_body__rocnik = rocnik) | ||||
| 	else: # filtrujeme podle ročníku i čísla | ||||
| 		letosni_reseni = Reseni.objects.filter(hodnoceni__cislo_body__rocnik = rocnik, | ||||
| 					hodnoceni__cislo_body__poradi__lte=cislo.poradi) | ||||
| 
 | ||||
| 	# vygenerujeme queryset řešitelů, co letos něco poslali | ||||
| 	letosni_resitele = Resitel.objects.none() | ||||
| 	for reseni in letosni_reseni: | ||||
| 		letosni_resitele = letosni_resitele | reseni.resitele.filter(rok_maturity__gte=rocnik.druhy_rok()) | ||||
| 	return letosni_resitele.distinct() | ||||
| 	 | ||||
| 		 | ||||
| def aktivniResitele(rocnik, cislo, pouze_realni=False): | ||||
| 	""" Vrací QuerySet aktivních řešitelů, což jsou ti, co ještě neodmaturovali | ||||
| 	a letos něco poslali (anebo loni něco poslali, pokud jde o první tři čísla). | ||||
| 	Parametry: | ||||
| 		rocnik (typu int)	číslo ročníku, o který se jedná | ||||
| 		cislo (typu int)	pořadí čísla, o které se jedná | ||||
| 		pouze_realni		jen řešitelé, kteří tento rok něco poslali | ||||
| 
 | ||||
| 	""" | ||||
| 	letos = Rocnik.objects.get(rocnik = rocnik) | ||||
| 	#TODO: co se stane, když zadané kombinace neexistují? ošetřit | ||||
| 	aktualni_cislo = Cislo.objects.get(rocnik = rocnik, poradi = cislo) | ||||
| 	loni = Rocnik.objects.get(rocnik = rocnik - 1) | ||||
| 
 | ||||
|         # detekujeme, zda jde o první tři čísla či nikoli | ||||
| 	zacatek_rocniku = True | ||||
| 	try: | ||||
| 		if int(aktualni_cislo.poradi) > 3: | ||||
| 			zacatek_rocniku = False | ||||
| 	except ValueError: | ||||
|        		# pravděpodobně se jedná o číslo 7-8 | ||||
| 		zacatek_rocniku = False | ||||
| 	 | ||||
| 	# nehledě na číslo chceme jen řešitele, kteří letos něco odevzdali | ||||
| 	if pouze_realni: | ||||
| 		zacatek_rocniku = False | ||||
| 
 | ||||
| 	if not zacatek_rocniku: | ||||
| 		return resi_v_rocniku(letos)	 | ||||
| 	else: | ||||
| 		# spojíme querysety s řešiteli loni a letos do daného čísla | ||||
| 		return (resi_v_rocniku(loni) | resi_v_rocniku(letos, aktualni_cislo)).distinct() | ||||
| 
 | ||||
| def cisloObalkyView(request, rocnik, cislo): | ||||
| 	return obalkyView(request, aktivniResitele(rocnik, cislo)) | ||||
| 	realne_cislo = Cislo.objects.get(poradi=cislo, rocnik__rocnik=rocnik) | ||||
| 	return obalkyView(request, aktivniResitele(realne_cislo)) | ||||
| 
 | ||||
| 
 | ||||
| def obalkyView(request, resitele): | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Pavel 'LEdoian' Turinsky
						Pavel 'LEdoian' Turinsky