Podezřelé semináře (#1465) #65
					 20 changed files with 232 additions and 213 deletions
				
			
		|  | @ -5,7 +5,9 @@ from dal import autocomplete | |||
| from django.shortcuts import get_object_or_404 | ||||
| from django.db.models import Q | ||||
| 
 | ||||
| import seminar.models as m | ||||
| from personalni.models import Skola, Resitel | ||||
| from tvorba.models import Problem | ||||
| from various.models import Nastaveni | ||||
| from .helpers import LoginRequiredAjaxMixin | ||||
| 
 | ||||
| # TODO filosofie - zkratky, jak v databázi, tak ve vyhledávání (SPŠE, GASOŠ, Kpt., soukr) | ||||
|  | @ -13,7 +15,7 @@ class SkolaAutocomplete(autocomplete.Select2QuerySetView): | |||
| 	""" View k :mod:`dal.autocomplete` pro vyhledávání škol hlavně při registraci. """ | ||||
| 	def get_queryset(self): | ||||
| 		# Don't forget to filter out results depending on the visitor ! | ||||
| 		qs = m.Skola.objects.all() | ||||
| 		qs = Skola.objects.all() | ||||
| 		if self.q: | ||||
| 			words = self.q.split(' ') #TODO re split podle bileho znaku | ||||
| 			partq = Q() | ||||
|  | @ -31,7 +33,7 @@ class SkolaAutocomplete(autocomplete.Select2QuerySetView): | |||
| class ResitelAutocomplete(LoginRequiredAjaxMixin,autocomplete.Select2QuerySetView): | ||||
| 	""" View k :mod:`dal.autocomplete` pro vyhledávání řešitelů především v odevzdávátku. """ | ||||
| 	def get_queryset(self): | ||||
| 		qs = m.Resitel.objects.all() | ||||
| 		qs = Resitel.objects.all() | ||||
| 		if self.q: | ||||
| 			parts = self.q.split() | ||||
| 			query = Q() | ||||
|  | @ -51,8 +53,8 @@ class PublicResitelAutocomplete(LoginRequiredAjaxMixin, autocomplete.Select2Quer | |||
| 		především v odevzdávátku. | ||||
| 	""" | ||||
| 	def get_queryset(self): | ||||
| 		letos = m.Nastaveni.get_solo().aktualni_rocnik | ||||
| 		qs = m.Resitel.objects.filter( | ||||
| 		letos = Nastaveni.get_solo().aktualni_rocnik | ||||
| 		qs = Resitel.objects.filter( | ||||
| 			rok_maturity__gte=letos.druhy_rok() | ||||
| 		).filter( | ||||
| 			prezdivka_resitele__isnull=False | ||||
|  | @ -70,7 +72,7 @@ class PublicResitelAutocomplete(LoginRequiredAjaxMixin, autocomplete.Select2Quer | |||
| class OdevzdatelnyProblemAutocomplete(autocomplete.Select2QuerySetView): | ||||
| 	""" View k :mod:`dal.autocomplete` pro vyhledávání problémů především v odevzdávátku. """ | ||||
| 	def get_queryset(self): | ||||
| 		qs = m.Problem.objects.filter(stav=m.Problem.STAV_ZADANY) | ||||
| 		qs = Problem.objects.filter(stav=Problem.STAV_ZADANY) | ||||
| 		if self.q: | ||||
| 			qs = qs.filter( | ||||
| 					Q(nazev__icontains=self.q)) | ||||
|  | @ -87,12 +89,12 @@ class ProblemAutocomplete(autocomplete.Select2QuerySetView): | |||
| 	""" View k :mod:`dal.autocomplete` pro vyhledávání problémů především v odevzdávátku. """ | ||||
| 	def get_queryset(self): | ||||
| 		# FIXME i starší úlohy | ||||
| 		nastaveni = get_object_or_404(m.Nastaveni) | ||||
| 		nastaveni = get_object_or_404(Nastaveni) | ||||
| 		rocnik = nastaveni.aktualni_rocnik | ||||
| 		temaQ = Q(Tema___rocnik = rocnik) | ||||
| 		ulohaQ = Q(Uloha___cislo_zadani__rocnik=rocnik) | ||||
| 		clanekQ = Q(Clanek___cislo__rocnik=rocnik) | ||||
| 		qs = m.Problem.objects.filter(temaQ | ulohaQ | clanekQ).order_by("-stav", "nazev") | ||||
| 		qs = Problem.objects.filter(temaQ | ulohaQ | clanekQ).order_by("-stav", "nazev") | ||||
| 		if self.q: | ||||
| 			qs = qs.filter( | ||||
| 					Q(nazev__icontains=self.q)) | ||||
|  |  | |||
|  | @ -43,7 +43,7 @@ def get_app_list(self, request, app_label=None): | |||
| 
 | ||||
| 	app_dict = self._build_app_dict(request, label=app_label) | ||||
| 	aplikace_nahore = [ | ||||
| 		'seminar', | ||||
| 		'tvorba', | ||||
| 		'personalni', | ||||
| 		'novinky', | ||||
| 		'korektury', | ||||
|  | @ -57,7 +57,7 @@ def get_app_list(self, request, app_label=None): | |||
| 
 | ||||
| 	# Sort the models alphabetically within each app. | ||||
| 	for app in app_list: | ||||
| 		app['models'].sort(key=lambda x: locale.strxfrm('žž' + x['name'].lower()) if (x['name'].endswith("(Node)")) else locale.strxfrm(x['name'].lower())) | ||||
| 		app['models'].sort(key=lambda x: locale.strxfrm(x['name'].lower())) | ||||
| 
 | ||||
| 	return app_list | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,7 +5,10 @@ from django.forms.models import inlineformset_factory | |||
| from django.utils import timezone | ||||
| 
 | ||||
| from personalni.models import Resitel | ||||
| import seminar.models as m | ||||
| from tvorba.models import Problem, Deadline | ||||
| from various.models import Nastaveni | ||||
| 
 | ||||
| from odevzdavatko.models import Reseni, PrilohaReseni, Hodnoceni | ||||
| 
 | ||||
| import logging | ||||
| 
 | ||||
|  | @ -22,7 +25,7 @@ class DateInput(forms.DateInput): | |||
| 
 | ||||
| class PosliReseniForm(forms.Form): | ||||
| 	problem = forms.ModelMultipleChoiceField( | ||||
| 		queryset=m.Problem.objects.all(), | ||||
| 		queryset=Problem.objects.all(), | ||||
| 		label="Problémy", | ||||
| 		widget=autocomplete.ModelSelect2Multiple( | ||||
| 			url='autocomplete_problem', | ||||
|  | @ -58,7 +61,7 @@ class PosliReseniForm(forms.Form): | |||
| 
 | ||||
| 	#cas_doruceni = models.DateTimeField('čas_doručení', default=timezone.now, blank=True) | ||||
| 
 | ||||
| 	forma = forms.ChoiceField(label="Forma řešení",choices = m.Reseni.FORMA_CHOICES) | ||||
| 	forma = forms.ChoiceField(label="Forma řešení",choices = Reseni.FORMA_CHOICES) | ||||
| 	#forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False, | ||||
| 	#	 default=FORMA_EMAIL) | ||||
| 
 | ||||
|  | @ -69,7 +72,7 @@ class PosliReseniForm(forms.Form): | |||
| 
 | ||||
| class NahrajReseniForm(forms.ModelForm): | ||||
| 	class Meta: | ||||
| 		model = m.Reseni | ||||
| 		model = Reseni | ||||
| 		fields = ('problem', 'resitele') | ||||
| 		help_texts = {'problem':''} # Nezobrazovat help text ve formuláři | ||||
| 		 | ||||
|  | @ -109,11 +112,11 @@ class NahrajReseniForm(forms.ModelForm): | |||
| 	def clean_problem(self): | ||||
| 		problem = self.cleaned_data.get('problem') | ||||
| 		for p in problem: | ||||
| 			if p.stav != m.Problem.STAV_ZADANY: | ||||
| 			if p.stav != Problem.STAV_ZADANY: | ||||
| 				raise forms.ValidationError("Problém " + str(p) + " již nelze řešit!") | ||||
| 		return problem | ||||
| 
 | ||||
| ReseniSPrilohamiFormSet = inlineformset_factory(m.Reseni,m.PrilohaReseni,  | ||||
| ReseniSPrilohamiFormSet = inlineformset_factory(Reseni, PrilohaReseni, | ||||
| 		form = NahrajReseniForm, | ||||
| 		fields = ('soubor','res_poznamka'), | ||||
| 		widgets = {'res_poznamka':forms.TextInput()}, | ||||
|  | @ -125,7 +128,7 @@ ReseniSPrilohamiFormSet = inlineformset_factory(m.Reseni,m.PrilohaReseni, | |||
| 
 | ||||
| class JednoHodnoceniForm(forms.ModelForm): | ||||
| 	class Meta: | ||||
| 		model = m.Hodnoceni | ||||
| 		model = Hodnoceni | ||||
| 		fields = ('problem', 'body', 'deadline_body', 'feedback',) | ||||
| 		widgets = { | ||||
| 			'problem': autocomplete.ModelSelect2( | ||||
|  | @ -158,7 +161,7 @@ OhodnoceniReseniFormSet = formset_factory(JednoHodnoceniForm, | |||
| 
 | ||||
| class PoznamkaReseniForm(forms.ModelForm): | ||||
| 	class Meta: | ||||
| 		model = m.Reseni | ||||
| 		model = Reseni | ||||
| 		fields = ('poznamka',) | ||||
| 
 | ||||
| # FIXME: Ideálně by mělo být součástí třídy níž, ale neumím to udělat | ||||
|  | @ -198,7 +201,7 @@ class OdevzdavatkoTabulkaFiltrForm(forms.Form): | |||
| 		 | ||||
| 		from django.db.utils import OperationalError | ||||
| 		try: | ||||
| 			aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik | ||||
| 			aktualni_rocnik = Nastaveni.get_solo().aktualni_rocnik | ||||
| 		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 | ||||
|  | @ -214,7 +217,7 @@ class OdevzdavatkoTabulkaFiltrForm(forms.Form): | |||
| 
 | ||||
| 		result.append(("0001-01-01", f"Odjakživa")) | ||||
| 
 | ||||
| 		for deadline in m.Deadline.objects.filter( | ||||
| 		for deadline in Deadline.objects.filter( | ||||
| 				deadline__lte=timezone.now(), | ||||
| 				cislo__rocnik=aktualni_rocnik | ||||
| 				).order_by("deadline"): | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ from django.urls import reverse_lazy | |||
| from django.utils import timezone | ||||
| from django.conf import settings | ||||
| 
 | ||||
| import tvorba.models as am | ||||
| from tvorba.models import Problem, Deadline, Cislo, Uloha, aux_generate_filename | ||||
| from seminar.models import base as bm | ||||
| 
 | ||||
| from odevzdavatko.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet | ||||
|  | @ -29,7 +29,7 @@ class Reseni(bm.SeminarModelBase): | |||
| 	id = models.AutoField(primary_key = True) | ||||
| 
 | ||||
| 	# Ke každé dvojici řešní a problém existuje nanejvýš jedno hodnocení, doplnění vazby. | ||||
| 	problem = models.ManyToManyField(am.Problem, verbose_name='problém', help_text='Problém', | ||||
| 	problem = models.ManyToManyField(Problem, verbose_name='problém', help_text='Problém', | ||||
| 									 through='Hodnoceni') | ||||
| 
 | ||||
| 	resitele = models.ManyToManyField(Resitel, verbose_name='autoři řešení', | ||||
|  | @ -79,7 +79,7 @@ class Reseni(bm.SeminarModelBase): | |||
| 	# NOTE: Potenciální DB HOG (bez select_related) | ||||
| 
 | ||||
| 	def deadline_reseni(self): | ||||
| 		return am.Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first() | ||||
| 		return Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first() | ||||
| 
 | ||||
| ## Pravdepodobne uz nebude potreba: | ||||
| #	def save(self, *args, **kwargs): | ||||
|  | @ -101,16 +101,16 @@ class Hodnoceni(bm.SeminarModelBase): | |||
| 	body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body', | ||||
| 							   blank=True, null=True) | ||||
| 
 | ||||
| 	cislo_body = models.ForeignKey(am.Cislo, verbose_name='číslo pro body', | ||||
| 	cislo_body = models.ForeignKey(Cislo, verbose_name='číslo pro body', | ||||
| 								   related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT) | ||||
| 
 | ||||
| 	# V ročníku < 26 nastaveno na deadline vygenerovaný pro původní cislo_body | ||||
| 	deadline_body = models.ForeignKey(am.Deadline, verbose_name='deadline pro body', | ||||
| 	deadline_body = models.ForeignKey(Deadline, verbose_name='deadline pro body', | ||||
| 								   related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT) | ||||
| 
 | ||||
| 	reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) | ||||
| 
 | ||||
| 	problem = models.ForeignKey(am.Problem, verbose_name='problém', | ||||
| 	problem = models.ForeignKey(Problem, verbose_name='problém', | ||||
| 								related_name='hodnoceni', on_delete=models.PROTECT) | ||||
| 
 | ||||
| 	feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)') | ||||
|  | @ -166,7 +166,7 @@ class Hodnoceni(bm.SeminarModelBase): | |||
| 
 | ||||
| 	@property | ||||
| 	def body_neprepocitane_max(self): | ||||
| 		if not isinstance(self.problem.get_real_instance(), am.Uloha): | ||||
| 		if not isinstance(self.problem.get_real_instance(), Uloha): | ||||
| 			return None | ||||
| 		return self.problem.uloha.max_body | ||||
| 
 | ||||
|  | @ -176,7 +176,7 @@ class Hodnoceni(bm.SeminarModelBase): | |||
| def generate_filename(self, filename): | ||||
| 	return os.path.join( | ||||
| 		settings.SEMINAR_RESENI_DIR, | ||||
| 		am.aux_generate_filename(self, filename) | ||||
| 		aux_generate_filename(self, filename) | ||||
| 	) | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,10 +17,14 @@ from decimal import Decimal | |||
| from itertools import groupby | ||||
| import logging | ||||
| 
 | ||||
| import seminar.models as m | ||||
| from . import forms as f | ||||
| from .forms import OdevzdavatkoTabulkaFiltrForm as FiltrForm | ||||
| from .models import Hodnoceni, Reseni | ||||
| 
 | ||||
| from personalni.models import Resitel, Osoba, Organizator | ||||
| from tvorba.models import Problem, Deadline, Rocnik | ||||
| from tvorba.utils import resi_v_rocniku | ||||
| from various.models import Nastaveni | ||||
| from various.views.pomocne import formularOKView | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
|  | @ -40,20 +44,20 @@ logger = logging.getLogger(__name__) | |||
| 
 | ||||
| class TabulkaOdevzdanychReseniView(ListView): | ||||
| 	template_name = 'odevzdavatko/tabulka.html' | ||||
| 	model = m.Hodnoceni | ||||
| 	model = Hodnoceni | ||||
| 
 | ||||
| 	def inicializuj_osy_tabulky(self): | ||||
| 		"""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 | ||||
| 		# TODO: Prefetches, Select related, ... | ||||
| 		self.resitele = m.Resitel.objects.all() | ||||
| 		self.problemy = m.Problem.objects.all() | ||||
| 		self.reseni = m.Reseni.objects.all() | ||||
| 		self.resitele = Resitel.objects.all() | ||||
| 		self.problemy = Problem.objects.all() | ||||
| 		self.reseni = Reseni.objects.all() | ||||
| 
 | ||||
| 		self.aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik	# .get_solo() vrátí tu jedinou instanci | ||||
| 		self.aktualni_rocnik = Nastaveni.get_solo().aktualni_rocnik	# .get_solo() vrátí tu jedinou instanci | ||||
| 		if 'rocnik' in self.kwargs: | ||||
| 			self.aktualni_rocnik = get_object_or_404(m.Rocnik, rocnik=self.kwargs['rocnik']) | ||||
| 			self.aktualni_rocnik = get_object_or_404(Rocnik, rocnik=self.kwargs['rocnik']) | ||||
| 
 | ||||
| 		form = FiltrForm(self.request.GET, rocnik=self.aktualni_rocnik) | ||||
| 		if form.is_valid(): | ||||
|  | @ -86,14 +90,14 @@ class TabulkaOdevzdanychReseniView(ListView): | |||
| 			self.resitele = self.resitele.filter(rok_maturity__gt=self.aktualni_rocnik.prvni_rok) | ||||
| 
 | ||||
| 		if problemy == FiltrForm.PROBLEMY_MOJE: | ||||
| 			org = m.Organizator.objects.get(osoba__user=self.request.user) | ||||
| 			org = Organizator.objects.get(osoba__user=self.request.user) | ||||
| 			self.problemy = self.problemy.filter( | ||||
| 					Q(autor=org)|Q(garant=org)|Q(opravovatele=org), | ||||
| 					Q(stav=m.Problem.STAV_ZADANY)|Q(stav=m.Problem.STAV_VYRESENY), | ||||
| 					Q(stav=Problem.STAV_ZADANY)|Q(stav=Problem.STAV_VYRESENY), | ||||
| 					) | ||||
| 		elif problemy == FiltrForm.PROBLEMY_LETOSNI: | ||||
| 			self.problemy = self.problemy.filter( | ||||
| 					Q(stav=m.Problem.STAV_ZADANY)|Q(stav=m.Problem.STAV_VYRESENY), | ||||
| 					Q(stav=Problem.STAV_ZADANY)|Q(stav=Problem.STAV_VYRESENY), | ||||
| 					) | ||||
| 			#self.problemy = list(filter(lambda problem: problem.rocnik() == self.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. | ||||
|  | @ -121,8 +125,8 @@ class TabulkaOdevzdanychReseniView(ListView): | |||
| 		ctx = super().get_context_data(*args, **kwargs) | ||||
| 		ctx['problemy'] = self.problemy | ||||
| 		ctx['resitele'] = self.resitele | ||||
| 		tabulka: dict[m.Problem, dict[m.Resitel, list[tuple[m.Reseni, m.Hodnoceni]]]] = dict() | ||||
| 		soucty: dict[m.Problem, dict[m.Resitel, Decimal]] = dict() | ||||
| 		tabulka: dict[Problem, dict[Resitel, list[tuple[Reseni, Hodnoceni]]]] = dict() | ||||
| 		soucty: dict[Problem, dict[Resitel, Decimal]] = dict() | ||||
| 
 | ||||
| 		def pridej_reseni(resitel, hodnoceni): | ||||
| 			problem = hodnoceni.problem | ||||
|  | @ -143,11 +147,11 @@ class TabulkaOdevzdanychReseniView(ListView): | |||
| 			for resitel in hodnoceni.reseni.resitele.all(): | ||||
| 				pridej_reseni(resitel, hodnoceni) | ||||
| 
 | ||||
| 		hodnoty: list[list[tuple[Decimal,list[tuple[m.Reseni, m.Hodnoceni]]]]] = [] # Seznam řádků výsledné tabulky podle self.resitele, v každém řádku buňky v pořadí podle self.problemy + jejich součty, v každé buňce seznam řešení k danému řešiteli a problému. | ||||
| 		resitele_do_tabulky: list[m.Resitel] = [] | ||||
| 		hodnoty: list[list[tuple[Decimal,list[tuple[Reseni, Hodnoceni]]]]] = [] # Seznam řádků výsledné tabulky podle self.resitele, v každém řádku buňky v pořadí podle self.problemy + jejich součty, v každé buňce seznam řešení k danému řešiteli a problému. | ||||
| 		resitele_do_tabulky: list[Resitel] = [] | ||||
| 		for resitel in self.resitele: | ||||
| 			dostal_body = False | ||||
| 			resiteluv_radek: list[tuple[Decimal,list[tuple[m.Reseni, m.Hodnoceni]]]] = [] # podle pořadí v self.problemy | ||||
| 			resiteluv_radek: list[tuple[Decimal,list[tuple[Reseni, Hodnoceni]]]] = [] # podle pořadí v self.problemy | ||||
| 			for problem in self.problemy: | ||||
| 				if problem in tabulka and resitel in tabulka[problem]: | ||||
| 					resiteluv_radek.append((soucty[problem][resitel], tabulka[problem][resitel])) | ||||
|  | @ -162,7 +166,7 @@ class TabulkaOdevzdanychReseniView(ListView): | |||
| 		# Pro použití hacku na automatické {{form.media}} v template: | ||||
| 		ctx['form'] = ctx['filtr'] | ||||
| 		# Pro maximum v přesměrovátku ročníků | ||||
| 		ctx['aktualni_rocnik'] = m.Nastaveni.get_solo().aktualni_rocnik | ||||
| 		ctx['aktualni_rocnik'] = Nastaveni.get_solo().aktualni_rocnik | ||||
| 		ctx['barvicky'] = self.barvicky | ||||
| 		if 'rocnik' in self.kwargs: | ||||
| 			ctx['rocnik'] = self.kwargs['rocnik'] | ||||
|  | @ -178,7 +182,7 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi | |||
| 	Asi už bude zastaralý v okamžiku, kdy se tenhle komentář nasadí na produkci :-) | ||||
| 
 | ||||
| 	V případě, že takové řešení existuje jen jedno, tak na něj přesměruje.""" | ||||
| 	model = m.Reseni | ||||
| 	model = Reseni | ||||
| 	template_name = 'odevzdavatko/seznam.html' | ||||
| 	 | ||||
| 	def get_queryset(self): | ||||
|  | @ -190,8 +194,8 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi | |||
| 		if problem_id is None: | ||||
| 			raise ValueError("Nemám problém! (To je problém!)") | ||||
| 		 | ||||
| 		resitel = m.Resitel.objects.get(id=resitel_id) | ||||
| 		problem = m.Problem.objects.get(id=problem_id) | ||||
| 		resitel = Resitel.objects.get(id=resitel_id) | ||||
| 		problem = Problem.objects.get(id=problem_id) | ||||
| 		qs = qs.filter( | ||||
| 			problem__in=[problem], | ||||
| 			resitele__in=[resitel], | ||||
|  | @ -221,13 +225,13 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi | |||
| ## XXX: https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#avoid-anything-more-complex | ||||
| class DetailReseniView(DetailView): | ||||
| 	""" Náhled na řešení. Editace je v :py:class:`EditReseniView`. """ | ||||
| 	model = m.Reseni | ||||
| 	model = Reseni | ||||
| 	template_name = 'odevzdavatko/detail.html' | ||||
| 	 | ||||
| 	def aktualni_hodnoceni(self): | ||||
| 		self.reseni = get_object_or_404(m.Reseni, id=self.kwargs['pk']) | ||||
| 		self.reseni = get_object_or_404(Reseni, id=self.kwargs['pk']) | ||||
| 		result = [] # Slovníky s klíči problem, body, deadline_body -- initial data pro f.OhodnoceniReseniFormSet | ||||
| 		for hodn in m.Hodnoceni.objects.filter(reseni=self.reseni): | ||||
| 		for hodn in Hodnoceni.objects.filter(reseni=self.reseni): | ||||
| 			seznam_atributu = [ | ||||
| 				"problem", | ||||
| 				"body", | ||||
|  | @ -284,7 +288,7 @@ class EditReseniView(DetailReseniView): | |||
| 
 | ||||
| 
 | ||||
| def hodnoceniReseniView(request, pk, *args, **kwargs): | ||||
| 	reseni = get_object_or_404(m.Reseni, pk=pk) | ||||
| 	reseni = get_object_or_404(Reseni, pk=pk) | ||||
| 	success_url = reverse('odevzdavatko_detail_reseni', kwargs={'pk': pk}) | ||||
| 
 | ||||
| 	# FIXME: Použit initial i tady a nebastlit hodnocení tak nízkoúrovňově | ||||
|  | @ -300,7 +304,7 @@ def hodnoceniReseniView(request, pk, *args, **kwargs): | |||
| 		poznamka_form.save() | ||||
| 
 | ||||
| 		# Smažeme všechna dosavadní hodnocení tohoto řešení | ||||
| 		qs = m.Hodnoceni.objects.filter(reseni=reseni) | ||||
| 		qs = Hodnoceni.objects.filter(reseni=reseni) | ||||
| 		logger.info(f"Will delete {qs.count()} objects: {qs}") | ||||
| 		qs.delete() | ||||
| 
 | ||||
|  | @ -311,7 +315,7 @@ def hodnoceniReseniView(request, pk, *args, **kwargs): | |||
| 			del(data_for_hodnoceni["body_celkem"]) | ||||
| 			del(data_for_hodnoceni["body_neprepocitane"]) | ||||
| 			del(data_for_hodnoceni["body_neprepocitane_celkem"]) | ||||
| 			hodnoceni = m.Hodnoceni( | ||||
| 			hodnoceni = Hodnoceni( | ||||
| 					reseni=reseni, | ||||
| 					**form.cleaned_data, | ||||
| 					) | ||||
|  | @ -332,14 +336,14 @@ def hodnoceniReseniView(request, pk, *args, **kwargs): | |||
| 
 | ||||
| 
 | ||||
| class PrehledOdevzdanychReseni(ListView): | ||||
| 	model = m.Hodnoceni | ||||
| 	model = Hodnoceni | ||||
| 	template_name = 'odevzdavatko/prehled_reseni.html' | ||||
| 
 | ||||
| 	def get_queryset(self): | ||||
| 		if not self.request.user.is_authenticated: | ||||
| 			raise RuntimeError("Uživatel měl být přihlášený!") | ||||
| 		# get_or_none, aby neexistence řešitele (např. u orgů) neházela chybu | ||||
| 		resitel = m.Resitel.objects.filter(osoba__user=self.request.user).first() | ||||
| 		resitel = Resitel.objects.filter(osoba__user=self.request.user).first() | ||||
| 		qs = super().get_queryset() | ||||
| 		qs = qs.filter(reseni__resitele__in=[resitel]) | ||||
| 		# Setřídíme podle času doručení řešení, aby se netřídily podle okamžiku vyrobení Hodnocení | ||||
|  | @ -360,13 +364,13 @@ class PrehledOdevzdanychReseni(ListView): | |||
| # Přehled všech řešení kvůli debugování | ||||
| 
 | ||||
| class SeznamReseniView(ListView): | ||||
| 	model = m.Reseni | ||||
| 	model = Reseni | ||||
| 	template_name = 'odevzdavatko/seznam.html' | ||||
| 
 | ||||
| class SeznamAktualnichReseniView(SeznamReseniView): | ||||
| 	def get_queryset(self): | ||||
| 		qs = super().get_queryset() | ||||
| 		akt_rocnik = m.Nastaveni.get_solo().aktualni_rocnik	# .get_solo() vrátí tu jedinou instanci, asi... | ||||
| 		akt_rocnik = Nastaveni.get_solo().aktualni_rocnik	# .get_solo() vrátí tu jedinou instanci, asi... | ||||
| 		resitele = resi_v_rocniku(akt_rocnik) | ||||
| 		qs = qs.filter(resitele__in=resitele)	# FIXME: Najde řešení i ze starých ročníků, která odevzdal alespoň jeden aktuální řešitel | ||||
| 		return qs | ||||
|  | @ -378,7 +382,7 @@ class VlozReseniView(LoginRequiredMixin, FormView): | |||
| 
 | ||||
| 	def form_valid(self, form): | ||||
| 		data = form.cleaned_data | ||||
| 		nove_reseni = m.Reseni.objects.create( | ||||
| 		nove_reseni = Reseni.objects.create( | ||||
| 			cas_doruceni=data['cas_doruceni'], | ||||
| 			forma=data['forma'], | ||||
| 			poznamka=data['poznamka'], | ||||
|  | @ -405,35 +409,35 @@ class VlozReseniView(LoginRequiredMixin, FormView): | |||
| 
 | ||||
| 
 | ||||
| class NahrajReseniRozcestnikTematekView(LoginRequiredMixin, ListView): | ||||
| 	model = m.Problem | ||||
| 	model = Problem | ||||
| 	template_name = 'odevzdavatko/nahraj_reseni_nadproblem.html' | ||||
| 
 | ||||
| 	def get_queryset(self): | ||||
| 		return super().get_queryset().filter(stav=m.Problem.STAV_ZADANY, nadproblem__isnull=True) | ||||
| 		return super().get_queryset().filter(stav=Problem.STAV_ZADANY, nadproblem__isnull=True) | ||||
| 
 | ||||
| 
 | ||||
| class NahrajReseniView(LoginRequiredMixin, CreateView): | ||||
| 	model = m.Reseni | ||||
| 	model = Reseni | ||||
| 	template_name = 'odevzdavatko/nahraj_reseni.html' | ||||
| 	form_class = f.NahrajReseniForm | ||||
| 	nadproblem: m.Problem | ||||
| 	nadproblem: Problem | ||||
| 
 | ||||
| 	def setup(self, request, *args, **kwargs): | ||||
| 		super().setup(request, *args, **kwargs) | ||||
| 		nadproblem_id = self.kwargs["nadproblem_id"] | ||||
| 		self.nadproblem = get_object_or_404(m.Problem, id=nadproblem_id) | ||||
| 		self.nadproblem = get_object_or_404(Problem, id=nadproblem_id) | ||||
| 
 | ||||
| 	def get(self, request, *args, **kwargs): | ||||
| 		# Zaříznutí nezadaných problémů | ||||
| 		if self.nadproblem.stav != m.Problem.STAV_ZADANY: | ||||
| 		if self.nadproblem.stav != Problem.STAV_ZADANY: | ||||
| 			raise PermissionDenied() | ||||
| 
 | ||||
| 
 | ||||
| 		# Zaříznutí starých řešitelů: | ||||
| 		# FIXME: Je to tady dost naprasené, mělo by to asi být jinde… | ||||
| 		osoba = m.Osoba.objects.get(user=self.request.user) | ||||
| 		osoba = Osoba.objects.get(user=self.request.user) | ||||
| 		resitel = osoba.resitel | ||||
| 		if resitel.rok_maturity <= m.Nastaveni.get_solo().aktualni_rocnik.prvni_rok: | ||||
| 		if resitel.rok_maturity <= Nastaveni.get_solo().aktualni_rocnik.prvni_rok: | ||||
| 			return render(request, 'universal.html', { | ||||
| 				'title': 'Nelze odevzdat', | ||||
| 				'error': 'Zdá se, že jsi již odmaturoval/a, a tedy nemůžeš odevzdat do našeho semináře řešení.', | ||||
|  | @ -445,7 +449,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): | |||
| 		nadproblem_id = self.nadproblem.id | ||||
| 		return { | ||||
| 			"nadproblem_id": nadproblem_id, | ||||
| 			"problem": [] if self.nadproblem.podproblem.filter(stav=m.Problem.STAV_ZADANY).exists() else nadproblem_id | ||||
| 			"problem": [] if self.nadproblem.podproblem.filter(stav=Problem.STAV_ZADANY).exists() else nadproblem_id | ||||
| 
 | ||||
| 		} | ||||
| 
 | ||||
|  | @ -457,7 +461,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): | |||
| 			data['prilohy'] = f.ReseniSPrilohamiFormSet() | ||||
| 
 | ||||
| 		data["nadproblem_id"] = self.nadproblem.id | ||||
| 		data["nadproblem"] = get_object_or_404(m.Problem, id=self.nadproblem.id) | ||||
| 		data["nadproblem"] = get_object_or_404(Problem, id=self.nadproblem.id) | ||||
| 		return data | ||||
| 
 | ||||
| 	# FIXME prepsat tak, aby form_valid se volalo jen tehdy, kdyz je form i formset validni | ||||
|  | @ -469,17 +473,17 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): | |||
| 			return super().form_invalid(form) | ||||
| 		with transaction.atomic(): | ||||
| 			self.object = form.save() | ||||
| 			self.object.resitele.add(m.Resitel.objects.get(osoba__user = self.request.user)) | ||||
| 			self.object.resitele.add(Resitel.objects.get(osoba__user = self.request.user)) | ||||
| 			self.object.resitele.add(*form.cleaned_data["resitele"]) | ||||
| 			self.object.cas_doruceni = timezone.now() | ||||
| 			self.object.forma = m.Reseni.FORMA_UPLOAD | ||||
| 			self.object.forma = Reseni.FORMA_UPLOAD | ||||
| 			self.object.save() | ||||
| 
 | ||||
| 			prilohy.instance = self.object | ||||
| 			prilohy.save() | ||||
| 
 | ||||
| 		for hodnoceni in self.object.hodnoceni_set.all(): | ||||
| 			hodnoceni.deadline_body = m.Deadline.objects.filter(deadline__gte=self.object.cas_doruceni).first() | ||||
| 			hodnoceni.deadline_body = Deadline.objects.filter(deadline__gte=self.object.cas_doruceni).first() | ||||
| 			hodnoceni.save() | ||||
| 
 | ||||
| 		# Pošleme mail opravovatelům a garantovi | ||||
|  | @ -497,7 +501,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): | |||
| 		# FIXME: Víc informativní obsah mailů, možná vč. příloh? | ||||
| 		prijemci = map(lambda it: it.osoba.email, prijemci) | ||||
| 
 | ||||
| 		resitel = m.Osoba.objects.get(user = self.request.user) | ||||
| 		resitel = Osoba.objects.get(user = self.request.user) | ||||
| 
 | ||||
| 		seznam = "problému " + str(problemy[0]) if len(problemy) == 1 else 'následujícím problémům:\n' + ', \n'.join(map(str, problemy)) | ||||
| 		seznam_do_subjectu = "problému " + str(problemy[0]) + ("" if len(problemy) == 1 else f" (a dalším { len(problemy) - 1 })") | ||||
|  |  | |||
|  | @ -16,10 +16,12 @@ from django.db import transaction | |||
| from django.http import HttpResponse | ||||
| from django.utils import timezone | ||||
| 
 | ||||
| import seminar.models as s | ||||
| 
 | ||||
| import personalni.models as m | ||||
| from soustredeni.models import Soustredeni | ||||
| from odevzdavatko.models import Hodnoceni | ||||
| from tvorba.models import Clanek, Uloha, Tema | ||||
| from various.models import Nastaveni | ||||
| from .forms import PrihlaskaForm, ProfileEditForm, PoMaturiteProfileEditForm | ||||
| 
 | ||||
| from datetime import date | ||||
|  | @ -94,7 +96,7 @@ class OrgoRozcestnikView(TemplateView): | |||
| 	def get_context_data(self, **kwargs): | ||||
| 		context = super().get_context_data(**kwargs) | ||||
| 		context['posledni_soustredeni'] = Soustredeni.objects.order_by('-datum_konce').first() | ||||
| 		nastaveni = s.Nastaveni.objects.first() | ||||
| 		nastaveni = Nastaveni.objects.first() | ||||
| 		aktualni_rocnik = nastaveni.aktualni_rocnik | ||||
| 		context['posledni_cislo_url'] = nastaveni.aktualni_cislo.verejne_url() | ||||
| 		# TODO možná chceme odkazovat na právě rozpracované číslo, a ne to poslední vydané | ||||
|  | @ -118,11 +120,11 @@ class OrgoRozcestnikView(TemplateView): | |||
| 		context["pocty_neopravenych_reseni"] = [(it['problem__nazev'], it['cas'].date) for it in pocty_neopravenych_reseni.all()] | ||||
| 
 | ||||
| 		#FIXME: přidat stav='STAV_ZADANY' | ||||
| 		temata = s.Tema.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]), | ||||
| 		temata = Tema.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]), | ||||
| 			rocnik=aktualni_rocnik).distinct() | ||||
| 		ulohy = s.Uloha.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]), | ||||
| 		ulohy = Uloha.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]), | ||||
| 			cislo_zadani__rocnik=aktualni_rocnik).distinct() | ||||
| 		clanky = s.Clanek.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]), | ||||
| 		clanky = Clanek.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]), | ||||
| 			cislo__rocnik=aktualni_rocnik).distinct() | ||||
| 
 | ||||
| 		context['temata'] = temata | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ from django.conf import settings | |||
| from personalni.models import Resitel, Organizator | ||||
| 
 | ||||
| from seminar.models.base import SeminarModelBase | ||||
| import tvorba.models as am | ||||
| from tvorba.models import Rocnik, Problem, aux_generate_filename | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
|  | @ -27,7 +27,7 @@ class Soustredeni(SeminarModelBase): | |||
| 	# Interní ID | ||||
| 	id = models.AutoField(primary_key = True) | ||||
| 
 | ||||
| 	rocnik = models.ForeignKey(am.Rocnik, verbose_name='ročník', related_name='soustredeni', | ||||
| 	rocnik = models.ForeignKey(Rocnik, verbose_name='ročník', related_name='soustredeni', | ||||
| 		on_delete=models.PROTECT) | ||||
| 
 | ||||
| 	datum_zacatku = models.DateField('datum začátku', blank=True, null=True, | ||||
|  | @ -143,13 +143,13 @@ class Soustredeni_Organizatori(SeminarModelBase): | |||
| def generate_filename_konfera(self, filename): | ||||
| 	return os.path.join( | ||||
| 		settings.SEMINAR_KONFERY_DIR, | ||||
| 		am.aux_generate_filename(self, filename) | ||||
| 		aux_generate_filename(self, filename) | ||||
| 	) | ||||
| 
 | ||||
| ## | ||||
| 
 | ||||
| @reversion.register(ignore_duplicates=True) | ||||
| class Konfera(am.Problem): | ||||
| class Konfera(Problem): | ||||
| 	class Meta: | ||||
| 		db_table = 'seminar_konfera' | ||||
| 		verbose_name = 'Konfera' | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ from typing import Sequence | |||
| import lorem | ||||
| 
 | ||||
| from .models import Soustredeni, Konfera | ||||
| import seminar.models as am # tvorba | ||||
| from tvorba.models import Rocnik | ||||
| import personalni.models as pm | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
|  | @ -25,7 +25,7 @@ def gen_soustredeni( | |||
| 	for _ in range(1, 10):  # FIXME Tu range si změňte jak chcete, nevím, co přesně znamená size (asi Anet?) | ||||
| 		datum_zacatku = datetime.date(rnd.randint(2000, 2020), rnd.randint(1, 12), rnd.randint(1, 28)) | ||||
| 		working_sous = Soustredeni.objects.create( | ||||
| 			rocnik=am.Rocnik.objects.order_by('?').first(), | ||||
| 			rocnik=Rocnik.objects.order_by('?').first(), | ||||
| 			verejne_db=rnd.choice([True, False]), | ||||
| 			misto=rnd.choice(['Kremrolovice', 'Indiánov', 'U zmzliny', 'Vafláreň', 'Větrník', 'Horní Rakvička', 'Dolní cheesecake']), | ||||
| 			typ=rnd.choice(['jarni', 'podzimni', 'vikend']), | ||||
|  |  | |||
|  | @ -1,30 +1,30 @@ | |||
| from django import template | ||||
| from django.utils.safestring import mark_safe | ||||
| register = template.Library() | ||||
| import seminar.models as m | ||||
| from tvorba.models import Deadline | ||||
| 
 | ||||
| @register.filter(name='deadline_kratseji') | ||||
| def deadline_kratsi_text(deadline: m.Deadline): | ||||
| def deadline_kratsi_text(deadline: Deadline): | ||||
| 	if deadline is None: | ||||
| 		return 'NONE' | ||||
| 	strings = { | ||||
| 		m.Deadline.TYP_PRVNI: f"{deadline.cislo} ⭯", | ||||
| 		m.Deadline.TYP_SOUS: f"{deadline.cislo} Ⓢ", | ||||
| 		m.Deadline.TYP_PRVNI_A_SOUS: f"{deadline.cislo} ⭯Ⓢ", | ||||
| 		m.Deadline.TYP_CISLA: f"{deadline.cislo} ✓", | ||||
| 		Deadline.TYP_PRVNI: f"{deadline.cislo} ⭯", | ||||
| 		Deadline.TYP_SOUS: f"{deadline.cislo} Ⓢ", | ||||
| 		Deadline.TYP_PRVNI_A_SOUS: f"{deadline.cislo} ⭯Ⓢ", | ||||
| 		Deadline.TYP_CISLA: f"{deadline.cislo} ✓", | ||||
| 		} | ||||
| 	return strings[deadline.typ] | ||||
| 
 | ||||
| @register.filter(name='deadline_html') | ||||
| def deadline_html(deadline: m.Deadline): | ||||
| def deadline_html(deadline: Deadline): | ||||
| 	if deadline is None: | ||||
| 		return 'Neznámý deadline' | ||||
| 	text = deadline_kratsi_text(deadline) | ||||
| 	classes = { | ||||
| 		m.Deadline.TYP_PRVNI: 'preddeadline', | ||||
| 		m.Deadline.TYP_SOUS: 'sous_deadline', | ||||
| 		m.Deadline.TYP_PRVNI_A_SOUS: 'sous_deadline', | ||||
| 		m.Deadline.TYP_CISLA: 'final_deadline', | ||||
| 		Deadline.TYP_PRVNI: 'preddeadline', | ||||
| 		Deadline.TYP_SOUS: 'sous_deadline', | ||||
| 		Deadline.TYP_PRVNI_A_SOUS: 'sous_deadline', | ||||
| 		Deadline.TYP_CISLA: 'final_deadline', | ||||
| 		} | ||||
| 	return mark_safe(f'<span class="{classes[deadline.typ]}" title="{deadline}">{text}</span>') | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,7 +6,9 @@ import lorem | |||
| import django.contrib.auth | ||||
| import logging | ||||
| 
 | ||||
| from seminar.models import Rocnik, Cislo, Deadline, Problem, Tema, Uloha, TextNode, UlohaVzorakNode, RocnikNode, CisloNode, TemaVCisleNode, Text, UlohaZadaniNode | ||||
| from .models import Rocnik, Cislo, Deadline, Problem, Tema, Uloha | ||||
| 
 | ||||
| from odevzdavatko.models import Reseni, Hodnoceni | ||||
| import seminar.models as m | ||||
| 
 | ||||
| from treenode.treelib import all_children, insert_last_child, all_children_of_type, create_node_after | ||||
|  | @ -54,12 +56,12 @@ def gen_zadani_ulohy(rnd, cisla, organizatori, pocet_oboru, poradi_cisla, poradi | |||
| 		rnd.choice(jmeno), | ||||
| 		rnd.choice(kde)] | ||||
| 		) | ||||
| 	text_zadani = Text.objects.create( | ||||
| 	text_zadani = m.Text.objects.create( | ||||
| 		na_web = text, | ||||
| 		do_cisla = text, | ||||
| 	) | ||||
| 	zad = TextNode.objects.create(text = text_zadani, root = p.cislo_zadani.rocnik.rocniknode) | ||||
| 	uloha_zadani = UlohaZadaniNode.objects.create(uloha = p, first_child = zad, root = p.cislo_zadani.rocnik.rocniknode) | ||||
| 	zad = m.TextNode.objects.create(text = text_zadani, root = p.cislo_zadani.rocnik.rocniknode) | ||||
| 	uloha_zadani = m.UlohaZadaniNode.objects.create(uloha = p, first_child = zad, root = p.cislo_zadani.rocnik.rocniknode) | ||||
| 	p.ulohazadaninode = uloha_zadani | ||||
| 	otec_syn(cisla[poradi_cisla-2-1].cislonode, uloha_zadani) | ||||
| 
 | ||||
|  | @ -76,12 +78,12 @@ def gen_vzoroveho_reseni_ulohy(rnd, organizatori, uloha, pocet_opravovatelu): | |||
| 
 | ||||
| 	# Generování vzorového řešení. | ||||
| 	obsah = rnd.choice(reseni) | ||||
| 	text_vzoraku = Text.objects.create( | ||||
| 	text_vzoraku = m.Text.objects.create( | ||||
| 		na_web = obsah, | ||||
| 		do_cisla = obsah | ||||
| 	) | ||||
| 	vzorak = TextNode.objects.create(text = text_vzoraku, root = uloha.cislo_zadani.rocnik.rocniknode) | ||||
| 	uloha_vzorak = UlohaVzorakNode.objects.create(uloha = uloha, first_child = vzorak, root = uloha.cislo_zadani.rocnik.rocniknode) | ||||
| 	vzorak = m.TextNode.objects.create(text = text_vzoraku, root = uloha.cislo_zadani.rocnik.rocniknode) | ||||
| 	uloha_vzorak = m.UlohaVzorakNode.objects.create(uloha = uloha, first_child = vzorak, root = uloha.cislo_zadani.rocnik.rocniknode) | ||||
| 	uloha.ulohavzoraknode = uloha_vzorak | ||||
| 
 | ||||
| 	uloha.opravovatele.set(rnd.sample(organizatori, pocet_opravovatelu)) | ||||
|  | @ -132,7 +134,7 @@ def gen_rocniky(last_rocnik, size): | |||
| 	node = None | ||||
| 	for ri in range(min(last_rocnik - size, 1), last_rocnik + 1): | ||||
| 		rocnik = Rocnik.objects.create(prvni_rok = 1993 + ri, rocnik = ri) | ||||
| 		node2 = RocnikNode.objects.create(rocnik = rocnik, succ = node) | ||||
| 		node2 = m.RocnikNode.objects.create(rocnik = rocnik, succ = node) | ||||
| 		rocnik.save() | ||||
| 		node = node2 | ||||
| 		rocniky.append(rocnik) | ||||
|  | @ -167,7 +169,7 @@ def gen_cisla(rnd, rocniky): | |||
| 				datum_vydani=vydano, | ||||
| 				verejne_db=True, | ||||
| 			) | ||||
| 			node2 = CisloNode.objects.get(cislo = cislo) | ||||
| 			node2 = m.CisloNode.objects.get(cislo = cislo) | ||||
| 			node2.succ = node | ||||
| 			node2.root = rocnik.rocniknode | ||||
| 			cislo.save() | ||||
|  | @ -195,7 +197,7 @@ def add_first_child(node, child): | |||
| 
 | ||||
| def get_text(): | ||||
| 	odstavec = lorem.paragraph() | ||||
| 	return Text.objects.create(na_web = odstavec, do_cisla = odstavec)	 | ||||
| 	return m.Text.objects.create(na_web = odstavec, do_cisla = odstavec) | ||||
| 
 | ||||
| def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod): | ||||
| 	tema = Tema.objects.create( | ||||
|  | @ -215,32 +217,32 @@ def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod): | |||
| 	for cislo in cisla: | ||||
| 		# Přidáme TemaVCisleNode do daného čísla | ||||
| 		cislo_node = cislo.cislonode	 | ||||
| 		tema_cislo_node = TemaVCisleNode.objects.create(tema = tema, root = cislo_node.root) | ||||
| 		tema_cislo_node = m.TemaVCisleNode.objects.create(tema = tema, root = cislo_node.root) | ||||
| 		insert_last_child(cislo_node, tema_cislo_node) | ||||
| 		 | ||||
| 		# Přidávání obsahu do čísla | ||||
| 		cast_node = m.CastNode.objects.create(nadpis = "Příspěvek k číslu {}".format(cislo.kod), root=cislo_node.root) | ||||
| 		add_first_child(tema_cislo_node, cast_node) | ||||
| 	 | ||||
| 		text_node = TextNode.objects.create(text = get_text(), root=cislo_node.root) | ||||
| 		text_node = m.TextNode.objects.create(text = get_text(), root=cislo_node.root) | ||||
| 		add_first_child(cast_node, text_node) | ||||
| 
 | ||||
| 		cast_node2 = m.CastNode.objects.create(nadpis = "První podproblém", root=cislo_node.root) | ||||
| 		add_first_child(text_node, cast_node2) | ||||
| 		 | ||||
| 		text_node2 = TextNode.objects.create(text = get_text(), root=cislo_node.root) | ||||
| 		text_node2 = m.TextNode.objects.create(text = get_text(), root=cislo_node.root) | ||||
| 		add_first_child(cast_node2, text_node2) | ||||
| 		 | ||||
| 		cast_node3 = m.CastNode.objects.create(nadpis = "Druhý podproblém", root=cislo_node.root) | ||||
| 		add_first_child(text_node2, cast_node3) | ||||
| 
 | ||||
| 		text_node3 = TextNode.objects.create(text = get_text(), root=cislo_node.root) | ||||
| 		text_node3 = m.TextNode.objects.create(text = get_text(), root=cislo_node.root) | ||||
| 		add_first_child(cast_node3, text_node3) | ||||
| 
 | ||||
| 		cast_node4 = m.CastNode.objects.create(nadpis = "Třetí podproblém", root=cislo_node.root) | ||||
| 		add_first_child(text_node3, cast_node4)	 | ||||
| 
 | ||||
| 		text_node4 = TextNode.objects.create(text = get_text(), root=cislo_node.root) | ||||
| 		text_node4 = m.TextNode.objects.create(text = get_text(), root=cislo_node.root) | ||||
| 		add_first_child(cast_node3, text_node4) | ||||
| 		 | ||||
| 		cast_node3a = m.CastNode.objects.create(nadpis = "Podproblém paralelní s " | ||||
|  | @ -248,7 +250,7 @@ def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod): | |||
| 		cast_node3.succ = cast_node3a | ||||
| 		cast_node3.save() | ||||
| 
 | ||||
| 		text_node3a = TextNode.objects.create(text = get_text(), root=cislo_node.root) | ||||
| 		text_node3a = m.TextNode.objects.create(text = get_text(), root=cislo_node.root) | ||||
| 		add_first_child(cast_node3a, text_node3a) | ||||
| 
 | ||||
| 		# Občas přidáme mezičíslo | ||||
|  | @ -261,8 +263,8 @@ def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod): | |||
| 			add_first_child(mezicislo_node, cast_node_mezicislo) | ||||
| 
 | ||||
| 			odstavec = lorem.paragraph() | ||||
| 			text_mezicislo = Text.objects.create(na_web = odstavec, do_cisla = odstavec) | ||||
| 			text_node_mezicislo = TextNode.objects.create(text = text_mezicislo, root=cislo_node.root) | ||||
| 			text_mezicislo = m.Text.objects.create(na_web = odstavec, do_cisla = odstavec) | ||||
| 			text_node_mezicislo = m.TextNode.objects.create(text = text_mezicislo, root=cislo_node.root) | ||||
| 			add_first_child(cast_node_mezicislo, text_node_mezicislo) | ||||
| 
 | ||||
| 	return tema | ||||
|  | @ -306,7 +308,7 @@ def gen_temata(rnd, rocniky, rocnik_cisla, organizatori): | |||
| 
 | ||||
| 			# Vyrobíme TemaVCisleNody pro obsah | ||||
| 			for i in range(zacatek_tematu, konec_tematu+1): | ||||
| 				node = TemaVCisleNode.objects.create(tema = t,root=rocnik.rocniknode) | ||||
| 				node = m.TemaVCisleNode.objects.create(tema = t,root=rocnik.rocniknode) | ||||
| 				# FIXME: Není to off-by-one? | ||||
| 				otec = cisla[i-1].cislonode | ||||
| 				otec_syn(otec, node) | ||||
|  | @ -359,12 +361,12 @@ def gen_ulohy_tematu(rnd, organizatori, resitele, tema, kod, cislo, cislo_se_vzo | |||
| 		rnd.choice(jmeno),  | ||||
| 		rnd.choice(kde)] | ||||
| 		) | ||||
| 	text_zadani = Text.objects.create( | ||||
| 	text_zadani = m.Text.objects.create( | ||||
| 		na_web = obsah, | ||||
| 		do_cisla = obsah, | ||||
| 		) | ||||
| 	zad = TextNode.objects.create(text = text_zadani, root=tema.temavcislenode_set.first().root) | ||||
| 	uloha_zadani = UlohaZadaniNode.objects.create(uloha=uloha, first_child = zad, root=tema.temavcislenode_set.first().root) | ||||
| 	zad = m.TextNode.objects.create(text = text_zadani, root=tema.temavcislenode_set.first().root) | ||||
| 	uloha_zadani = m.UlohaZadaniNode.objects.create(uloha=uloha, first_child = zad, root=tema.temavcislenode_set.first().root) | ||||
| 	uloha.ulohazadaninode = uloha_zadani | ||||
| 
 | ||||
| 	# Generování řešení a hodnocení k úloze | ||||
|  | @ -396,7 +398,7 @@ def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori, | |||
| 			else: | ||||
| 				cislo_se_vzorakem = cislo_se_vzorakem.first() | ||||
| 
 | ||||
| 			for tema_node in all_children_of_type(cislo.cislonode, TemaVCisleNode): | ||||
| 			for tema_node in all_children_of_type(cislo.cislonode, m.TemaVCisleNode): | ||||
| 				tema = tema_node.tema | ||||
| 					 | ||||
| 				# Pokud už témátko skončilo, žádné úložky negenerujeme | ||||
|  | @ -419,7 +421,7 @@ def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori, | |||
| 					# Najdeme správný TemaVCisleNode pro vložení vzoráku | ||||
| 					res_tema_node = None; | ||||
| 					for node in all_children(cislo_se_vzorakem.cislonode): | ||||
| 						if isinstance(node, TemaVCisleNode):  | ||||
| 						if isinstance(node, m.TemaVCisleNode): | ||||
| 							if node.tema == tema: | ||||
| 								res_tema_node = node | ||||
| 					if res_tema_node is None: | ||||
|  | @ -448,16 +450,16 @@ def gen_clanek(rnd, organizatori, resitele): | |||
| 		) | ||||
| 	clanek.save() | ||||
| 
 | ||||
| 	reseni = m.Reseni.objects.create( | ||||
| 	reseni = Reseni.objects.create( | ||||
| 		zverejneno=True, | ||||
| 		) | ||||
| 	reseni.resitele.add(rnd.choice(resitele)) | ||||
| 	reseni.save() | ||||
| 
 | ||||
| 	cislo = m.Cislo.objects.get(rocnik__rocnik=22, poradi=2) | ||||
| 	cislo = Cislo.objects.get(rocnik__rocnik=22, poradi=2) | ||||
| 	cislonode = cislo.cislonode | ||||
| 
 | ||||
| 	hodnoceni = m.Hodnoceni.objects.create( | ||||
| 	hodnoceni = Hodnoceni.objects.create( | ||||
| 		body=15.0, | ||||
| 		cislo_body=cislo, | ||||
| 		reseni=reseni, | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ from django.core.exceptions import ObjectDoesNotExist | |||
| 
 | ||||
| import personalni.models | ||||
| 
 | ||||
| import seminar.models as m | ||||
| import tvorba.models as m | ||||
| 
 | ||||
| 
 | ||||
| def resi_v_rocniku(rocnik, cislo=None): | ||||
|  |  | |||
|  | @ -14,12 +14,11 @@ from django.db.models import Q, Sum, Count | |||
| from django.views.generic.base import RedirectView | ||||
| from django.core.exceptions import PermissionDenied | ||||
| 
 | ||||
| import seminar.models as s | ||||
| import seminar.models as m | ||||
| from seminar.models import Problem, Cislo, Reseni, Nastaveni, Rocnik, \ | ||||
| 	Resitel, Novinky, Tema, Clanek, \ | ||||
| 	Deadline  # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci | ||||
| #from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva | ||||
| from personalni.models import Resitel | ||||
| from soustredeni.models import Konfera | ||||
| from tvorba.models import Problem, Cislo, Rocnik, Tema, Clanek, Deadline, Uloha | ||||
| from various.models import Nastaveni | ||||
| from treenode import treelib | ||||
| import treenode.templatetags as tnltt | ||||
| import treenode.serializers as vr | ||||
|  | @ -58,7 +57,7 @@ def get_problemy_k_tematu(tema): | |||
| 
 | ||||
| # FIXME: Pozor, níž je ještě jeden ProblemView! | ||||
| #class ProblemView(generic.DetailView): | ||||
| #	model = s.Problem | ||||
| #	model = Problem | ||||
| #	# Zkopírujeme template_name od TreeNodeView, protože jsme prakticky jen trošku upravený TreeNodeView | ||||
| #	template_name = TreeNodeView.template_name | ||||
| # | ||||
|  | @ -70,17 +69,17 @@ def get_problemy_k_tematu(tema): | |||
| #		if False: | ||||
| #			# Hezčí formátování zbytku :-P | ||||
| #			pass | ||||
| #		elif isinstance(self.object, s.Clanek) or  isinstance(self.object, s.Konfera): | ||||
| #		elif isinstance(self.object, Clanek) or  isinstance(self.object, Konfera): | ||||
| #			# Tyhle Problémy mají ŘešeníNode | ||||
| #			context['tnldata'] = TNLData.from_treenode(self.object.reseninode,user) | ||||
| #		elif isinstance(self.object, s.Uloha): | ||||
| #		elif isinstance(self.object, Uloha): | ||||
| #			# FIXME: Teď vždycky zobrazujeme i vzorák! Možná by bylo hezčí/lepší mít to stejně jako pro Téma: procházet jen dosažitelné z Ročníku / čísla / whatever | ||||
| #			tnl_zadani = TNLData.from_treenode(self.object.ulohazadaninode,user) | ||||
| #			tnl_vzorak = TNLData.from_treenode(self.object.ulohavzoraknode,user) | ||||
| #			context['tnldata'] = TNLData.from_tnldata_list([tnl_zadani, tnl_vzorak]) | ||||
| #		elif isinstance(self.object, s.Tema): | ||||
| #		elif isinstance(self.object, Tema): | ||||
| #			rocniknode = self.object.rocnik.rocniknode | ||||
| #			context['tnldata'] = TNLData.filter_treenode(rocniknode, lambda x: isinstance(x, s.TemaVCisleNode)) | ||||
| #			context['tnldata'] = TNLData.filter_treenode(rocniknode, lambda x: isinstance(x, m.TemaVCisleNode)) | ||||
| #		else: | ||||
| #			raise ValueError("Obecný problém nejde zobrazit.") | ||||
| #		return context | ||||
|  | @ -115,7 +114,7 @@ def ZadaniTemataView(request): | |||
| 	nastaveni = get_object_or_404(Nastaveni) | ||||
| 	verejne = nastaveni.aktualni_cislo.verejne() | ||||
| 	akt_rocnik = nastaveni.aktualni_cislo.rocnik | ||||
| 	temata = s.Tema.objects.filter(rocnik=akt_rocnik, stav='zadany') | ||||
| 	temata = Tema.objects.filter(rocnik=akt_rocnik, stav='zadany') | ||||
| 	return render(request, 'tvorba/tematka/rozcestnik.html', | ||||
| 				  { | ||||
| 					  'tematka': temata, | ||||
|  | @ -140,14 +139,14 @@ def ZadaniTemataView(request): | |||
| # | ||||
| # | ||||
| #def TematkoView(request, rocnik, tematko): | ||||
| #	nastaveni = s.Nastaveni.objects.first() | ||||
| #	rocnik_object = s.Rocnik.objects.filter(rocnik=rocnik) | ||||
| #	tematko_object = s.Tema.objects.filter(rocnik=rocnik_object[0], kod=tematko) | ||||
| #	nastaveni = Nastaveni.objects.first() | ||||
| #	rocnik_object = Rocnik.objects.filter(rocnik=rocnik) | ||||
| #	tematko_object = Tema.objects.filter(rocnik=rocnik_object[0], kod=tematko) | ||||
| #	seznam = vytahniZLesaSeznam(tematko_object[0], nastaveni.aktualni_rocnik().rocniknode) | ||||
| #	for node, depth in seznam: | ||||
| #		if node.isinstance(node, s.KonferaNode): | ||||
| #		if node.isinstance(node, m.KonferaNode): | ||||
| #			raise Exception("Not implemented yet") | ||||
| #		if node.isinstance(node, s.PohadkaNode): # Mohu ignorovat, má pod sebou | ||||
| #		if node.isinstance(node, m.PohadkaNode): # Mohu ignorovat, má pod sebou | ||||
| #			pass | ||||
| # | ||||
| #	return render(request, 'tvorba/tematka/toaletak.html', {}) | ||||
|  | @ -155,8 +154,8 @@ def ZadaniTemataView(request): | |||
| # | ||||
| #def TemataRozcestnikView(request): | ||||
| #	print("=============================================") | ||||
| #	nastaveni = s.Nastaveni.objects.first() | ||||
| #	tematka_objects = s.Tema.objects.filter(rocnik=nastaveni.aktualni_rocnik()) | ||||
| #	nastaveni = Nastaveni.objects.first() | ||||
| #	tematka_objects = Tema.objects.filter(rocnik=nastaveni.aktualni_rocnik()) | ||||
| #	tematka = [] #List tematka obsahuje pro kazde tematko object a list vsech TemaVCisleNodu - implementované pomocí slovníku | ||||
| #	for tematko_object in tematka_objects: | ||||
| #		print("AKTUALNI TEMATKO") | ||||
|  | @ -278,7 +277,7 @@ def resiteleRocnikuCsvExportView(request, rocnik): | |||
| 	assert request.method in ('GET', 'HEAD') | ||||
| 	return dataResiteluCsvResponse( | ||||
| 		utils.resi_v_rocniku( | ||||
| 			get_object_or_404(m.Rocnik, rocnik=rocnik) | ||||
| 			get_object_or_404(Rocnik, rocnik=rocnik) | ||||
| 		) | ||||
| 	) | ||||
| 
 | ||||
|  | @ -291,10 +290,10 @@ def resiteleRocnikuCsvExportView(request, rocnik): | |||
| #	def get_template_names(self, **kwargs): | ||||
| #		# FIXME: Switch podle typu není hezký, ale nechtělo se mi to přepisovat celé. Správně by se tohle mělo řešit polymorfismem. | ||||
| #		spravne_templaty = { | ||||
| #				s.Uloha: "uloha", | ||||
| #				s.Tema: "tema", | ||||
| #				s.Konfera: "konfera", | ||||
| #				s.Clanek: "clanek", | ||||
| #				Uloha: "uloha", | ||||
| #				Tema: "tema", | ||||
| #				Konfera: "konfera", | ||||
| #				Clanek: "clanek", | ||||
| #				} | ||||
| #		context = super().get_context_data(**kwargs) | ||||
| #		return ['tvorba/archiv/problem_' + spravne_templaty[context['object'].__class__]  + '.html'] | ||||
|  | @ -340,10 +339,10 @@ class CisloView(generic.DetailView): | |||
| 		deadliny_s_vysledkovkami = [] | ||||
| 
 | ||||
| 		nadpisy = { | ||||
| 			m.Deadline.TYP_CISLA: "Výsledkovka", | ||||
| 			m.Deadline.TYP_PRVNI: "Výsledkovka do prvního deadlinu", | ||||
| 			m.Deadline.TYP_PRVNI_A_SOUS: "Výsledkovka do prvního deadlinu a deadlinu pro účast na soustředění", | ||||
| 			m.Deadline.TYP_SOUS: "Výsledkovka do deadlinu pro účast na soustředění", | ||||
| 			Deadline.TYP_CISLA: "Výsledkovka", | ||||
| 			Deadline.TYP_PRVNI: "Výsledkovka do prvního deadlinu", | ||||
| 			Deadline.TYP_PRVNI_A_SOUS: "Výsledkovka do prvního deadlinu a deadlinu pro účast na soustředění", | ||||
| 			Deadline.TYP_SOUS: "Výsledkovka do deadlinu pro účast na soustředění", | ||||
| 		} | ||||
| 
 | ||||
| 		for deadline in deadliny: | ||||
|  | @ -580,5 +579,5 @@ class AktualniRocnikRedirectView(RedirectView): | |||
| 	pattern_name = 'seminar_rocnik' | ||||
| 
 | ||||
| 	def get_redirect_url(self, *args, **kwargs): | ||||
| 		aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik.rocnik | ||||
| 		aktualni_rocnik = Nastaveni.get_solo().aktualni_rocnik.rocnik | ||||
| 		return super().get_redirect_url(rocnik=aktualni_rocnik, *args, **kwargs) | ||||
|  |  | |||
|  | @ -5,13 +5,13 @@ from django.db import transaction | |||
| from django.forms import Form, CharField, IntegerField | ||||
| from django.views.generic import FormView | ||||
| 
 | ||||
| import seminar.models as m | ||||
| from tvorba.models import Cislo, Problem, Uloha, Tema | ||||
| from django.shortcuts import render, get_object_or_404 | ||||
| 
 | ||||
| def problemView(request, pk): | ||||
| 	# Pokud problém neexistuje, hodíme obyčejnou 404 | ||||
| 	# Taktéž v případě, že takový problém nemá být vidět | ||||
| 	problem = get_object_or_404(m.Problem, id=pk, stav__in=[m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY]) | ||||
| 	problem = get_object_or_404(Problem, id=pk, stav__in=[Problem.STAV_ZADANY, Problem.STAV_VYRESENY]) | ||||
| 	# Problém existuje, neumíme ho zobrazit, renderujeme nějakou haluz | ||||
| 	template = 'universal.html' | ||||
| 	ctx = { | ||||
|  | @ -32,7 +32,7 @@ class HromadnePridaniForm(Form): | |||
| 
 | ||||
| 	def clean_tema(self): | ||||
| 		""" Kontrola, že `tema` je název právě jednoho tématu """ | ||||
| 		if m.Tema.objects.filter( | ||||
| 		if Tema.objects.filter( | ||||
| 				nazev__exact=self.cleaned_data['tema'], | ||||
| 				nadproblem=None).count() != 1: | ||||
| 			raise ValidationError("Špatný nebo nepřesně zadaný název témátka") | ||||
|  | @ -67,20 +67,20 @@ class HromadnePridaniView(FormView): | |||
| 		dil = cd["dil"] | ||||
| 		body = list(map(int, cd["body"].split(","))) | ||||
| 
 | ||||
| 		t = m.Problem.objects.get(nazev__exact=tema, nadproblem=None) | ||||
| 		t = Problem.objects.get(nazev__exact=tema, nadproblem=None) | ||||
| 		with transaction.atomic(): | ||||
| 			pfx = f"{t.nazev}, díl {dil}, " | ||||
| 
 | ||||
| 			for k, b in enumerate(body, 1): | ||||
| 				u = m.Uloha.objects.create( | ||||
| 				u = Uloha.objects.create( | ||||
| 					nadproblem=t, | ||||
| 					nazev=pfx + f"{'úloha' if b > 0 else 'problém'} {k}", | ||||
| 					autor=t.autor, | ||||
| 					garant=t.garant, | ||||
| 					max_body=b, | ||||
| 					cislo_zadani=m.Cislo.get(t.rocnik.rocnik, dil), | ||||
| 					cislo_zadani=Cislo.get(t.rocnik.rocnik, dil), | ||||
| 					kod=k, | ||||
| 					stav=m.Problem.STAV_ZADANY, | ||||
| 					stav=Problem.STAV_ZADANY, | ||||
| 				) | ||||
| 				u.opravovatele.set(t.opravovatele.all()) | ||||
| 		return super().form_valid(form) | ||||
|  |  | |||
|  | @ -55,5 +55,4 @@ class PasswordResetCompleteView(auth_views.PasswordResetCompleteView): | |||
| 
 | ||||
| 
 | ||||
| class PasswordChangeView(auth_views.PasswordChangeView): | ||||
| 	# template_name = 'seminar/password_change.html' | ||||
| 	success_url = reverse_lazy('titulni_strana') | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| from django.core.management.base import BaseCommand | ||||
| 
 | ||||
| from seminar.models import Cislo | ||||
| from tvorba.models import Cislo | ||||
| 
 | ||||
| from subprocess import CalledProcessError | ||||
| import logging | ||||
|  |  | |||
|  | @ -1,11 +1,11 @@ | |||
| from django.core.management.base import BaseCommand | ||||
| import seminar.models as m | ||||
| from tvorba.models import Deadline | ||||
| 
 | ||||
| 
 | ||||
| class Command(BaseCommand): | ||||
| 	help = "Všem deadlinům se zveřejněnou výsledkovkou vygeneruj výsledkovku" | ||||
| 
 | ||||
| 	def handle(self, *args, **options): | ||||
| 		for deadline in m.Deadline.objects.filter(verejna_vysledkovka=True): | ||||
| 		for deadline in Deadline.objects.filter(verejna_vysledkovka=True): | ||||
| 			deadline.vygeneruj_vysledkovku() | ||||
| 		 | ||||
|  |  | |||
|  | @ -4,7 +4,9 @@ from django.core.management.base import BaseCommand | |||
| from django.core.management import call_command | ||||
| from django.conf import settings | ||||
| 
 | ||||
| from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni | ||||
| from odevzdavatko.models import Reseni | ||||
| from personalni.models import Skola, Resitel | ||||
| from tvorba.models import Rocnik, Cislo, Problem | ||||
| from various.testutils import create_test_data | ||||
| import django.contrib.auth | ||||
| User = django.contrib.auth.get_user_model() | ||||
|  |  | |||
|  | @ -7,7 +7,9 @@ from django.contrib.flatpages.models import FlatPage | |||
| from django.contrib.sites.models import Site | ||||
| from django.db import transaction | ||||
| 
 | ||||
| from seminar.models import Rocnik, Cislo, Nastaveni, Osoba, Organizator | ||||
| from personalni.models import Osoba, Organizator | ||||
| from tvorba.models import Rocnik, Cislo | ||||
| from various.models import Nastaveni | ||||
| 
 | ||||
| from korektury.testutils import create_test_pdf | ||||
| from novinky.testutils import gen_novinky | ||||
|  |  | |||
|  | @ -14,8 +14,9 @@ from django.views import generic | |||
| import novinky.views | ||||
| import treenode.treelib as t | ||||
| import tvorba.views | ||||
| from personalni.models import Resitel | ||||
| from seminar import models as m | ||||
| import seminar.models as m | ||||
| from personalni.models import Resitel, Osoba | ||||
| from tvorba.models import Clanek, Deadline | ||||
| 
 | ||||
| from ..models import Nastaveni | ||||
| 
 | ||||
|  | @ -30,7 +31,7 @@ class TitulniStranaView(generic.ListView): | |||
| 		context = super(TitulniStranaView, self).get_context_data(**kwargs) | ||||
| 		nastaveni = get_object_or_404(Nastaveni) | ||||
| 
 | ||||
| 		deadline = m.Deadline.objects.filter( | ||||
| 		deadline = Deadline.objects.filter( | ||||
| 			deadline__gte=timezone.now()).order_by("deadline").first() | ||||
| 		context['nejblizsi_deadline'] = deadline | ||||
| 
 | ||||
|  | @ -93,31 +94,31 @@ def seznam_problemu(): | |||
| 
 | ||||
| 	# Duplicita jmen | ||||
| 	jmena = {} | ||||
| 	for r in m.Resitel.objects.all(): | ||||
| 	for r in Resitel.objects.all(): | ||||
| 		j = r.osoba.plne_jmeno() | ||||
| 		if j not in jmena: | ||||
| 			jmena[j] = [] | ||||
| 		jmena[j].append(r) | ||||
| 	for j in jmena: | ||||
| 		if len(jmena[j]) > 1: | ||||
| 			prb(m.Resitel, 'Duplicitní jméno "%s"' % (j,), jmena[j]) | ||||
| 			prb(Resitel, 'Duplicitní jméno "%s"' % (j,), jmena[j]) | ||||
| 
 | ||||
| 	# Data maturity a narození | ||||
| 	for r in m.Resitel.objects.all(): | ||||
| 	for r in Resitel.objects.all(): | ||||
| 		if not r.rok_maturity: | ||||
| 			prb(m.Resitel, 'Neznámý rok maturity', [r]) | ||||
| 			prb(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]) | ||||
| 			prb(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): | ||||
| 			prb(m.Resitel, 'Podezřelé datum narození', [r]) | ||||
| 			prb(Resitel, 'Podezřelé datum narození', [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(): | ||||
| 	for clanek in Clanek.objects.all(): | ||||
| 		# získáme řešení svázané se článkem a z něj node ve stromě | ||||
| 		reseni = clanek.reseni_set | ||||
| 		if (reseni.count() != 1): | ||||
|  | @ -136,7 +137,7 @@ def seznam_problemu(): | |||
| 				# .cislonode je opačná vazba k treenode_ptr, abychom z TreeNode dostali | ||||
| 				# CisloNode | ||||
| 				if clanek.cislo != node.cislonode.cislo: | ||||
| 					prb(m.Clanek, "Číslo otištění uložené u článku nesedí s " | ||||
| 					prb(Clanek, "Číslo otištění uložené u článku nesedí s " | ||||
| 								  "číslem otištění podle struktury treenodů.", [clanek]) | ||||
| 				break | ||||
| 			node = t.get_parent(node) | ||||
|  | @ -146,8 +147,8 @@ def seznam_problemu(): | |||
| def StavDatabazeView(request): | ||||
| 	# nastaveni = Nastaveni.objects.get() | ||||
| 	problemy = seznam_problemu() | ||||
| 	muzi = Resitel.objects.filter(osoba__osloveni=m.Osoba.OSLOVENI_MUZSKE) | ||||
| 	zeny = Resitel.objects.filter(osoba__osloveni=m.Osoba.OSLOVENI_ZENSKE) | ||||
| 	muzi = Resitel.objects.filter(osoba__osloveni=Osoba.OSLOVENI_MUZSKE) | ||||
| 	zeny = Resitel.objects.filter(osoba__osloveni=Osoba.OSLOVENI_ZENSKE) | ||||
| 	return render(request, 'various/stav_databaze.html', { | ||||
| 		# 'nastaveni': nastaveni, | ||||
| 		'problemy': problemy, | ||||
|  |  | |||
|  | @ -2,7 +2,10 @@ import abc | |||
| from functools import cached_property | ||||
| from typing import Union, Iterable  # TODO: s pythonem 3.10 přepsat na '|' | ||||
| 
 | ||||
| import seminar.models as m | ||||
| from odevzdavatko.models import Hodnoceni | ||||
| from personalni.models import Resitel | ||||
| from soustredeni.models import Konfera | ||||
| from tvorba.models import Cislo, Rocnik, Deadline, Problem, Clanek | ||||
| from django.db.models import Q, Sum | ||||
| from tvorba.utils import resi_v_rocniku | ||||
| 
 | ||||
|  | @ -18,11 +21,11 @@ class FixedIterator: | |||
| 
 | ||||
| 
 | ||||
| def body_resitelu( | ||||
| 		za: Union[m.Cislo, m.Rocnik, None] = None, | ||||
| 		do: m.Deadline = None, | ||||
| 		od: m.Deadline = None, | ||||
| 		za: Union[Cislo, Rocnik, None] = None, | ||||
| 		do: Deadline = None, | ||||
| 		od: Deadline = None, | ||||
| 		jen_verejne: bool = True, | ||||
| 		resitele: Iterable[m.Resitel] = None, | ||||
| 		resitele: Iterable[Resitel] = None, | ||||
| 		null=0 # Výchozí hodnota, pokud pro daného řešitele nejsou body | ||||
| ) -> dict[int, int]: | ||||
| 	filtr = Q() | ||||
|  | @ -31,9 +34,9 @@ def body_resitelu( | |||
| 		filtr &= Q(reseni__hodnoceni__deadline_body__verejna_vysledkovka=True) | ||||
| 
 | ||||
| 	# Zjistíme, typ objektu v parametru "za" | ||||
| 	if isinstance(za, m.Rocnik): | ||||
| 	if isinstance(za, Rocnik): | ||||
| 		filtr &= Q(reseni__hodnoceni__deadline_body__cislo__rocnik=za) | ||||
| 	elif isinstance(za, m.Cislo): | ||||
| 	elif isinstance(za, Cislo): | ||||
| 		filtr &= Q(reseni__hodnoceni__deadline_body__cislo=za) | ||||
| 
 | ||||
| 	if do: | ||||
|  | @ -42,7 +45,7 @@ def body_resitelu( | |||
| 	if od: | ||||
| 		filtr &= Q(reseni__hodnoceni__deadline_body__deadline__gte=od.deadline) | ||||
| 
 | ||||
| 	resiteleQuery = m.Resitel.objects.all() | ||||
| 	resiteleQuery = Resitel.objects.all() | ||||
| 
 | ||||
| 	if resitele is not None: | ||||
| 		resitele_id = [r.id for r in resitele] | ||||
|  | @ -63,12 +66,12 @@ def body_resitelu( | |||
| 
 | ||||
| class Vysledkovka(abc.ABC): | ||||
| 	jen_verejne: bool | ||||
| 	rocnik: m.Rocnik | ||||
| 	do_deadlinu: m.Deadline | ||||
| 	rocnik: Rocnik | ||||
| 	do_deadlinu: Deadline | ||||
| 
 | ||||
| 	@property | ||||
| 	@abc.abstractmethod | ||||
| 	def aktivni_resitele(self) -> list[m.Resitel]: | ||||
| 	def aktivni_resitele(self) -> list[Resitel]: | ||||
| 		... | ||||
| 
 | ||||
| 	@cached_property | ||||
|  | @ -143,20 +146,20 @@ class Vysledkovka(abc.ABC): | |||
| 
 | ||||
| class VysledkovkaRocniku(Vysledkovka): | ||||
| 
 | ||||
| 	def __init__(self, rocnik: m.Rocnik, jen_verejne: bool = True): | ||||
| 	def __init__(self, rocnik: Rocnik, jen_verejne: bool = True): | ||||
| 		self.rocnik = rocnik | ||||
| 		self.jen_verejne = jen_verejne | ||||
| 		deadliny = m.Deadline.objects.filter(cislo__rocnik=rocnik) | ||||
| 		deadliny = Deadline.objects.filter(cislo__rocnik=rocnik) | ||||
| 		if jen_verejne: | ||||
| 			deadliny = deadliny.filter(verejna_vysledkovka=True) | ||||
| 		self.do_deadlinu = deadliny.order_by("deadline").last() | ||||
| 
 | ||||
| 	@cached_property | ||||
| 	def aktivni_resitele(self) -> list[m.Resitel]: | ||||
| 	def aktivni_resitele(self) -> list[Resitel]: | ||||
| 		return list(resi_v_rocniku(self.rocnik)) | ||||
| 
 | ||||
| 	@cached_property | ||||
| 	def cisla_rocniku(self) -> list[m.Cislo]: | ||||
| 	def cisla_rocniku(self) -> list[Cislo]: | ||||
| 		""" Vrátí všechna čísla daného ročníku. """ | ||||
| 		if self.jen_verejne: | ||||
| 			return self.rocnik.verejne_vysledkovky_cisla() | ||||
|  | @ -164,7 +167,7 @@ class VysledkovkaRocniku(Vysledkovka): | |||
| 			return self.rocnik.cisla.all().order_by('poradi') | ||||
| 
 | ||||
| 	@cached_property | ||||
| 	def body_za_cisla_slovnik(self) -> dict[int, dict[int, int]]: # Výstup: m.Cislo.id → ( m.Resitel.id → body ) | ||||
| 	def body_za_cisla_slovnik(self) -> dict[int, dict[int, int]]: # Výstup: Cislo.id → ( Resitel.id → body ) | ||||
| 		# TODO: Body jsou decimal! | ||||
| 		body_cisla_slovnik = dict() | ||||
| 		for cislo in self.cisla_rocniku: | ||||
|  | @ -197,7 +200,7 @@ class VysledkovkaRocniku(Vysledkovka): | |||
| 		radky_vysledkovky = [] | ||||
| 
 | ||||
| 		setrizeni_resitele_dict = dict() | ||||
| 		for r in m.Resitel.objects.filter( | ||||
| 		for r in Resitel.objects.filter( | ||||
| 				id__in=self.setrizeni_resitele_id | ||||
| 		).select_related('osoba'): | ||||
| 			setrizeni_resitele_dict[r.id] = r | ||||
|  | @ -227,31 +230,31 @@ class VysledkovkaRocniku(Vysledkovka): | |||
| class VysledkovkaCisla(Vysledkovka): | ||||
| 	def __init__( | ||||
| 			self, | ||||
| 			cislo: m.Cislo, | ||||
| 			cislo: Cislo, | ||||
| 			jen_verejne: bool = True, | ||||
| 			do_deadlinu: m.Deadline = None | ||||
| 			do_deadlinu: Deadline = None | ||||
| 	): | ||||
| 		self.cislo = cislo | ||||
| 		self.rocnik = cislo.rocnik | ||||
| 		self.jen_verejne = jen_verejne | ||||
| 		if do_deadlinu is None: | ||||
| 			do_deadlinu = m.Deadline.objects.filter(cislo=cislo).last() | ||||
| 			do_deadlinu = Deadline.objects.filter(cislo=cislo).last() | ||||
| 		self.do_deadlinu = do_deadlinu | ||||
| 
 | ||||
| 	@cached_property | ||||
| 	def aktivni_resitele(self) -> list[m.Resitel]: | ||||
| 	def aktivni_resitele(self) -> list[Resitel]: | ||||
| 		# TODO možná chytřeji vybírat aktivní řešitele | ||||
| 		return list(resi_v_rocniku(self.rocnik)) | ||||
| 
 | ||||
| 	@cached_property | ||||
| 	def problemy(self) -> list[m.Problem]: | ||||
| 	def problemy(self) -> list[Problem]: | ||||
| 		""" Vrátí seznam všech problémů s body v daném čísle. """ | ||||
| 		return m.Problem.objects.filter( | ||||
| 			hodnoceni__in=m.Hodnoceni.objects.filter(deadline_body__cislo=self.cislo) | ||||
| 		return Problem.objects.filter( | ||||
| 			hodnoceni__in=Hodnoceni.objects.filter(deadline_body__cislo=self.cislo) | ||||
| 		).distinct().non_polymorphic().select_related('nadproblem').select_related('nadproblem__nadproblem') | ||||
| 
 | ||||
| 	@cached_property | ||||
| 	def hlavni_problemy(self) -> list[m.Problem]: | ||||
| 	def hlavni_problemy(self) -> list[Problem]: | ||||
| 		""" Vrátí seznam všech problémů, které již nemají nadproblém. """ | ||||
| 		# hlavní problémy čísla | ||||
| 		# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém) | ||||
|  | @ -269,7 +272,7 @@ class VysledkovkaCisla(Vysledkovka): | |||
| 	# Není cached, protože si myslím, že queryset lze použít ve for jen jednou. | ||||
| 	@property | ||||
| 	def hodnoceni_do_cisla(self): | ||||
| 		hodnoceni = m.Hodnoceni.objects.prefetch_related('reseni__resitele').select_related('problem', 'reseni') | ||||
| 		hodnoceni = Hodnoceni.objects.prefetch_related('reseni__resitele').select_related('problem', 'reseni') | ||||
| 		if self.jen_verejne: | ||||
| 			hodnoceni = hodnoceni.filter(deadline_body__verejna_vysledkovka=True) | ||||
| 		return hodnoceni.filter( | ||||
|  | @ -347,7 +350,7 @@ class VysledkovkaCisla(Vysledkovka): | |||
| 		return self.sectene_body[2] | ||||
| 
 | ||||
| 	@cached_property | ||||
| 	def temata_a_spol(self) -> list[m.Problem]: | ||||
| 	def temata_a_spol(self) -> list[Problem]: | ||||
| 		if self.rocnik.rocnik < ROCNIK_ZRUSENI_TEMAT: | ||||
| 			return self.hlavni_problemy | ||||
| 		else: | ||||
|  | @ -358,7 +361,7 @@ class VysledkovkaCisla(Vysledkovka): | |||
| 		return len(self.hlavni_problemy) - len(self.temata_a_spol) > 0 | ||||
| 
 | ||||
| 	@cached_property | ||||
| 	def podproblemy(self) -> dict[int, list[m.Problem]]: | ||||
| 	def podproblemy(self) -> dict[int, list[Problem]]: | ||||
| 		podproblemy = {hp.id: [] for hp in self.temata_a_spol} | ||||
| 		temata_a_spol = set(self.temata_a_spol) | ||||
| 		podproblemy[-1] = [] | ||||
|  | @ -375,7 +378,7 @@ class VysledkovkaCisla(Vysledkovka): | |||
| 		return podproblemy | ||||
| 
 | ||||
| 	@cached_property | ||||
| 	def podproblemy_seznam(self) -> list[list[m.Problem]]: | ||||
| 	def podproblemy_seznam(self) -> list[list[Problem]]: | ||||
| 		return [self.podproblemy[it.id] for it in self.temata_a_spol] + [self.podproblemy[-1]] | ||||
| 
 | ||||
| 	@cached_property | ||||
|  | @ -405,7 +408,7 @@ class VysledkovkaCisla(Vysledkovka): | |||
| 		radky_vysledkovky = [] | ||||
| 
 | ||||
| 		setrizeni_resitele_slovnik = {} | ||||
| 		setrizeni_resitele = m.Resitel.objects.filter(id__in=self.setrizeni_resitele_id).select_related('osoba') | ||||
| 		setrizeni_resitele = Resitel.objects.filter(id__in=self.setrizeni_resitele_id).select_related('osoba') | ||||
| 
 | ||||
| 		for r in setrizeni_resitele: | ||||
| 			setrizeni_resitele_slovnik[r.id] = r | ||||
|  | @ -456,29 +459,29 @@ class VysledkovkaCisla(Vysledkovka): | |||
| 	@staticmethod | ||||
| 	def ne_clanek_ne_konfera(problem): | ||||
| 		inst = problem.get_real_instance() | ||||
| 		return not (isinstance(inst, m.Clanek) or isinstance(inst, m.Konfera)) | ||||
| 		return not (isinstance(inst, Clanek) or isinstance(inst, Konfera)) | ||||
| 
 | ||||
| 
 | ||||
| class VysledkovkaDoTeXu(VysledkovkaCisla): | ||||
| 	def __init__( | ||||
| 			self, | ||||
| 			nejake_cislo: m.Cislo, | ||||
| 			od_vyjma: m.Deadline, | ||||
| 			do_vcetne: m.Deadline | ||||
| 			nejake_cislo: Cislo, | ||||
| 			od_vyjma: Deadline, | ||||
| 			do_vcetne: Deadline | ||||
| 	): | ||||
| 		super().__init__(nejake_cislo, False, do_vcetne) | ||||
| 		self.od_deadlinu = od_vyjma | ||||
| 
 | ||||
| 	@cached_property | ||||
| 	def problemy(self) -> list[m.Problem]: | ||||
| 		return m.Problem.objects.filter(hodnoceni__in=m.Hodnoceni.objects.filter( | ||||
| 	def problemy(self) -> list[Problem]: | ||||
| 		return Problem.objects.filter(hodnoceni__in=Hodnoceni.objects.filter( | ||||
| 				deadline_body__deadline__gt=self.od_deadlinu.deadline, | ||||
| 				deadline_body__deadline__lte=self.do_deadlinu.deadline, | ||||
| 			)).distinct().non_polymorphic().select_related('nadproblem').select_related('nadproblem__nadproblem') | ||||
| 
 | ||||
| 	@property | ||||
| 	def hodnoceni_do_cisla(self): | ||||
| 		hodnoceni = m.Hodnoceni.objects.prefetch_related( | ||||
| 		hodnoceni = Hodnoceni.objects.prefetch_related( | ||||
| 			'problem', 'reseni', 'reseni__resitele') | ||||
| 		if self.jen_verejne: | ||||
| 			hodnoceni = hodnoceni.filter(deadline_body__verejna_vysledkovka=True) | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue