Merge branch 'data_migrations' of gimli.ms.mff.cuni.cz:/akce/mam/git/mamweb into data_migrations
This commit is contained in:
		
						commit
						aaa84a5f00
					
				
					 26 changed files with 1350 additions and 392 deletions
				
			
		
							
								
								
									
										
											BIN
										
									
								
								Schema_new.dia
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Schema_new.dia
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							|  | @ -2,6 +2,7 @@ from django import forms | ||||||
| from dal import autocomplete | from dal import autocomplete | ||||||
| from django.core.exceptions import ObjectDoesNotExist | from django.core.exceptions import ObjectDoesNotExist | ||||||
| from django.contrib.auth.models import User | from django.contrib.auth.models import User | ||||||
|  | from django.forms.models import inlineformset_factory | ||||||
| 
 | 
 | ||||||
| from .models import Skola, Resitel, Osoba, Problem | from .models import Skola, Resitel, Osoba, Problem | ||||||
| import seminar.models as m | import seminar.models as m | ||||||
|  | @ -252,6 +253,25 @@ class VlozReseniForm(forms.Form): | ||||||
| 		super().__init__(*args, **kwargs) | 		super().__init__(*args, **kwargs) | ||||||
| 		#self.fields['favorite_color'] = forms.ChoiceField(choices=[(color.id, color.name) for color in Resitel.objects.all()]) | 		#self.fields['favorite_color'] = forms.ChoiceField(choices=[(color.id, color.name) for color in Resitel.objects.all()]) | ||||||
| 
 | 
 | ||||||
|  | class NahrajReseniForm(forms.ModelForm): | ||||||
|  | 	class Meta: | ||||||
|  | 		model = m.Reseni | ||||||
|  | 		fields = ('problem',) | ||||||
|  | 		 | ||||||
|  | 		widgets = {'problem': | ||||||
|  | 				autocomplete.ModelSelect2Multiple( | ||||||
|  | 					url='autocomplete_problem_odevzdatelny', | ||||||
|  | 					attrs = {'data-placeholder--id': '-1', | ||||||
|  | 						'data-placeholder--text' : '---', | ||||||
|  | 						'data-allow-clear': 'true'}, | ||||||
|  | 				) | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
|  | ReseniSPrilohamiFormSet = inlineformset_factory(m.Reseni,m.PrilohaReseni,  | ||||||
|  | 		form = NahrajReseniForm, | ||||||
|  | 		fields = ('soubor','res_poznamka'), | ||||||
|  | 		widgets = {'res_poznamka':forms.TextInput()}, | ||||||
|  | 		extra = 1, | ||||||
| 
 | 
 | ||||||
|  | 		) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -38,7 +38,8 @@ class Command(BaseCommand): | ||||||
| 		if not options['no_migrate']: | 		if not options['no_migrate']: | ||||||
| 			call_command('migrate', no_input=True) | 			call_command('migrate', no_input=True) | ||||||
| 		self.stdout.write('Vytvarim uzivatele "admin" (heslo "admin") a pseudo-nahodna data ...') | 		self.stdout.write('Vytvarim uzivatele "admin" (heslo "admin") a pseudo-nahodna data ...') | ||||||
| 		create_test_data(size=8) | 		create_test_data(size=5) | ||||||
|  | 		# menší počet ročníků, aby se zrychlilo generování dat a bylo dost úloh | ||||||
| 		self.stdout.write('Vytvoreno {} uzivatelu, {} skol, {} resitelu, {} rocniku, {} cisel,'  | 		self.stdout.write('Vytvoreno {} uzivatelu, {} skol, {} resitelu, {} rocniku, {} cisel,'  | ||||||
| 			   ' {} problemu, {} reseni.'.format(User.objects.count(), Skola.objects.count(),  | 			   ' {} problemu, {} reseni.'.format(User.objects.count(), Skola.objects.count(),  | ||||||
| 					Resitel.objects.count(), Rocnik.objects.count(), Cislo.objects.count(),  | 					Resitel.objects.count(), Rocnik.objects.count(), Cislo.objects.count(),  | ||||||
|  |  | ||||||
							
								
								
									
										24
									
								
								seminar/migrations/0074_auto_20200228_1401.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								seminar/migrations/0074_auto_20200228_1401.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | ||||||
|  | # Generated by Django 2.2.9 on 2020-02-28 13:01 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations, models | ||||||
|  | import django.db.models.deletion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('seminar', '0073_copy_osoba_email_to_user_email'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AddField( | ||||||
|  |             model_name='prilohareseni', | ||||||
|  |             name='res_poznamka', | ||||||
|  |             field=models.TextField(blank=True, help_text='Poznámka k příloze řešení, např. co daný soubor obsahuje', verbose_name='poznámka řešitele'), | ||||||
|  |         ), | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='hodnoceni', | ||||||
|  |             name='problem', | ||||||
|  |             field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='seminar.Problem', verbose_name='problém'), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
							
								
								
									
										18
									
								
								seminar/migrations/0075_auto_20200228_2010.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								seminar/migrations/0075_auto_20200228_2010.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | # Generated by Django 2.2.9 on 2020-02-28 19:10 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations, models | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('seminar', '0074_auto_20200228_1401'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='hodnoceni', | ||||||
|  |             name='body', | ||||||
|  |             field=models.DecimalField(decimal_places=1, max_digits=8, null=True, verbose_name='body'), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
							
								
								
									
										19
									
								
								seminar/migrations/0076_auto_20200228_2013.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								seminar/migrations/0076_auto_20200228_2013.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | ||||||
|  | # Generated by Django 2.2.9 on 2020-02-28 19:13 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations, models | ||||||
|  | import django.db.models.deletion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('seminar', '0075_auto_20200228_2010'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='hodnoceni', | ||||||
|  |             name='cislo_body', | ||||||
|  |             field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='seminar.Cislo', verbose_name='číslo pro body'), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
							
								
								
									
										32
									
								
								seminar/migrations/0077_auto_20200318_2146.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								seminar/migrations/0077_auto_20200318_2146.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | ||||||
|  | # Generated by Django 2.2.9 on 2020-03-18 20:46 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations, models | ||||||
|  | import django.db.models.deletion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('seminar', '0076_auto_20200228_2013'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name='CastNode', | ||||||
|  |             fields=[ | ||||||
|  |                 ('treenode_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='seminar.TreeNode')), | ||||||
|  |                 ('nadpis', models.CharField(help_text='Nadpis podvěšené části obsahu', max_length=100, verbose_name='Nadpis')), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 'verbose_name': 'Část (Node)', | ||||||
|  |                 'verbose_name_plural': 'Části (Node)', | ||||||
|  |                 'db_table': 'seminar_nodes_cast', | ||||||
|  |             }, | ||||||
|  |             bases=('seminar.treenode',), | ||||||
|  |         ), | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='treenode', | ||||||
|  |             name='first_child', | ||||||
|  |             field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='father_of_first', to='seminar.TreeNode', verbose_name='první potomek'), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
							
								
								
									
										27
									
								
								seminar/migrations/0078_otistenereseninode.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								seminar/migrations/0078_otistenereseninode.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | # Generated by Django 2.2.9 on 2020-03-18 23:59 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations, models | ||||||
|  | import django.db.models.deletion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('seminar', '0077_auto_20200318_2146'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name='OtisteneReseniNode', | ||||||
|  |             fields=[ | ||||||
|  |                 ('treenode_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='seminar.TreeNode')), | ||||||
|  |                 ('reseni', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='seminar.Reseni', verbose_name='reseni')), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 'verbose_name': 'Otištěné řešení (Node)', | ||||||
|  |                 'verbose_name_plural': 'Otištěná řešení (Node)', | ||||||
|  |                 'db_table': 'seminar_nodes_otistene_reseni', | ||||||
|  |             }, | ||||||
|  |             bases=('seminar.treenode',), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
							
								
								
									
										18
									
								
								seminar/migrations/0079_clanek_resitelsky.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								seminar/migrations/0079_clanek_resitelsky.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | # Generated by Django 2.2.9 on 2020-03-25 19:24 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations, models | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('seminar', '0078_otistenereseninode'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AddField( | ||||||
|  |             model_name='clanek', | ||||||
|  |             name='resitelsky', | ||||||
|  |             field=models.BooleanField(default=True, verbose_name='Jde o řešitelský článek?'), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
|  | @ -304,9 +304,10 @@ class Resitel(SeminarModelBase): | ||||||
| 		return sum(h.body for h in list(vsechna_hodnoceni)) | 		return sum(h.body for h in list(vsechna_hodnoceni)) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 	def get_titul(self): | 	def get_titul(self, celkove_body=None): | ||||||
| 		"Vrati titul" | 		"Vrati titul" | ||||||
| 		celkove_body = self.vsechny_body() | 		if celkove_body is None: | ||||||
|  | 			celkove_body = self.vsechny_body() | ||||||
| 
 | 
 | ||||||
| 		if celkove_body < 10: | 		if celkove_body < 10: | ||||||
| 			return '' | 			return '' | ||||||
|  | @ -753,6 +754,8 @@ class Clanek(Problem): | ||||||
| 	cislo = models.ForeignKey(Cislo, verbose_name='číslo', blank=True, null=True, | 	cislo = models.ForeignKey(Cislo, verbose_name='číslo', blank=True, null=True, | ||||||
| 		on_delete=models.PROTECT) | 		on_delete=models.PROTECT) | ||||||
| 
 | 
 | ||||||
|  | 	resitelsky = models.BooleanField('Jde o řešitelský článek?', default=True) | ||||||
|  | 
 | ||||||
| 	# má OneToOneField s: | 	# má OneToOneField s: | ||||||
| 	# ClanekNode | 	# ClanekNode | ||||||
| 
 | 
 | ||||||
|  | @ -898,6 +901,9 @@ class Reseni(SeminarModelBase): | ||||||
| 	# má OneToOneField s: | 	# má OneToOneField s: | ||||||
| 	# Konfera | 	# Konfera | ||||||
| 
 | 
 | ||||||
|  | 	# má ForeignKey s: | ||||||
|  | 	# Hodnoceni | ||||||
|  | 
 | ||||||
| 	def __str__(self): | 	def __str__(self): | ||||||
| 		return "{}({}): {}({})".format(self.resitele.first(),len(self.resitele.all()), self.problem.first() ,len(self.problem.all())) | 		return "{}({}): {}({})".format(self.resitele.first(),len(self.resitele.all()), self.problem.first() ,len(self.problem.all())) | ||||||
| 		# NOTE: Potenciální DB HOG (bez select_related) | 		# NOTE: Potenciální DB HOG (bez select_related) | ||||||
|  | @ -920,10 +926,10 @@ class Hodnoceni(SeminarModelBase): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 	body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body',  | 	body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body',  | ||||||
| 		blank=False, null=False) | 		blank=False, null=True) | ||||||
| 
 | 
 | ||||||
| 	cislo_body = models.ForeignKey(Cislo, verbose_name='číslo pro body',  | 	cislo_body = models.ForeignKey(Cislo, verbose_name='číslo pro body',  | ||||||
| 		related_name='hodnoceni', blank=False, null=False, on_delete=models.PROTECT) | 		related_name='hodnoceni', blank=False, null=True, on_delete=models.PROTECT) | ||||||
| 
 | 
 | ||||||
| 	reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) | 	reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) | ||||||
| 
 | 
 | ||||||
|  | @ -935,17 +941,16 @@ class Hodnoceni(SeminarModelBase): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ## FIXME: Budeme řešit později, pokud to bude potřeba. | def aux_generate_filename(self, filename): | ||||||
| #def aux_generate_filename(self, filename): | 	"""Pomocná funkce generující ošetřený název souboru v adresáři s datem""" | ||||||
| #	"""Pomocná funkce generující ošetřený název souboru v adresáři s datem""" | 	clean = get_valid_filename( | ||||||
| #	clean = get_valid_filename( | 		unidecode(filename.replace('/', '-').replace('\0', '')) | ||||||
| #		unidecode(filename.replace('/', '-').replace('\0', '')) | 	) | ||||||
| #	) | 	datedir = timezone.now().strftime('%Y-%m') | ||||||
| #	datedir = timezone.now().strftime('%Y-%m') | 	fname = "{}_{}".format( | ||||||
| #	fname = "%s_%s" % ( | 		timezone.now().strftime('%Y-%m-%d-%H:%M'), | ||||||
| #		timezone.now().strftime('%Y-%m-%d-%H:%M'), | 		clean) | ||||||
| #		clean) | 	return os.path.join(datedir, fname) | ||||||
| #	return os.path.join(datedir, fname) |  | ||||||
| 
 | 
 | ||||||
| # Django neumí jednoduše serializovat partial nebo třídu s __call__ | # Django neumí jednoduše serializovat partial nebo třídu s __call__ | ||||||
| # (https://docs.djangoproject.com/en/1.8/topics/migrations/), | # (https://docs.djangoproject.com/en/1.8/topics/migrations/), | ||||||
|  | @ -988,9 +993,12 @@ class PrilohaReseni(SeminarModelBase): | ||||||
| 
 | 
 | ||||||
| 	poznamka = models.TextField('neveřejná poznámka', blank=True, | 	poznamka = models.TextField('neveřejná poznámka', blank=True, | ||||||
| 		help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu') | 		help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu') | ||||||
|  | 	 | ||||||
|  | 	res_poznamka = models.TextField('poznámka řešitele', blank=True, | ||||||
|  | 		help_text='Poznámka k příloze řešení, např. co daný soubor obsahuje') | ||||||
| 
 | 
 | ||||||
| 	def __str__(self): | 	def __str__(self): | ||||||
| 		return self.soubor | 		return str(self.soubor) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Pohadka(SeminarModelBase): | class Pohadka(SeminarModelBase): | ||||||
|  | @ -1228,6 +1236,8 @@ class Obrazek(SeminarModelBase): | ||||||
| 			help_text = 'Černobílá verze obrázku do čísla',  | 			help_text = 'Černobílá verze obrázku do čísla',  | ||||||
| 			upload_to = 'obrazky/%Y/%m/%d/', blank=True, null=True) | 			upload_to = 'obrazky/%Y/%m/%d/', blank=True, null=True) | ||||||
| 
 | 
 | ||||||
|  | 	# TODO placement hint - chci ho tady / pred textem / za textem | ||||||
|  | 
 | ||||||
| class TreeNode(PolymorphicModel): | class TreeNode(PolymorphicModel): | ||||||
| 	class Meta: | 	class Meta: | ||||||
| 		db_table = "seminar_nodes_treenode" | 		db_table = "seminar_nodes_treenode" | ||||||
|  | @ -1242,6 +1252,7 @@ class TreeNode(PolymorphicModel): | ||||||
| 		on_delete = models.SET_NULL, # Vrcholy s null kořenem jsou sirotci bez ročníku  | 		on_delete = models.SET_NULL, # Vrcholy s null kořenem jsou sirotci bez ročníku  | ||||||
| 		verbose_name="kořen stromu") | 		verbose_name="kořen stromu") | ||||||
| 	first_child = models.ForeignKey('TreeNode', | 	first_child = models.ForeignKey('TreeNode', | ||||||
|  | 		related_name='father_of_first', | ||||||
| 		null = True, | 		null = True, | ||||||
| 		blank = True, | 		blank = True, | ||||||
| 		on_delete=models.SET_NULL, | 		on_delete=models.SET_NULL, | ||||||
|  | @ -1262,15 +1273,6 @@ class TreeNode(PolymorphicModel): | ||||||
| 	srolovatelne = models.BooleanField(null = True, blank = True, | 	srolovatelne = models.BooleanField(null = True, blank = True, | ||||||
| 		verbose_name = "Srolovatelné", | 		verbose_name = "Srolovatelné", | ||||||
| 		help_text = "Bude na stránce témátka možnost tuto položku skrýt") | 		help_text = "Bude na stránce témátka možnost tuto položku skrýt") | ||||||
| 	 |  | ||||||
| 	# Slouží k debugování pro rychlé získání představy o podobě podstromu pod tímto TreeNode. |  | ||||||
| 	def print_tree(self,indent=0): |  | ||||||
| 		# FIXME: Tady se spoléháme na to, že nedeklarovaný primární klíč se jmenuje by default 'id', což není úplně správně |  | ||||||
| 		print("{}{} (id: {})".format(" "*indent,self, self.id)) |  | ||||||
| 		if self.first_child: |  | ||||||
| 			self.first_child.print_tree(indent=indent+2) |  | ||||||
| 		if self.succ: |  | ||||||
| 			self.succ.print_tree(indent=indent) |  | ||||||
| 
 | 
 | ||||||
| 	def getOdkazStr(self): # String na rozcestník | 	def getOdkazStr(self): # String na rozcestník | ||||||
| 		return self.first_child.getOdkazStr() | 		return self.first_child.getOdkazStr() | ||||||
|  | @ -1339,6 +1341,7 @@ class MezicisloNode(TreeNode): | ||||||
| 		verbose_name = 'Mezičíslo (Node)' | 		verbose_name = 'Mezičíslo (Node)' | ||||||
| 		verbose_name_plural = 'Mezičísla (Node)' | 		verbose_name_plural = 'Mezičísla (Node)' | ||||||
| 
 | 
 | ||||||
|  | 	# TODO: Využít TreeLib | ||||||
| 	def aktualizuj_nazev(self): | 	def aktualizuj_nazev(self): | ||||||
| 		if self.prev: | 		if self.prev: | ||||||
| 			if (self.prev.get_real_instance_class() != CisloNode and | 			if (self.prev.get_real_instance_class() != CisloNode and | ||||||
|  | @ -1469,6 +1472,34 @@ class TextNode(TreeNode): | ||||||
| 	def getOdkazStr(self): | 	def getOdkazStr(self): | ||||||
| 		return str(self.text) | 		return str(self.text) | ||||||
| 
 | 
 | ||||||
|  | class CastNode(TreeNode): | ||||||
|  | 	class Meta: | ||||||
|  | 		db_table = 'seminar_nodes_cast' | ||||||
|  | 		verbose_name = 'Část (Node)' | ||||||
|  | 		verbose_name_plural = 'Části (Node)' | ||||||
|  | 	 | ||||||
|  | 	nadpis = models.CharField('Nadpis', max_length=100, help_text = 'Nadpis podvěšené části obsahu') | ||||||
|  | 
 | ||||||
|  | 	def aktualizuj_nazev(self): | ||||||
|  | 		self.nazev = "CastNode: "+str(self.nadpis) | ||||||
|  | 
 | ||||||
|  | 	def getOdkazStr(self): | ||||||
|  | 		return str(self.nadpis) | ||||||
|  | 
 | ||||||
|  | class OtisteneReseniNode(TreeNode): | ||||||
|  | 	class Meta: | ||||||
|  | 		db_table = 'seminar_nodes_otistene_reseni' | ||||||
|  | 		verbose_name = 'Otištěné řešení (Node)' | ||||||
|  | 		verbose_name_plural = 'Otištěná řešení (Node)' | ||||||
|  | 	reseni = models.ForeignKey(Reseni, | ||||||
|  | 		on_delete=models.PROTECT, | ||||||
|  | 		verbose_name = 'reseni') | ||||||
|  | 	 | ||||||
|  | 	def aktualizuj_nazev(self): | ||||||
|  | 		self.nazev = "OtisteneReseniNode: "+str(self.reseni) | ||||||
|  | 
 | ||||||
|  | 	def getOdkazStr(self): | ||||||
|  | 		return str(self.reseni) | ||||||
| 
 | 
 | ||||||
| ## FIXME: Logiku přesunout do views. | ## FIXME: Logiku přesunout do views. | ||||||
| #class VysledkyBase(SeminarModelBase): | #class VysledkyBase(SeminarModelBase): | ||||||
|  |  | ||||||
							
								
								
									
										49
									
								
								seminar/static/seminar/dynamic_formsets.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								seminar/static/seminar/dynamic_formsets.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | ||||||
|  | // Credit https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0
 | ||||||
|  | function updateElementIndex(el, prefix, ndx) { | ||||||
|  | 	var id_regex = new RegExp('(' + prefix + '-\\d+)'); | ||||||
|  | 	var replacement = prefix + '-' + ndx; | ||||||
|  | 	if ($(el).attr("for")) { | ||||||
|  | 		$(el).attr("for", $(el).attr("for").replace(id_regex, replacement)); | ||||||
|  | 	} | ||||||
|  | 	if (el.id) { | ||||||
|  | 		el.id = el.id.replace(id_regex, replacement); | ||||||
|  | 	} | ||||||
|  | 	if (el.name) { | ||||||
|  | 		el.name = el.name.replace(id_regex, replacement); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Credit https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0
 | ||||||
|  | function deleteForm(prefix, btn) { | ||||||
|  |     var total = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val()); | ||||||
|  |     if (total >= 1){ | ||||||
|  |         btn.closest('div').remove(); | ||||||
|  |         var forms = $('.attachment'); | ||||||
|  |         $('#id_' + prefix + '-TOTAL_FORMS').val(forms.length); | ||||||
|  |         for (var i=0, formCount=forms.length; i<formCount; i++) { | ||||||
|  |             $(forms.get(i)).find(':input').each(function() { | ||||||
|  |                 updateElementIndex(this, prefix, i); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Credit: https://simpleit.rocks/python/django/dynamic-add-form-with-add-button-in-django-modelformset-template/
 | ||||||
|  | $(document).ready(function(){ | ||||||
|  | 	$('#add_attachment').click(function() { | ||||||
|  | 		var form_idx = $('#id_prilohy-TOTAL_FORMS').val(); | ||||||
|  | 		var new_form = $('#empty_form').html().replace(/__prefix__/g, form_idx); | ||||||
|  | 		$('#form_set').append(new_form); | ||||||
|  | 		// Newly created form has not the binding between remove button and remove function
 | ||||||
|  | 		// We need to add it manually
 | ||||||
|  | 		$('.remove_attachment').click(function(){ | ||||||
|  | 			deleteForm("prilohy",this); | ||||||
|  | 		}); | ||||||
|  | 		$('#id_prilohy-TOTAL_FORMS').val(parseInt(form_idx) + 1); | ||||||
|  | 	}); | ||||||
|  | 	$('.remove_attachment').click(function(){ | ||||||
|  | 		deleteForm("prilohy",this); | ||||||
|  | 	}); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | @ -1,7 +1,8 @@ | ||||||
| {% extends "seminar/archiv/base_cisla.html" %} | {% extends "seminar/archiv/base_cisla.html" %} | ||||||
| 
 | 
 | ||||||
| {% block content %} |  {% block content %} | ||||||
| <div> |  <div> | ||||||
|  | 
 | ||||||
|   <h1> |   <h1> | ||||||
|     {% block nadpis1a %}{% block nadpis1b %} |     {% block nadpis1a %}{% block nadpis1b %} | ||||||
|       Číslo {{ cislo }} |       Číslo {{ cislo }} | ||||||
|  | @ -45,14 +46,15 @@ | ||||||
|           <li><a href="obalkovani">Obálkování</a></li> |           <li><a href="obalkovani">Obálkování</a></li> | ||||||
|         </ul> |         </ul> | ||||||
|       </div> |       </div> | ||||||
|   {% endif %} |   {% endif %}  | ||||||
| 
 | 
 | ||||||
|   {% if cislo.verejna_vysledkovka %} |   {% if cislo.verejna_vysledkovka %} | ||||||
|   <h2>Výsledkovka</h2> |   <h2>Výsledkovka ({% now "jS F Y H:i" %})</h2> | ||||||
|  | 
 | ||||||
|   {% else %} |   {% else %} | ||||||
|     {% if user.is_staff %} |     {% if user.is_staff %} | ||||||
|       <div class='mam-org-only'> |       <div class='mam-org-only'> | ||||||
|       <h2>Výsledkovka (neveřejná)</h2> |       <h2>Výsledkovka (neveřejná, {% now "jS F Y H:i:s" %})</h2> | ||||||
|     {% endif %} |     {% endif %} | ||||||
|   {% endif %} |   {% endif %} | ||||||
| 
 | 
 | ||||||
|  | @ -61,36 +63,37 @@ | ||||||
|       <tr class='border-b'> |       <tr class='border-b'> | ||||||
|         <th class='border-r'># |         <th class='border-r'># | ||||||
|         <th class='border-r'>Jméno |         <th class='border-r'>Jméno | ||||||
|         {# problémy by měly být veřejné, když je veřejná výsledkovka #} |  | ||||||
|         {% for p in problemy %} |         {% for p in problemy %} | ||||||
|         <th class='border-r'><a href="{{ p.verejne_url }}">{{ p.kod_v_rocniku }}</a> |         <th class='border-r'><a href="{{ p.verejne_url }}">{{ p.kod_v_rocniku }}</a> | ||||||
|         {% endfor %} |         {% endfor %} | ||||||
|         <th class='border-r'>Za číslo</sup> |         <th class='border-r'>Za číslo</sup> | ||||||
|         <th class='border-r'>Za ročník |         <th class='border-r'>Za ročník | ||||||
|         <th class='border-r'>Odjakživa |         <th class='border-r'>Odjakživa | ||||||
|     {% for rv in vysledkovka %} |     {% for rv in radky_vysledkovky %} | ||||||
|       <tr> |       <tr> | ||||||
|         <td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %} |         <td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %} | ||||||
|             <th class='border-r'> |             <th class='border-r'> | ||||||
|             {% if rv.titul %} |             {% if rv.titul is not '' %} | ||||||
|               {{ rv.titul }}<sup>MM</sup> |               {{ rv.titul }}<sup>MM</sup> | ||||||
|             {% endif %} |             {% endif %} | ||||||
|             {{ rv.resitel.plne_jmeno }} |             {{ rv.resitel.osoba.plne_jmeno }} | ||||||
|         {% for b in rv.body_ulohy %} |         {% for b in rv.body_problemy_sezn %} | ||||||
|         <td class='border-r'>{{ b }} |         <td class='border-r'>{{ b }} | ||||||
|         {% endfor %} |         {% endfor %} | ||||||
|         <td class='border-r'>{{ rv.body_cislo }} |         <td class='border-r'>{{ rv.body_cislo }} | ||||||
|         <td class='border-r'><b>{{ rv.body_celkem_rocnik }}</b> |         <td class='border-r'><b>{{ rv.body_rocnik }}</b> | ||||||
|         <td class='border-r'>{{ rv.body_celkem_odjakziva }} |         <td class='border-r'>{{ rv.body_celkem_odjakziva }} | ||||||
|       </tr> |       </tr> | ||||||
|     {% endfor %} |     {% endfor %} | ||||||
|     </table> |     </table> | ||||||
|   {% endif %} |   {% endif %}  | ||||||
| 
 | 
 | ||||||
|   {% if not cislo.verejna_vysledkovka and user.is_staff %} |   {% if not cislo.verejna_vysledkovka and user.is_staff %} | ||||||
|       </div> |       </div> | ||||||
|   {% endif %} |   {% endif %} | ||||||
| 
 | 
 | ||||||
| </div> |   Čas: {% now "jS F Y H:i:s" %} | ||||||
| {% endblock content %} | 
 | ||||||
|  | </div>  | ||||||
|  | {% endblock content %}  | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										44
									
								
								seminar/templates/seminar/nahraj_reseni.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								seminar/templates/seminar/nahraj_reseni.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,44 @@ | ||||||
|  | {% extends "seminar/zadani/base.html" %} | ||||||
|  | {% load staticfiles %} | ||||||
|  | {% block script %} | ||||||
|  |     <!--script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script!--> | ||||||
|  |     {{form.media}} | ||||||
|  |     <script src="{% static 'seminar/dynamic_formsets.js' %}"></script> | ||||||
|  | {% endblock %} | ||||||
|  | 
 | ||||||
|  | {% block content %} | ||||||
|  | <h1> | ||||||
|  |   {% block nadpis1a %}{% block nadpis1b %} | ||||||
|  |     Vložit řešení | ||||||
|  |   {% endblock %}{% endblock %} | ||||||
|  | </h1> | ||||||
|  | <form enctype="multipart/form-data" action="{% url 'seminar_nahraj_reseni' %}" method="post"> | ||||||
|  |   {% csrf_token %} | ||||||
|  | {{form}} | ||||||
|  | {{prilohy.management_form}} | ||||||
|  | <div id="form_set"> | ||||||
|  | {% for form in prilohy.forms %} | ||||||
|  | 	<div class="attachment"> | ||||||
|  | 	{{form.non_field_errors}} | ||||||
|  | 	{{form.errors}} | ||||||
|  |         <table class='no_error'> | ||||||
|  |             {{ form }} | ||||||
|  |         </table> | ||||||
|  | 	<input type="button" value="Odebrat" class="remove_attachment" id="{{form.prefix}}-jsremove"> | ||||||
|  | 	</div> | ||||||
|  | {% endfor %} | ||||||
|  | </div> | ||||||
|  | <input type="button" value="Přidat přílohu" id="add_attachment"> | ||||||
|  | <div id="empty_form" style="display:none"> | ||||||
|  | 	<div class="attachment"> | ||||||
|  |         <table class='no_error'> | ||||||
|  |             {{ prilohy.empty_form }} | ||||||
|  |         </table> | ||||||
|  | 	<input type="button" value="Odebrat" class="remove_attachment" id="id_prilohy-__prefix__-jsremove"> | ||||||
|  | 	</div> | ||||||
|  | 
 | ||||||
|  | </div> | ||||||
|  |     <input type="submit" value="Odevzdat"> | ||||||
|  | </form> | ||||||
|  | 
 | ||||||
|  | {% endblock %} | ||||||
							
								
								
									
										10
									
								
								seminar/templates/seminar/treenode.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								seminar/templates/seminar/treenode.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | ||||||
|  | {% extends "seminar/archiv/base_ulohy.html" %} | ||||||
|  | 
 | ||||||
|  | {% load comments %} | ||||||
|  | 
 | ||||||
|  | {% block content %} | ||||||
|  | 
 | ||||||
|  | {%with obj=tnldata depth=1 template_name="seminar/treenode_recursive.html" %} | ||||||
|  |    {%include template_name%} | ||||||
|  | {%endwith%} | ||||||
|  | {% endblock content %} | ||||||
							
								
								
									
										28
									
								
								seminar/templates/seminar/treenode_recursive.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								seminar/templates/seminar/treenode_recursive.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | ||||||
|  | {% load treenodes %} | ||||||
|  | {# <b>{{depth}}</b> #} | ||||||
|  | <div> | ||||||
|  | {%   if obj.node|isRocnik %} | ||||||
|  | <h{{depth}}> Ročník {{obj.node.rocnik}} </h{{depth}}> | ||||||
|  | {% elif obj.node|isCislo %} | ||||||
|  | <h{{depth}}> Číslo {{obj.node.cislo}} </h{{depth}}> | ||||||
|  | {% elif obj.node|isTemaVCisle %} | ||||||
|  | <h{{depth}}> Téma {{obj.node.tema.nazev}} </h{{depth}}> | ||||||
|  | {% elif obj.node|isUlohaZadani %} | ||||||
|  | <h{{depth}}>Úloha {{obj.node.uloha.kod_v_rocniku}} ({{obj.node.uloha.max_body}} b)</h{{depth}}> | ||||||
|  | {% elif obj.node|isUlohaVzorak %} | ||||||
|  | <h{{depth}}>Řešení: {{obj.node.uloha.kod_v_rocniku}}</h{{depth}}> | ||||||
|  | {% elif obj.node|isText %} | ||||||
|  | {{obj.node.text.na_web}} | ||||||
|  | {% else %} | ||||||
|  | Objekt jiného typu {{obj.node}} | ||||||
|  | {% endif %} | ||||||
|  |     {%if obj.children %} | ||||||
|  | 	<div> | ||||||
|  |          {%for ch in obj.children %} | ||||||
|  |               {%with obj=ch depth=depth|add:"1" template_name="seminar/treenode_recursive.html" %} | ||||||
|  |                    {%include template_name%} | ||||||
|  |               {%endwith%} | ||||||
|  |          {%endfor%} | ||||||
|  | 	</div> | ||||||
|  |     {%endif%} | ||||||
|  | </div> | ||||||
							
								
								
									
										49
									
								
								seminar/templatetags/treenodes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								seminar/templatetags/treenodes.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | ||||||
|  | from django import template | ||||||
|  | import seminar.models as m | ||||||
|  | 
 | ||||||
|  | register = template.Library() | ||||||
|  | 
 | ||||||
|  | @register.filter | ||||||
|  | def isRocnik(value): | ||||||
|  |     return isinstance(value, m.RocnikNode) | ||||||
|  | 
 | ||||||
|  | @register.filter | ||||||
|  | def isCislo(value): | ||||||
|  |     return isinstance(value, m.CisloNode) | ||||||
|  | 
 | ||||||
|  | @register.filter | ||||||
|  | def isCast(value): | ||||||
|  |     return isinstance(value, m.CastNode) | ||||||
|  | 
 | ||||||
|  | @register.filter | ||||||
|  | def isText(value): | ||||||
|  |     return isinstance(value, m.TextNode) | ||||||
|  | 
 | ||||||
|  | @register.filter | ||||||
|  | def isTemaVCisle(value): | ||||||
|  |     return isinstance(value, m.TemaVCisleNode) | ||||||
|  | 
 | ||||||
|  | @register.filter | ||||||
|  | def isKonfera(value): | ||||||
|  |     return isinstance(value, m.KonferaNode) | ||||||
|  | 
 | ||||||
|  | @register.filter | ||||||
|  | def isClanek(value): | ||||||
|  |     return isinstance(value, m.ClanekNode) | ||||||
|  | 
 | ||||||
|  | @register.filter | ||||||
|  | def isUlohaVzorak(value): | ||||||
|  |     return isinstance(value, m.UlohaVzorakNode) | ||||||
|  | 
 | ||||||
|  | @register.filter | ||||||
|  | def isUlohaZadani(value): | ||||||
|  |     return isinstance(value, m.UlohaZadaniNode) | ||||||
|  | 
 | ||||||
|  | @register.filter | ||||||
|  | def isPohadka(value): | ||||||
|  |     return isinstance(value, m.PohadkaNode) | ||||||
|  | 
 | ||||||
|  | #@register.filter | ||||||
|  | #def isOtisteneReseniNode(value): | ||||||
|  | #    return isinstance(value, m.OtisteneReseniNode) | ||||||
|  | 
 | ||||||
|  | @ -9,7 +9,7 @@ from django.db import transaction | ||||||
| import unidecode | import unidecode | ||||||
| import logging | import logging | ||||||
| 
 | 
 | ||||||
| from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni, Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Osoba, Organizator, Prijemce, Tema, Uloha, Konfera, KonferaNode, TextNode, UlohaVzorakNode, RocnikNode, CisloNode, TemaVCisleNode, Text, Hodnoceni, UlohaZadaniNode, Novinky | from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni, Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Osoba, Organizator, Prijemce, Tema, Uloha, Konfera, KonferaNode, TextNode, UlohaVzorakNode, RocnikNode, CisloNode, TemaVCisleNode, Text, Hodnoceni, UlohaZadaniNode, Novinky, TreeNode | ||||||
| 
 | 
 | ||||||
| from django.contrib.flatpages.models import FlatPage | from django.contrib.flatpages.models import FlatPage | ||||||
| from django.contrib.sites.models import Site | from django.contrib.sites.models import Site | ||||||
|  | @ -19,6 +19,13 @@ zlinska = None # tohle bude speciální škola, které později dodáme kontaktn | ||||||
| 
 | 
 | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
|  | # testuje unikátnost vygenerovaného jména | ||||||
|  | def __unikatni_jmeno(osoby, jmeno, prijmeni): | ||||||
|  | 	for os in osoby: | ||||||
|  | 		if os.jmeno == jmeno and os.prijmeni == prijmeni: | ||||||
|  | 			return 0 | ||||||
|  | 	else: return 1 | ||||||
|  | 
 | ||||||
| def gen_osoby(rnd, size): | def gen_osoby(rnd, size): | ||||||
| 	logger.info('Generuji osoby (size={})...'.format(size)) | 	logger.info('Generuji osoby (size={})...'.format(size)) | ||||||
| 
 | 
 | ||||||
|  | @ -48,6 +55,19 @@ def gen_osoby(rnd, size): | ||||||
| 		pohlavi = rnd.randint(0,1) | 		pohlavi = rnd.randint(0,1) | ||||||
| 		jmeno = rnd.choice([jmena_m, jmena_f][pohlavi]) | 		jmeno = rnd.choice([jmena_m, jmena_f][pohlavi]) | ||||||
| 		prijmeni = rnd.choice([prijmeni_m, prijmeni_f][pohlavi]) | 		prijmeni = rnd.choice([prijmeni_m, prijmeni_f][pohlavi]) | ||||||
|  | 		pokusy = 0 | ||||||
|  | 		max_pokusy = 120*size | ||||||
|  | 		while (not __unikatni_jmeno and pokusy < max_pokusy): | ||||||
|  | 		# pokud jméno a příjmení není unikátní, zkoušíme generovat nová | ||||||
|  | 		# do daného limitu (abychom se nezacyklili do nekonečna při málo jménech a příjmeních | ||||||
|  | 		# ze kterých se generuje) | ||||||
|  | 			jmeno = rnd.choice([jmena_m, jmena_f][pohlavi]) | ||||||
|  | 			prijmeni = rnd.choice([prijmeni_m, prijmeni_f][pohlavi]) | ||||||
|  | 			pokusy = pokusy + 1 | ||||||
|  | 		if pokusy >= max_pokusy: | ||||||
|  | 			print("Chyba, na danou velikost testovacích dat příliš málo možných" | ||||||
|  | 				" jmen a příjmení") | ||||||
|  | 			exit | ||||||
| 		prezdivka = rnd.choice(prezdivky) | 		prezdivka = rnd.choice(prezdivky) | ||||||
| 		email = "@".join([unidecode.unidecode(jmeno), rnd.choice(domain)]) | 		email = "@".join([unidecode.unidecode(jmeno), rnd.choice(domain)]) | ||||||
| 		telefon = "".join([str(rnd.choice([k for k in range(10)])) for i in range(9)]) | 		telefon = "".join([str(rnd.choice([k for k in range(10)])) for i in range(9)]) | ||||||
|  | @ -180,11 +200,12 @@ def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size) | ||||||
| 	k = 0 | 	k = 0 | ||||||
| 	for rocnik in rocniky: | 	for rocnik in rocniky: | ||||||
| 		k+=1 | 		k+=1 | ||||||
|  | 		print("Generuji {}. číslo.".format(k)) | ||||||
| 		cisla = rocnik_cisla[k-1] | 		cisla = rocnik_cisla[k-1] | ||||||
| 		for ci in range(3, len(cisla)+1): # pro všechna čísla | 		for ci in range(3, len(cisla)+1): # pro všechna čísla | ||||||
| 			resitele_size = round(7/8 * 30 * size) # očekáváný celkový počet řešitelů | 			resitele_size = round(7/8 * 30 * size) # očekáváný celkový počet řešitelů | ||||||
| 			poc_res = rnd.randint(round(resitele_size/8), round(3*resitele_size/4)) | 			poc_res = rnd.randint(round(resitele_size/8), round(resitele_size/4)) | ||||||
|  			# dané číslo řeší něco mezi osminou a tříčtvrtinou všech řešitelů |  			# dané číslo řeší něco mezi osminou a čtvrtinou všech řešitelů | ||||||
| 			# (náhodná hausnumera, možno změnit) | 			# (náhodná hausnumera, možno změnit) | ||||||
| 			# účelem je, aby se řešení generovala z menší množiny řešitelů a tedy | 			# účelem je, aby se řešení generovala z menší množiny řešitelů a tedy | ||||||
| 			# bylo více řešení od jednoho řešitele daného čísla | 			# bylo více řešení od jednoho řešitele daného čísla | ||||||
|  | @ -243,9 +264,10 @@ def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size) | ||||||
| 				p.save() | 				p.save() | ||||||
| 
 | 
 | ||||||
| 				# generování řešení | 				# generování řešení | ||||||
| 				poc_reseni = rnd.randint(size // 2, size * 2) | 				poc_reseni = rnd.randint(poc_res, poc_res * 4) | ||||||
| 				# generujeme náhodný počet řešení | 				# generujeme náhodný počet řešení vzhledem k počtu řešitelů čísla | ||||||
| 				for ri in range(poc_reseni): | 				for ri in range(poc_reseni): | ||||||
|  | 					#print("Generuji {}-té řešení".format(ri)) | ||||||
| 					if rnd.randint(1, 10) == 6: | 					if rnd.randint(1, 10) == 6: | ||||||
| 					# cca desetina řešení od více řešitelů | 					# cca desetina řešení od více řešitelů | ||||||
| 						res_vyber = rnd.sample(resitele_cisla, rnd.randint(2, 5)) | 						res_vyber = rnd.sample(resitele_cisla, rnd.randint(2, 5)) | ||||||
|  | @ -383,17 +405,15 @@ def gen_temata(rnd, rocniky, rocnik_cisla, organizatori): | ||||||
| 	co = ["téma", "záření", "stavení", "jiskření", "jelito", | 	co = ["téma", "záření", "stavení", "jiskření", "jelito", | ||||||
| 					"drama", "kuře", "moře", "klání", "proudění", "čekání"] | 					"drama", "kuře", "moře", "klání", "proudění", "čekání"] | ||||||
| 	poc_oboru = rnd.randint(1, 2) | 	poc_oboru = rnd.randint(1, 2) | ||||||
| 	poc_op = rnd.randint(1, 3) |  | ||||||
| 
 | 
 | ||||||
| 	rocnik_temata = [] | 	rocnik_temata = [] | ||||||
| 	k = 0 | 	# Věříme, že rocnik_cisla je pole polí čísel podle ročníků, tak si necháme dát vždycky jeden ročník a k němu příslušná čísla. | ||||||
| 	for rocnik in rocniky: | 	for rocnik, cisla in zip(rocniky, rocnik_cisla): | ||||||
| 		k+=1 | 		kod = 1 | ||||||
| 		n = 0 | 		letosni_temata = [] | ||||||
| 		temata = [] | 		# Do každého ročníku vymyslíme tři (zatím) témata, v každém z prvních čísel jedno | ||||||
| 		cisla = rocnik_cisla[k-1] | 		for zacatek_tematu in range(1, 3): | ||||||
| 		for ci in range(1, 3): | 			# Vygenerujeme téma | ||||||
| 			n+=1 |  | ||||||
| 			t = Tema.objects.create( | 			t = Tema.objects.create( | ||||||
| 				# atributy třídy Problem | 				# atributy třídy Problem | ||||||
| 				nazev=" ".join([rnd.choice(jake), rnd.choice(co)]), | 				nazev=" ".join([rnd.choice(jake), rnd.choice(co)]), | ||||||
|  | @ -401,22 +421,32 @@ def gen_temata(rnd, rocniky, rocnik_cisla, organizatori): | ||||||
| 				zamereni=rnd.sample(["M", "F", "I", "O", "B"], poc_oboru), | 				zamereni=rnd.sample(["M", "F", "I", "O", "B"], poc_oboru), | ||||||
| 				autor=rnd.choice(organizatori), | 				autor=rnd.choice(organizatori), | ||||||
| 				garant=rnd.choice(organizatori), | 				garant=rnd.choice(organizatori), | ||||||
| 				kod=str(n), | 				kod=str(kod), | ||||||
| 				# atributy třídy Téma | 				# atributy třídy Téma | ||||||
| 				tema_typ=rnd.choice(Tema.TEMA_CHOICES)[0], | 				tema_typ=rnd.choice(Tema.TEMA_CHOICES)[0], | ||||||
| 				rocnik=rocnik, | 				rocnik=rocnik, | ||||||
| 				abstrakt = "Abstrakt tematka {}".format(n) | 				abstrakt = "Abstrakt tematka {}".format(kod) | ||||||
| 			) | 			) | ||||||
| 			konec_tematu = min(rnd.randint(ci, 7), len(cisla)) | 			kod += 1 | ||||||
| 			for i in range(ci, konec_tematu+1): | 
 | ||||||
|  | 			# Vymyslíme, kdy skončí | ||||||
|  | 			konec_tematu = min(rnd.randint(zacatek_tematu, 7), len(cisla)) | ||||||
|  | 
 | ||||||
|  | 			# Vyrobíme TemaVCisleNody pro obsah | ||||||
|  | 			for i in range(zacatek_tematu, konec_tematu+1): | ||||||
| 				node = TemaVCisleNode.objects.create(tema = t) | 				node = TemaVCisleNode.objects.create(tema = t) | ||||||
|  | 				# FIXME: Není to off-by-one? | ||||||
| 				otec = cisla[i-1].cislonode | 				otec = cisla[i-1].cislonode | ||||||
| 				otec_syn(otec, node) | 				otec_syn(otec, node) | ||||||
| 
 | 
 | ||||||
| 			t.opravovatele.set(rnd.sample(organizatori, poc_op)) | 			# Vymyslíme, kdo to bude opravovat | ||||||
|  | 			poc_opravovatelu = rnd.randint(1, 3) | ||||||
|  | 			t.opravovatele.set(rnd.sample(organizatori, poc_opravovatelu)) | ||||||
|  | 
 | ||||||
|  | 			# Uložíme všechno | ||||||
| 			t.save() | 			t.save() | ||||||
| 			temata.append((ci, konec_tematu, t)) | 			letosni_temata.append((zacatek_tematu, konec_tematu, t)) | ||||||
| 		rocnik_temata.append(temata) | 		rocnik_temata.append(letosni_temata) | ||||||
| 	return rocnik_temata | 	return rocnik_temata | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -441,81 +471,103 @@ def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori) | ||||||
| 		"netriviální aplikace diferenciálních rovnic", "zadání je vnitřně" | 		"netriviální aplikace diferenciálních rovnic", "zadání je vnitřně" | ||||||
| 		"sporné", "nepopsatelně jednoduché", "pokud jste na to nepřišli," | 		"sporné", "nepopsatelně jednoduché", "pokud jste na to nepřišli," | ||||||
| 		"tak jste fakt hloupí"] | 		"tak jste fakt hloupí"] | ||||||
| 	k = 0 | 	# Ke každému ročníku si vezmeme příslušná čísla a témata | ||||||
| 	for rocnik in rocniky: | 	for rocnik, cisla, temata in zip(rocniky, rocnik_cisla, rocnik_temata): | ||||||
| 		k+=1 | 		# Do každého čísla nagenerujeme ke každému témátku pár úložek | ||||||
| 		cisla = rocnik_cisla[k-1] | 		for cislo in cisla: | ||||||
| 		temata = rocnik_temata[k-1] | 			print("Generuji úložky do {}-tého čísla".format(cislo.poradi)) | ||||||
| 		for ci in range(len(cisla)): | 			# Vzorák bude o dvě čísla dál | ||||||
| 			cislo = cisla[ci-1] | 			cislo_se_vzorakem = Cislo.objects.filter( | ||||||
| 			mozna_tema_vcn = cislo.cislonode.first_child | 					rocnik=rocnik, | ||||||
| 			while mozna_tema_vcn != None: | 					poradi=str(int(cislo.poradi) + 2), | ||||||
| 				if type(mozna_tema_vcn) != TemaVCisleNode: | 					) | ||||||
| 					mozna_tema_vcn = mozna_tema_vcn.succ | 			# Pokud není číslo pro vzorák, tak se dá do posledního čísla (i kdyby tam mělo být zadání i řešení...) | ||||||
| 					continue | 			# Tohle sice umožňuje vygenerovat vzorák do čísla dávno po konci témátka, ale to nám pro jednoduchost nevadí. | ||||||
| 				else: | 			if len(cislo_se_vzorakem) == 0: | ||||||
| 					tema = mozna_tema_vcn.tema | 				cislo_se_vzorakem = cisla[-1] | ||||||
|  | 			else: | ||||||
|  | 				cislo_se_vzorakem = cislo_se_vzorakem.first() | ||||||
| 
 | 
 | ||||||
| 				if not temata[int(tema.kod)-1][1] >= ci+2: | 			# FIXME: Tenhle generátor dát asi někam jinam | ||||||
| 					mozna_tema_vcn = mozna_tema_vcn.succ | 			def potomci(node): | ||||||
| 					continue | 				if not isinstance(node, TreeNode): | ||||||
|  | 					raise ValueError("Typ {} nemá potomky", type(node)) | ||||||
|  | 				current_child = node.first_child | ||||||
|  | 				while current_child is not None: | ||||||
|  | 					yield current_child | ||||||
|  | 					current_child = current_child.succ | ||||||
| 
 | 
 | ||||||
| 				for i in range(1, rnd.randint(1, 4)): | 			for mozna_tema_node in potomci(cislo.cislonode): | ||||||
| 					poc_op = rnd.randint(1, 4) | 				if not isinstance(mozna_tema_node, TemaVCisleNode): | ||||||
| 					poc_oboru = rnd.randint(1, 2) | 					continue | ||||||
| 					p = Uloha.objects.create( | 				tema_node = mozna_tema_node | ||||||
| 						nazev=": ".join([tema.nazev, | 				tema = tema_node.tema | ||||||
| 							"úloha {}.".format(i)]), | 					 | ||||||
|  | 				# Pokud už témátko skončilo, žádné úložky negenerujeme | ||||||
|  | 				# FIXME: Bylo by hezčí, kdyby se čísla předávala jako Cislo a ne jako int v té trojici (start, konec, tema) | ||||||
|  | 				if not temata[int(tema.kod)-1][1] >= int(cislo_se_vzorakem.poradi): | ||||||
|  | 					continue | ||||||
|  | 					 | ||||||
|  | 				# Generujeme 1 až 4 úložky k tomuto témátku do tohoto čísla | ||||||
|  | 				for kod in range(1, rnd.randint(1, 4)): | ||||||
|  | 					u = Uloha.objects.create( | ||||||
|  | 						nazev=": ".join([tema.nazev,  | ||||||
|  | 							"úloha {}.".format(kod)]), | ||||||
| 						nadproblem=tema, | 						nadproblem=tema, | ||||||
| 						stav=Problem.STAV_ZADANY, | 						stav=Problem.STAV_ZADANY, | ||||||
| 						zamereni=tema.zamereni, | 						zamereni=tema.zamereni, | ||||||
| 						autor=tema.autor, | 						autor=tema.autor, | ||||||
| 						garant=tema.garant, | 						garant=tema.garant, | ||||||
| 						kod=str(i), | 						kod=str(kod), | ||||||
| 						cislo_zadani=cislo, | 						cislo_zadani=cislo, | ||||||
| 						cislo_reseni=cisla[ci+2-1], | 						cislo_reseni=cislo_se_vzorakem, | ||||||
| 						cislo_deadline=cisla[ci+2-1], | 						cislo_deadline=cislo_se_vzorakem, | ||||||
| 						max_body = rnd.randint(1, 8) | 						max_body = rnd.randint(1, 8) | ||||||
| 					) | 					) | ||||||
| 
 | 					 | ||||||
| 					p.opravovatele.set(rnd.sample(organizatori, poc_op)) | 					poc_opravovatelu = rnd.randint(1, 4) | ||||||
| 
 | 					u.opravovatele.set(rnd.sample(organizatori, poc_opravovatelu))   | ||||||
| 					text_zadani = Text.objects.create( | 					 | ||||||
| 						na_web = " ".join( | 					# Samotný obsah následně vzniklého Textu zadání | ||||||
| 						[rnd.choice(sloveso), | 					obsah = " ".join( | ||||||
| 						rnd.choice(koho), | 						[rnd.choice(sloveso),  | ||||||
| 						rnd.choice(ceho), | 						rnd.choice(koho),  | ||||||
| 						rnd.choice(jmeno), | 						rnd.choice(ceho),  | ||||||
| 						rnd.choice(kde)] | 						rnd.choice(jmeno),  | ||||||
| 						), |  | ||||||
| 						do_cisla = " ".join( |  | ||||||
| 						[rnd.choice(sloveso), |  | ||||||
| 						rnd.choice(koho), |  | ||||||
| 						rnd.choice(ceho), |  | ||||||
| 						rnd.choice(jmeno), |  | ||||||
| 						rnd.choice(kde)] | 						rnd.choice(kde)] | ||||||
| 						) | 						) | ||||||
| 					) | 					text_zadani = Text.objects.create( | ||||||
|  | 						na_web = obsah, | ||||||
|  | 						do_cisla = obsah, | ||||||
|  | 						) | ||||||
| 					zad = TextNode.objects.create(text = text_zadani) | 					zad = TextNode.objects.create(text = text_zadani) | ||||||
| 					uloha_zadani = UlohaZadaniNode.objects.create(uloha=p, first_child = zad) | 					uloha_zadani = UlohaZadaniNode.objects.create(uloha=u, first_child = zad) | ||||||
| 					p.ulohazadaninode = uloha_zadani | 					u.ulohazadaninode = uloha_zadani | ||||||
| 					otec_syn(mozna_tema_vcn, uloha_zadani) # TODO dělá se podproblém takto??? TODO |  | ||||||
| 
 | 
 | ||||||
|  | 					# FIXME: Tohle dává zadání vždy jako prvního potomka témátka, spec. se naskládají v opačném pořadí a nemůže mezi nimi vzniknout žádný (orgo-)text | ||||||
|  | 					otec_syn(tema_node, uloha_zadani) | ||||||
|  | 					 | ||||||
|  | 					# Text vzoráku stejně | ||||||
|  | 					obsah = rnd.choice(reseni) | ||||||
| 					text_vzoraku = Text.objects.create( | 					text_vzoraku = Text.objects.create( | ||||||
| 						na_web = rnd.choice(reseni), | 						na_web = obsah, | ||||||
| 						do_cisla = rnd.choice(reseni) | 						do_cisla = obsah, | ||||||
| 					) | 					) | ||||||
| 					vzorak = TextNode.objects.create(text = text_vzoraku) | 					vzorak = TextNode.objects.create(text = text_vzoraku) | ||||||
| 					uloha_vzorak = UlohaVzorakNode.objects.create(uloha=p, first_child = vzorak) | 					uloha_vzorak = UlohaVzorakNode.objects.create(uloha=u, first_child = vzorak) | ||||||
| 					p.UlohaVzorakNode = uloha_vzorak | 					u.UlohaVzorakNode = uloha_vzorak | ||||||
| 					res_tema_vcn = cisla[ci+2-1].cislonode.first_child |  | ||||||
| 					while res_tema_vcn.tema != tema: |  | ||||||
| 						res_tema_vcn = res_tema_vcn.succ |  | ||||||
| 					otec_syn(res_tema_vcn, uloha_vzorak) |  | ||||||
| 
 | 
 | ||||||
| 					p.save() | 					# Najdeme správný TemaVCisleNode pro vložení vzoráku | ||||||
| 
 | 					res_tema_node = None; | ||||||
| 				mozna_tema_vcn = mozna_tema_vcn.succ | 					for node in potomci(cislo_se_vzorakem.cislonode): | ||||||
|  | 						if isinstance(node, TemaVCisleNode) and node.tema == tema: | ||||||
|  | 							res_tema_node = node | ||||||
|  | 					if res_tema_node is None: | ||||||
|  | 						raise LookupError("Nenalezen Node pro vložení vzoráku") | ||||||
|  | 					# FIXME: Stejný problém jako výše: vzoráky se dají na začátek v opačném pořadí. | ||||||
|  | 					otec_syn(res_tema_node, uloha_vzorak) | ||||||
|  | 					 | ||||||
|  | 					u.save() | ||||||
| 	return | 	return | ||||||
| 
 | 
 | ||||||
| def gen_novinky(rnd, organizatori): | def gen_novinky(rnd, organizatori): | ||||||
|  | @ -594,6 +646,7 @@ def create_test_data(size = 6, rnd = None): | ||||||
| 	rocniky = gen_rocniky(last_rocnik, size) | 	rocniky = gen_rocniky(last_rocnik, size) | ||||||
| 
 | 
 | ||||||
| 	# cisla | 	# cisla | ||||||
|  | 	# rocnik_cisla je pole polí čísel (typ Cislo), vnitřní pole odpovídají jednotlivým ročníkům. | ||||||
| 	rocnik_cisla = gen_cisla(rnd, rocniky) | 	rocnik_cisla = gen_cisla(rnd, rocniky) | ||||||
| 
 | 
 | ||||||
| 	# generování obyčejných úloh do čísel | 	# generování obyčejných úloh do čísel | ||||||
|  | @ -601,6 +654,7 @@ def create_test_data(size = 6, rnd = None): | ||||||
| 
 | 
 | ||||||
| 	# generování témat, zatím v prvních třech číslech po jednom | 	# generování témat, zatím v prvních třech číslech po jednom | ||||||
| 	# FIXME: více témat | 	# FIXME: více témat | ||||||
|  | 	# rocnik_temata je pole polí trojic (první číslo :int, poslední číslo :int, téma:Tema), přičemž každé vnitřní pole odpovídá ročníku a FIXME: je to takhle fuj a když to někdo vidí poprvé, tak je z toho smutný, protože vůbec neví, co se děje a co má čekat. | ||||||
| 	rocnik_temata = gen_temata(rnd, rocniky, rocnik_cisla, organizatori) | 	rocnik_temata = gen_temata(rnd, rocniky, rocnik_cisla, organizatori) | ||||||
| 
 | 
 | ||||||
| 	# generování úloh k tématům ve všech číslech | 	# generování úloh k tématům ve všech číslech | ||||||
|  |  | ||||||
							
								
								
									
										286
									
								
								seminar/treelib.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										286
									
								
								seminar/treelib.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,286 @@ | ||||||
|  | from django.core.exceptions import ObjectDoesNotExist | ||||||
|  | # NOTE: node.prev a node.succ jsou implementovány přímo v models.TreeNode | ||||||
|  | # TODO: Všechny tyto funkce se naivně spoléhají na to, že jako parametr dostanou nějaký TreeNode (některé možná zvládnou i None) | ||||||
|  | # TODO: Chceme, aby všechno nějak zvládlo None jako parametr. | ||||||
|  | 
 | ||||||
|  | # Slouží k debugování pro rychlé získání představy o podobě podstromu pod tímto TreeNode. | ||||||
|  | def print_tree(node,indent=0): | ||||||
|  | 	# FIXME: Tady se spoléháme na to, že nedeklarovaný primární klíč se jmenuje by default 'id', což není úplně správně | ||||||
|  | 	print("{}{} (id: {})".format(" "*indent,node, node.id)) | ||||||
|  | 	if node.first_child: | ||||||
|  | 		print_tree(node.first_child, indent=indent+2) | ||||||
|  | 	if node.succ: | ||||||
|  | 		print_tree(node.succ, indent=indent) | ||||||
|  | 
 | ||||||
|  | # Django je trošku hloupé, takže node.prev nevrací None, ale hází django.core.exceptions.ObjectDoesNotExist | ||||||
|  | def safe_pred(node): | ||||||
|  | 	try: | ||||||
|  | 		return node.prev | ||||||
|  | 	except ObjectDoesNotExist: | ||||||
|  | 		return None | ||||||
|  | 
 | ||||||
|  | def first_brother(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		return None | ||||||
|  | 	brother = node | ||||||
|  | 	while safe_pred(brother) is not None: | ||||||
|  | 		brother = safe_pred(brother) | ||||||
|  | 	return brother | ||||||
|  | 
 | ||||||
|  | # A to samé pro .father_of_first | ||||||
|  | def safe_father_of_first(node): | ||||||
|  | 	first = first_brother(node) | ||||||
|  | 	try: | ||||||
|  | 		return first.father_of_first | ||||||
|  | 	except ObjectDoesNotExist: | ||||||
|  | 		return None | ||||||
|  | 
 | ||||||
|  | ## Rodinné vztahy | ||||||
|  | def get_parent(node): | ||||||
|  | 	# Nejdřív získáme prvního potomka... | ||||||
|  | 	while safe_pred(node) is not None: | ||||||
|  | 		node = safe_pred(node) | ||||||
|  | 	# ... a z prvního potomka umíme najít rodiče | ||||||
|  | 	return safe_father_of_first(node) | ||||||
|  | 
 | ||||||
|  | # Obecný next: další Node v "the-right-order" pořadí (já, pak potomci, pak sousedé) | ||||||
|  | def general_next(node): | ||||||
|  | 	# Máme potomka? | ||||||
|  | 	if node.first_child is not None: | ||||||
|  | 		return node.first_child | ||||||
|  | 	# Nemáme potomka. | ||||||
|  | 	# Chceme najít následníka sebe, nebo některého (toho nejblíž příbuzného) z našich předků (tatínka, dědečka, ...) | ||||||
|  | 	while node.succ is None: | ||||||
|  | 		node = get_parent(node) | ||||||
|  | 		if node is None: | ||||||
|  | 			return None	# žádný z předků nemá následníka, takže žádny vrchol nenásleduje. | ||||||
|  | 	return node.succ | ||||||
|  | 
 | ||||||
|  | def last_brother(node): | ||||||
|  | 	while node.succ is not None: | ||||||
|  | 		node = node.succ | ||||||
|  | 	return node | ||||||
|  | 
 | ||||||
|  | def general_prev(node): | ||||||
|  | 	# Předchůdce je buď rekurzivně poslední potomek předchůdce, nebo náš otec. | ||||||
|  | 	# Otce vyřešíme nejdřív: | ||||||
|  | 	if safe_pred(node) is None: | ||||||
|  | 		return safe_father_of_first(node) | ||||||
|  | 	pred = safe_pred(node) | ||||||
|  | 	while pred.first_child is not None: | ||||||
|  | 		pred = last_brother(pred.first_child) | ||||||
|  | 	# pred nyní nemá žádné potomky, takže je to poslední rekurzivní potomek původního předchůdce | ||||||
|  | 	return pred | ||||||
|  | 
 | ||||||
|  | # Generátor pravých bratrů (konkrétně sebe a následujících potomků) | ||||||
|  | # Generátor potomků níže spoléhá na to, že se tohle dá volat i s parametrem None. | ||||||
|  | def me_and_right_brothers(node): | ||||||
|  | 	current = node | ||||||
|  | 	while current is not None: | ||||||
|  | 		yield current | ||||||
|  | 		current = current.succ | ||||||
|  | 
 | ||||||
|  | def right_brothers(node): | ||||||
|  | 	generator = me_and_right_brothers(node.succ) | ||||||
|  | 	for item in generator: | ||||||
|  | 		yield item | ||||||
|  | 
 | ||||||
|  | # Generátor všech sourozenců (vč. sám sebe) | ||||||
|  | def all_brothers(node): | ||||||
|  | 	# Najdeme prvního bratra | ||||||
|  | 	fb = first_brother(node) | ||||||
|  | 	marb = me_and_right_brothers(fb) | ||||||
|  | 	for cur in marb: | ||||||
|  | 		yield cur | ||||||
|  | 	 | ||||||
|  | def all_proper_brothers(node): | ||||||
|  | 	all = all_brothers(node) | ||||||
|  | 	for br in all: | ||||||
|  | 		if br is node: | ||||||
|  | 			continue | ||||||
|  | 		yield br | ||||||
|  | 
 | ||||||
|  | # Generátor potomků | ||||||
|  | def all_children(node): | ||||||
|  | 	brothers = all_brothers(node.first_child) | ||||||
|  | 	for br in brothers: | ||||||
|  | 		yield br | ||||||
|  | 
 | ||||||
|  | # Generátor následníků v "the-right-order" | ||||||
|  | # Bez tohoto vrcholu | ||||||
|  | def all_following(node): | ||||||
|  | 	current = general_next(node) | ||||||
|  | 	while current is not None: | ||||||
|  | 		yield current | ||||||
|  | 		current = general_next(current) | ||||||
|  | 
 | ||||||
|  | ## Filtrační hledání | ||||||
|  | # Najdi dalšího bratra nějakého typu, nebo None. | ||||||
|  | # hledá i podtřídy, i.e. get_next_brother_of_type(neco, TreeNode) je prostě succ. | ||||||
|  | def get_next_brother_of_type(node, type): | ||||||
|  | 	for current in right_brothers(node): | ||||||
|  | 		if isinstance(current, type): | ||||||
|  | 			return current | ||||||
|  | 	return None | ||||||
|  | 	 | ||||||
|  | def get_prev_brother_of_type(node, type): | ||||||
|  | 	# Na tohle není rozumný generátor, ani ho asi nechceme, prostě to implementujeme cyklem. | ||||||
|  | 	current = node | ||||||
|  | 	while safe_pred(current) is not None: | ||||||
|  | 		current = safe_pred(current) | ||||||
|  | 		if isinstance(current, type): | ||||||
|  | 			return current | ||||||
|  | 	return None | ||||||
|  | 
 | ||||||
|  | # Totéž pro "the-right-order" pořadí | ||||||
|  | def get_next_node_of_type(node, type): | ||||||
|  | 	for cur in all_folowing(node): | ||||||
|  | 		if isinstance(cur, type): | ||||||
|  | 			return cur | ||||||
|  | 	return None | ||||||
|  | 
 | ||||||
|  | def get_prev_node_of_type(node, type): | ||||||
|  | 	# Na tohle není rozumný generátor, ani ho asi nechceme, prostě to implementujeme cyklem. | ||||||
|  | 	current = node | ||||||
|  | 	while general_prev(current) is not None: | ||||||
|  | 		current = general_prev(current) | ||||||
|  | 		if isinstance(current, type): | ||||||
|  | 			return current | ||||||
|  | 	return None | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Editace stromu: | ||||||
|  | def create_node_after(predecessor, type, **kwargs): | ||||||
|  | 	new_node = type.objects.create(**kwargs) | ||||||
|  | 	new_node.save() | ||||||
|  | 	succ = predecessor.succ | ||||||
|  | 	predecessor.succ = new_node | ||||||
|  | 	predecessor.save() | ||||||
|  | 	new_node.succ = succ | ||||||
|  | 	new_node.save() | ||||||
|  | 
 | ||||||
|  | # Vyrábí prvního syna, ostatní nalepí za (existují-li) | ||||||
|  | def create_child(parent, type, **kwargs): | ||||||
|  | 	new_node = type.objects.create(**kwargs) | ||||||
|  | 	new_node.save() | ||||||
|  | 	orig_child = parent.first_child | ||||||
|  | 	parent.first_child = new_node | ||||||
|  | 	parent.save() | ||||||
|  | 	if orig_child is not None: | ||||||
|  | 		# Přidáme původního prvního syna jako potomka nového vrcholu | ||||||
|  | 		new_node.succ = orig_child | ||||||
|  | 		new_node.save() | ||||||
|  | 
 | ||||||
|  | def create_node_before(successor, type, **kwargs): | ||||||
|  | 	if safe_pred(successor) is not None: | ||||||
|  | 		# Easy: přidáme za předchůdce | ||||||
|  | 		create_node_after(successor.prev, type, **kwargs) | ||||||
|  | 	# Nemáme předchůdce, jsme tedy první z bratrů. Máme otce? | ||||||
|  | 	if safe_father_of_first(successor) is not None: | ||||||
|  | 		# Ano -> Easy: vyrobíme nového potomka | ||||||
|  | 		# NOTE: Tohle je možná trošku abuse implementace výše, ale to nevadí moc... | ||||||
|  | 		create_child(successor.father_of_first, type, **kwargs) | ||||||
|  | 	# Teď už easy: Jsme sirotci, takže se vyrobíme a našeho následníka si přidáme jako succ | ||||||
|  | 	new = type.objects.create(**kwargs) | ||||||
|  | 	new.succ = successor | ||||||
|  | 	new.save() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # ValueError, pokud je (aspoň) jeden parametr None | ||||||
|  | def swap(node, other): | ||||||
|  | 	raise NotImplementedError("YAGNI (You aren't gonna need it).") | ||||||
|  | 
 | ||||||
|  | # Exception, kterou některé metody při špatném použití mohou házet | ||||||
|  | # Hlavní důvod je možnost informovat o selhání, aby se příslušný problém dal zobrazit na frontendu, | ||||||
|  | class TreeLibError(RuntimeError): | ||||||
|  | 	pass | ||||||
|  | 
 | ||||||
|  | def swap_pred(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.") | ||||||
|  | 	pred = safe_pred(node) | ||||||
|  | 	if pred is None: | ||||||
|  | 		raise TreeLibError("Nelze posunout vlevo, není tam žádný další uzel.") | ||||||
|  | 	pre_pred = safe_pred(pred) | ||||||
|  | 	succ = node.succ | ||||||
|  | 
 | ||||||
|  | 	if pre_pred is not None: | ||||||
|  | 		pre_pred.succ = node | ||||||
|  | 		pre_pred.save() | ||||||
|  | 	node.succ = pred | ||||||
|  | 	node.save() | ||||||
|  | 	pred.succ = succ | ||||||
|  | 	pred.save() | ||||||
|  | 
 | ||||||
|  | def swap_succ(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.") | ||||||
|  | 	succ = node.succ | ||||||
|  | 	if succ is None: | ||||||
|  | 		raise TreeLibError("Nelze posunout vpravo, není tam žádný další uzel") | ||||||
|  | 	pred = safe_pred(node) | ||||||
|  | 	post_succ = succ.succ | ||||||
|  | 
 | ||||||
|  | 	if pred is not None: | ||||||
|  | 		pred.succ = succ | ||||||
|  | 		pred.save() | ||||||
|  | 	succ.succ = node | ||||||
|  | 	succ.save() | ||||||
|  | 	node.succ = post_succ | ||||||
|  | 	node.save() | ||||||
|  | 
 | ||||||
|  | # Rotace stromu | ||||||
|  | # Dokumentace viz wiki: | ||||||
|  | # (lower bude jednoduchá rotace, ne mega, existence jednoduché rotace mi došla až po nakreslení obrázku) | ||||||
|  | def raise_node(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.") | ||||||
|  | 	# Pojmenování viz WIKI (as of 2020-03-19 01:33:44 GMT+1) | ||||||
|  | 	# FIXME: Velmi naivní, chybí error checky | ||||||
|  | 	D = node | ||||||
|  | 	C = get_parent(D) | ||||||
|  | 	E = C.succ | ||||||
|  | 	subtree4_head = D.first_child | ||||||
|  | 	subtree4_tail = last_brother(subtree4_head) | ||||||
|  | 	subtree3P_head = D.succ | ||||||
|  | 	subtree3L_head = C.first_child | ||||||
|  | 	subtree3L_tail = safe_pred(D) | ||||||
|  | 	 | ||||||
|  | 	# Prostor pro motlitbu... | ||||||
|  | 	pass | ||||||
|  | 	 | ||||||
|  | 	# Amen. | ||||||
|  | 	C.succ = D | ||||||
|  | 	C.save() | ||||||
|  | 	D.succ = E | ||||||
|  | 	D.save() | ||||||
|  | 	subtree3L_tail.succ = None | ||||||
|  | 	subtree3L_tail.save() | ||||||
|  | 	subtree4_tail.succ = subtree3P.head | ||||||
|  | 	subtree4_tail.save() | ||||||
|  | 
 | ||||||
|  | 	# To by mělo být všechno... | ||||||
|  | 
 | ||||||
|  | def lower_node(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.") | ||||||
|  | 	# Pojmenování viz WIKI (as of 2020-03-19 01:33:44 GMT+1) | ||||||
|  | 	# FIXME: Velmi naivní, chybí error checky | ||||||
|  | 	C = node | ||||||
|  | 	D = C.succ | ||||||
|  | 	B = safe_pred(C) | ||||||
|  | 	subtree2_head = B.first_child | ||||||
|  | 	subtree2_tail = last_brother(subtree2_head) | ||||||
|  | 	 | ||||||
|  | 	# Prostor pro motlitbu... | ||||||
|  | 	pass | ||||||
|  | 	 | ||||||
|  | 	# Amen. | ||||||
|  | 	B.succ = D | ||||||
|  | 	B.save() | ||||||
|  | 	subtree2_tail.succ = C | ||||||
|  | 	subtree2_tail.save() | ||||||
|  | 
 | ||||||
|  | 	# To by mělo být všechno... | ||||||
|  | @ -8,8 +8,8 @@ from django.contrib.auth import views as auth_views | ||||||
| staff_member_required = user_passes_test(lambda u: u.is_staff) | staff_member_required = user_passes_test(lambda u: u.is_staff) | ||||||
| 
 | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
|   path('aktualni/temata/', views.TemataRozcestnikView), | #	path('aktualni/temata/', views.TemataRozcestnikView), | ||||||
| 	path('<int:rocnik>/t<int:tematko>/', views.TematkoView), | #	path('<int:rocnik>/t<int:tematko>/', views.TematkoView), | ||||||
| 
 | 
 | ||||||
| 	# REDIRECTy | 	# REDIRECTy | ||||||
| 	path('jak-resit/', RedirectView.as_view(url='/co-je-MaM/jak-resit/')), | 	path('jak-resit/', RedirectView.as_view(url='/co-je-MaM/jak-resit/')), | ||||||
|  | @ -25,6 +25,7 @@ urlpatterns = [ | ||||||
| 	path('rocnik/<int:rocnik>/', views.RocnikView.as_view(), name='seminar_rocnik'), | 	path('rocnik/<int:rocnik>/', views.RocnikView.as_view(), name='seminar_rocnik'), | ||||||
| 	path('cislo/<int:rocnik>.<int:cislo>/', views.CisloView.as_view(), name='seminar_cislo'), # odkomentované jenom kvůli testování archivu | 	path('cislo/<int:rocnik>.<int:cislo>/', views.CisloView.as_view(), name='seminar_cislo'), # odkomentované jenom kvůli testování archivu | ||||||
| 	path('problem/<int:pk>/', views.ProblemView.as_view(), name='seminar_problem'), | 	path('problem/<int:pk>/', views.ProblemView.as_view(), name='seminar_problem'), | ||||||
|  | 	path('treenode/<int:pk>/', views.TreeNodeView.as_view(), name='seminar_treenode'), | ||||||
| 	#path('problem/(?P<pk>\d+)/(?P<prispevek>\d+)/', views.PrispevekView.as_view(), name='seminar_problem_prispevek'), | 	#path('problem/(?P<pk>\d+)/(?P<prispevek>\d+)/', views.PrispevekView.as_view(), name='seminar_problem_prispevek'), | ||||||
| 
 | 
 | ||||||
| 	# Soustredeni | 	# Soustredeni | ||||||
|  | @ -59,8 +60,8 @@ urlpatterns = [ | ||||||
| 	), | 	), | ||||||
| 
 | 
 | ||||||
| 	# Zadani | 	# Zadani | ||||||
| 	path('zadani/aktualni/', views.AktualniZadaniView, name='seminar_aktualni_zadani'), | 	path('zadani/aktualni/', views.AktualniZadaniView.as_view(), name='seminar_aktualni_zadani'), | ||||||
| 	path('zadani/temata/', views.ZadaniTemataView, name='seminar_temata'), | #	path('zadani/temata/', views.ZadaniTemataView, name='seminar_temata'), | ||||||
| 	#path('zadani/vysledkova-listina/', views.ZadaniAktualniVysledkovkaView, name='seminar_vysledky'), | 	#path('zadani/vysledkova-listina/', views.ZadaniAktualniVysledkovkaView, name='seminar_vysledky'), | ||||||
| 	path('stare-novinky/', views.StareNovinkyView.as_view(), name='stare_novinky'), | 	path('stare-novinky/', views.StareNovinkyView.as_view(), name='stare_novinky'), | ||||||
| 
 | 
 | ||||||
|  | @ -107,8 +108,6 @@ urlpatterns = [ | ||||||
| 	path('auth/login/', views.LoginView.as_view(), name='login'), | 	path('auth/login/', views.LoginView.as_view(), name='login'), | ||||||
| 	path('auth/logout/', views.LogoutView.as_view(), name='logout'), | 	path('auth/logout/', views.LogoutView.as_view(), name='logout'), | ||||||
| 	path('auth/resitel/', views.ResitelView.as_view(), name='seminar_resitel'), | 	path('auth/resitel/', views.ResitelView.as_view(), name='seminar_resitel'), | ||||||
| 	path('autocomplete/skola/',views.SkolaAutocomplete.as_view(), name='autocomplete_skola'), |  | ||||||
| 	path('autocomplete/resitel/',views.ResitelAutocomplete.as_view(), name='autocomplete_resitel'), |  | ||||||
| 	path('auth/reset_password/', views.PasswordResetView.as_view(), name='reset_password'), | 	path('auth/reset_password/', views.PasswordResetView.as_view(), name='reset_password'), | ||||||
| 	path('auth/change_password/', views.PasswordChangeView.as_view(), name='change_password'), | 	path('auth/change_password/', views.PasswordChangeView.as_view(), name='change_password'), | ||||||
| 	path('auth/reset_password_done/', views.PasswordResetDoneView.as_view(), name='reset_password_done'), | 	path('auth/reset_password_done/', views.PasswordResetDoneView.as_view(), name='reset_password_done'), | ||||||
|  | @ -116,8 +115,13 @@ urlpatterns = [ | ||||||
| 	path('auth/reset_password_complete/', views.PasswordResetCompleteView.as_view(), name='reset_password_complete'), | 	path('auth/reset_password_complete/', views.PasswordResetCompleteView.as_view(), name='reset_password_complete'), | ||||||
| 	path('auth/resitel_edit', views.resitelEditView, name='seminar_resitel_edit'), | 	path('auth/resitel_edit', views.resitelEditView, name='seminar_resitel_edit'), | ||||||
| 
 | 
 | ||||||
|  | 	# Autocomplete | ||||||
|  | 	path('autocomplete/skola/',views.SkolaAutocomplete.as_view(), name='autocomplete_skola'), | ||||||
|  | 	path('autocomplete/resitel/',views.ResitelAutocomplete.as_view(), name='autocomplete_resitel'), | ||||||
|  | 	path('autocomplete/problem/odevzdatelny',views.OdevzdatelnyProblemAutocomplete.as_view(), name='autocomplete_problem_odevzdatelny'), | ||||||
| 
 | 
 | ||||||
| 	path('temp/add_solution', views.AddSolutionView.as_view(),name='seminar_vloz_reseni'), | 	path('temp/add_solution', views.AddSolutionView.as_view(),name='seminar_vloz_reseni'), | ||||||
|  | 	path('temp/submit_solution', views.SubmitSolutionView.as_view(),name='seminar_nahraj_reseni'), | ||||||
| 
 | 
 | ||||||
| 	path('', views.TitulniStranaView.as_view(), name='titulni_strana'), | 	path('', views.TitulniStranaView.as_view(), name='titulni_strana'), | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,6 +4,8 @@ import datetime | ||||||
| from django.contrib.auth.decorators import user_passes_test | from django.contrib.auth.decorators import user_passes_test | ||||||
| from html.parser import HTMLParser  | from html.parser import HTMLParser  | ||||||
| 
 | 
 | ||||||
|  | import seminar.models as m | ||||||
|  | 
 | ||||||
| staff_member_required = user_passes_test(lambda u: u.is_staff) | staff_member_required = user_passes_test(lambda u: u.is_staff) | ||||||
| 
 | 
 | ||||||
| class FirstTagParser(HTMLParser): | class FirstTagParser(HTMLParser): | ||||||
|  | @ -43,7 +45,6 @@ def from_roman(rom): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def seznam_problemu(): | def seznam_problemu(): | ||||||
| 	from .models import Problem, Resitel, Rocnik, Reseni, Cislo |  | ||||||
| 	problemy = [] | 	problemy = [] | ||||||
| 
 | 
 | ||||||
| 	# Pomocna fce k formatovani problemovych hlasek | 	# Pomocna fce k formatovani problemovych hlasek | ||||||
|  | @ -65,27 +66,26 @@ def seznam_problemu(): | ||||||
| 
 | 
 | ||||||
| 	# Duplicita jmen | 	# Duplicita jmen | ||||||
| 	jmena = {} | 	jmena = {} | ||||||
| 	for r in Resitel.objects.all(): | 	for r in m.Resitel.objects.all(): | ||||||
| 		j = r.plne_jmeno() | 		j = r.plne_jmeno() | ||||||
| 		if j not in jmena: | 		if j not in jmena: | ||||||
| 			jmena[j] = [] | 			jmena[j] = [] | ||||||
| 		jmena[j].append(r) | 		jmena[j].append(r) | ||||||
| 	for j in jmena: | 	for j in jmena: | ||||||
| 		if len(jmena[j]) > 1: | 		if len(jmena[j]) > 1: | ||||||
| 			prb(Resitel, u'Duplicitní jméno "%s"' % (j, ), jmena[j]) | 			prb(m.Resitel, u'Duplicitní jméno "%s"' % (j, ), jmena[j]) | ||||||
| 
 | 
 | ||||||
| 	# Data maturity a narození | 	# Data maturity a narození | ||||||
| 	for r in Resitel.objects.all(): | 	for r in m.Resitel.objects.all(): | ||||||
| 		if not r.rok_maturity: | 		if not r.rok_maturity: | ||||||
| 			prb(Resitel, u'Neznámý rok maturity', [r]) | 			prb(m.Resitel, u'Neznámý rok maturity', [r]) | ||||||
| 		if r.rok_maturity and (r.rok_maturity < 1990 or r.rok_maturity > datetime.date.today().year + 10): | 		if r.rok_maturity and (r.rok_maturity < 1990 or r.rok_maturity > datetime.date.today().year + 10): | ||||||
| 			prb(Resitel, u'Podezřelé datum maturity', [r]) | 			prb(m.Resitel, u'Podezřelé datum maturity', [r]) | ||||||
| 		if r.datum_narozeni and (r.datum_narozeni.year < 1970 or r.datum_narozeni.year > datetime.date.today().year - 12): | 		if r.datum_narozeni and (r.datum_narozeni.year < 1970 or r.datum_narozeni.year > datetime.date.today().year - 12): | ||||||
| 			prb(Resitel, u'Podezřelé datum narození', [r]) | 			prb(m.Resitel, u'Podezřelé datum narození', [r]) | ||||||
| #        if not r.email: | #        if not r.email: | ||||||
| #            prb(Resitel, u'Neznámý email', [r]) | #            prb(Resitel, u'Neznámý email', [r]) | ||||||
| 
 | 
 | ||||||
| 	return problemy | 	return problemy | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								seminar/views/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								seminar/views/__init__.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | ||||||
|  | from .views_all import * | ||||||
|  | from .autocomplete import * | ||||||
							
								
								
									
										63
									
								
								seminar/views/autocomplete.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								seminar/views/autocomplete.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,63 @@ | ||||||
|  | from dal import autocomplete | ||||||
|  | from django.shortcuts import get_object_or_404 | ||||||
|  | 
 | ||||||
|  | import seminar.models as m | ||||||
|  | from .helpers import LoginRequiredAjaxMixin | ||||||
|  | 
 | ||||||
|  | class SkolaAutocomplete(autocomplete.Select2QuerySetView): | ||||||
|  | 	def get_queryset(self): | ||||||
|  |         	# Don't forget to filter out results depending on the visitor ! | ||||||
|  | 		qs = m.Skola.objects.all() | ||||||
|  | 		if self.q: | ||||||
|  | 			qs = qs.filter( | ||||||
|  | 				Q(nazev__istartswith=self.q)| | ||||||
|  | 				Q(kratky_nazev__istartswith=self.q)| | ||||||
|  | 				Q(ulice__istartswith=self.q)| | ||||||
|  | 				Q(mesto__istartswith=self.q)) | ||||||
|  | 		 | ||||||
|  | 		return qs | ||||||
|  | 
 | ||||||
|  | class ResitelAutocomplete(LoginRequiredAjaxMixin,autocomplete.Select2QuerySetView): | ||||||
|  | 	def get_queryset(self): | ||||||
|  | 		qs = m.Resitel.objects.all() | ||||||
|  | 		if self.q: | ||||||
|  | 			qs = qs.filter( | ||||||
|  | 				Q(osoba__jmeno__startswith=self.q)| | ||||||
|  | 				Q(osoba__prijmeni__startswith=self.q)| | ||||||
|  | 				Q(osoba__prezdivka__startswith=self.q) | ||||||
|  | 				) | ||||||
|  | 		return qs | ||||||
|  | 
 | ||||||
|  | class OdevzdatelnyProblemAutocomplete(autocomplete.Select2QuerySetView): | ||||||
|  | 	def get_queryset(self): | ||||||
|  | 		nastaveni = get_object_or_404(m.Nastaveni) | ||||||
|  | 		rocnik = nastaveni.aktualni_rocnik | ||||||
|  | 		temata = m.Tema.objects.filter(rocnik=rocnik, stav=m.Problem.STAV_ZADANY) | ||||||
|  | 		ulohy = m.Uloha.objects.filter(cislo_deadline__rocnik = rocnik) | ||||||
|  | 		ulohy.union(temata) | ||||||
|  | 		qs = ulohy | ||||||
|  | 		if self.q: | ||||||
|  | 			qs = qs.filter( | ||||||
|  | 					Q(nazev__startswith=self.q)) | ||||||
|  | 		return qs | ||||||
|  | 
 | ||||||
|  | # Ceka na autocomplete v3 | ||||||
|  | # class OrganizatorAutocomplete(autocomplete.Select2QuerySetView): | ||||||
|  | # 	def get_queryset(self): | ||||||
|  | # 		if not self.request.user.is_authenticated(): | ||||||
|  | # 			return Organizator.objects.none() | ||||||
|  | # | ||||||
|  | # 		qs = aktivniOrganizatori() | ||||||
|  | # | ||||||
|  | # 		if self.q: | ||||||
|  | # 			if self.q[0] == "!": | ||||||
|  | # 				qs = Organizator.objects.all() | ||||||
|  | # 				query = self.q[1:] | ||||||
|  | # 			else: | ||||||
|  | # 				query = self.q | ||||||
|  | # 			qs = qs.filter( | ||||||
|  | # 				Q(prezdivka__isstartswith=query)| | ||||||
|  | # 				Q(user__first_name__isstartswith=query)| | ||||||
|  | # 				Q(user__last_name__isstartswith=query)) | ||||||
|  | # | ||||||
|  | # 		return qs | ||||||
							
								
								
									
										8
									
								
								seminar/views/helpers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								seminar/views/helpers.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | ||||||
|  | from dal import autocomplete | ||||||
|  | 
 | ||||||
|  | class LoginRequiredAjaxMixin(object): | ||||||
|  | 	def dispatch(self, request, *args, **kwargs): | ||||||
|  | 		#if request.is_ajax() and not request.user.is_authenticated: # Pokud to otevřu jako stránku, tak se omezení neuplatní, takže to asi nechceme | ||||||
|  | 		if not request.user.is_authenticated: | ||||||
|  | 			return JsonResponse(data={'results': [], 'pagination': {}}, status=401) | ||||||
|  | 		return super(LoginRequiredAjaxMixin, self).dispatch(request, *args, **kwargs) | ||||||
							
								
								
									
										91
									
								
								seminar/views/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								seminar/views/utils.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,91 @@ | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | 
 | ||||||
|  | import datetime | ||||||
|  | from django.contrib.auth.decorators import user_passes_test | ||||||
|  | from html.parser import HTMLParser  | ||||||
|  | 
 | ||||||
|  | import seminar.models as m | ||||||
|  | 
 | ||||||
|  | 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: | ||||||
|  | 		if i not in d: | ||||||
|  | 			d[i] = 0 | ||||||
|  | 		d[i] += 1 | ||||||
|  | 	return d | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 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 = "" | ||||||
|  | 	for i, n in roman_numerals: | ||||||
|  | 		res += n * (num // i) | ||||||
|  | 		num %= i | ||||||
|  | 	return res | ||||||
|  | 
 | ||||||
|  | def from_roman(rom): | ||||||
|  | 	if not rom: | ||||||
|  | 		return 0 | ||||||
|  | 	for i, n in roman_numerals: | ||||||
|  | 		if rom.upper().startswith(n): | ||||||
|  | 			return i + from_roman(rom[len(n):]) | ||||||
|  | 	raise Exception('Invalid roman numeral: "%s"', rom) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def seznam_problemu(): | ||||||
|  | 	problemy = [] | ||||||
|  | 
 | ||||||
|  | 	# Pomocna fce k formatovani problemovych hlasek | ||||||
|  | 	def prb(cls, msg, objs=None): | ||||||
|  | 		s = u'<b>%s:</b> %s' % (cls.__name__, msg) | ||||||
|  | 		if objs: | ||||||
|  | 			s += u' [' | ||||||
|  | 			for o in objs: | ||||||
|  | 				try: | ||||||
|  | 					url = o.admin_url() | ||||||
|  | 				except: | ||||||
|  | 					url = None | ||||||
|  | 				if url: | ||||||
|  | 					s += u'<a href="%s">%s</a>, ' % (url, o.pk, ) | ||||||
|  | 				else: | ||||||
|  | 					s += u'%s, ' % (o.pk, ) | ||||||
|  | 			s = s[:-2] + u']' | ||||||
|  | 		problemy.append(s) | ||||||
|  | 
 | ||||||
|  | 	# Duplicita jmen | ||||||
|  | 	jmena = {} | ||||||
|  | 	for r in m.Resitel.objects.all(): | ||||||
|  | 		j = r.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, u'Duplicitní jméno "%s"' % (j, ), jmena[j]) | ||||||
|  | 
 | ||||||
|  | 	# Data maturity a narození | ||||||
|  | 	for r in m.Resitel.objects.all(): | ||||||
|  | 		if not r.rok_maturity: | ||||||
|  | 			prb(m.Resitel, u'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, u'Podezřelé datum maturity', [r]) | ||||||
|  | 		if r.datum_narozeni and (r.datum_narozeni.year < 1970 or r.datum_narozeni.year > datetime.date.today().year - 12): | ||||||
|  | 			prb(m.Resitel, u'Podezřelé datum narození', [r]) | ||||||
|  | #        if not r.email: | ||||||
|  | #            prb(Resitel, u'Neznámý email', [r]) | ||||||
|  | 
 | ||||||
|  | 	return problemy | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @ -9,20 +9,19 @@ from django.utils.translation import ugettext as _ | ||||||
| from django.http import Http404,HttpResponseBadRequest,HttpResponseRedirect | from django.http import Http404,HttpResponseBadRequest,HttpResponseRedirect | ||||||
| from django.db.models import Q, Sum, Count | from django.db.models import Q, Sum, Count | ||||||
| from django.views.decorators.csrf import ensure_csrf_cookie | from django.views.decorators.csrf import ensure_csrf_cookie | ||||||
| from django.views.generic.edit import FormView | from django.views.generic.edit import FormView, CreateView | ||||||
| from django.contrib.auth import authenticate, login, get_user_model, logout | from django.contrib.auth import authenticate, login, get_user_model, logout | ||||||
| from django.contrib.auth import views as auth_views | from django.contrib.auth import views as auth_views | ||||||
| from django.contrib.auth.models import User | from django.contrib.auth.models import User | ||||||
| from django.contrib.auth.mixins import LoginRequiredMixin | from django.contrib.auth.mixins import LoginRequiredMixin | ||||||
| from django.db import transaction | from django.db import transaction | ||||||
| from dal import autocomplete |  | ||||||
| 
 | 
 | ||||||
| import seminar.models as s | import seminar.models as s | ||||||
| from .models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni, Organizator, Resitel, Novinky, Soustredeni_Ucastnici, Pohadka, Tema, Clanek, Osoba, Skola # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci | from seminar.models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni, Organizator, Resitel, Novinky, Soustredeni_Ucastnici, Pohadka, Tema, Clanek, Osoba, Skola # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci | ||||||
| #from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva | #from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva | ||||||
| from . import utils | from seminar import utils,treelib | ||||||
| from .unicodecsv import UnicodeWriter | from .unicodecsv import UnicodeWriter | ||||||
| from .forms import PrihlaskaForm, LoginForm, ProfileEditForm | from seminar.forms import PrihlaskaForm, LoginForm, ProfileEditForm | ||||||
| import seminar.forms as f | import seminar.forms as f | ||||||
| 
 | 
 | ||||||
| from datetime import timedelta, date, datetime | from datetime import timedelta, date, datetime | ||||||
|  | @ -84,131 +83,106 @@ class ObalkovaniView(generic.ListView): | ||||||
| 		context['cislo'] = self.cislo | 		context['cislo'] = self.cislo | ||||||
| 		return context | 		return context | ||||||
| 
 | 
 | ||||||
|  | class TNLData(object): | ||||||
|  | 	def __init__(self,anode): | ||||||
|  | 		self.node = anode | ||||||
|  | 		self.children = [] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | def treenode_strom_na_seznamy(node): | ||||||
|  | 	out = TNLData(node) | ||||||
|  | 	for ch in treelib.all_children(node): | ||||||
|  | 		outitem = treenode_strom_na_seznamy(ch) | ||||||
|  | 		out.children.append(outitem) | ||||||
|  | 	return out | ||||||
| 
 | 
 | ||||||
| def AktualniZadaniView(request): | class TreeNodeView(generic.DetailView): | ||||||
| 	nastaveni = get_object_or_404(Nastaveni) | 	model = s.TreeNode | ||||||
| 	verejne = nastaveni.aktualni_cislo.verejne() | 	template_name = 'seminar/treenode.html' | ||||||
| 	problemy = Problem.objects.filter(cislo_zadani=nastaveni.aktualni_cislo).filter(stav = 'zadany') |  | ||||||
| 	ulohy = problemy.filter(typ = 'uloha').order_by('kod') |  | ||||||
| 	serialy = problemy.filter(typ = 'serial').order_by('kod') |  | ||||||
| 	jednorazove_problemy = [ulohy, serialy] |  | ||||||
| 	return render(request, 'seminar/zadani/AktualniZadani.html', |  | ||||||
| 			{'nastaveni': nastaveni, |  | ||||||
| 			 'jednorazove_problemy': jednorazove_problemy, |  | ||||||
| 			 'temata': verejna_temata(nastaveni.aktualni_rocnik), |  | ||||||
| 			 'verejne': verejne, |  | ||||||
| 				}, |  | ||||||
| 			) |  | ||||||
| 
 | 
 | ||||||
| def ZadaniTemataView(request): | 	def get_context_data(self,**kwargs): | ||||||
| 	nastaveni = get_object_or_404(Nastaveni) | 		context = super().get_context_data(**kwargs) | ||||||
| 	temata = verejna_temata(nastaveni.aktualni_rocnik) | 		context['tnldata'] = treenode_strom_na_seznamy(self.object) | ||||||
| 	for t in temata: | 		return context | ||||||
| 		if request.user.is_staff: | 	 | ||||||
| 			t.prispevky = t.prispevek_set.filter(problem=t) |  | ||||||
| 		else: |  | ||||||
| 			t.prispevky = t.prispevek_set.filter(problem=t, zverejnit=True) |  | ||||||
| 	return render(request, 'seminar/zadani/Temata.html', |  | ||||||
| 		{ |  | ||||||
| 			'temata': temata, |  | ||||||
| 		} |  | ||||||
| 	) |  | ||||||
| 
 | 
 | ||||||
| # TODO Napsat tuto funkci znovu rekurzivně podle Jethrorad. Potom se podívat, jak lehce se dá modifikovat pro Rozcestník. Pokud lehce, rozšířit ji. Pokud složitě - použít tuhle | class AktualniZadaniView(TreeNodeView): | ||||||
| def vytahniZLesaSeznam(tematko, koren, pouze_zajimave=False): | 	def get_object(self): | ||||||
| 	returnVal = [] | 		nastaveni = get_object_or_404(Nastaveni) | ||||||
|  | 		return nastaveni.aktualni_cislo.cislonode | ||||||
| 
 | 
 | ||||||
| 	stack = [] | 	def get_context_data(self,**kwargs): | ||||||
| 	stack.append((koren.first_child, 0, False)) #Tuple of node, depth and relevance | 		nastaveni = get_object_or_404(Nastaveni) | ||||||
| 
 | 		context = super().get_context_data(**kwargs) | ||||||
| 	while len(stack) > 0: | 		verejne = nastaveni.aktualni_cislo.verejne() | ||||||
| 		wn, wd, wr = stack.pop() | 		context['verejne'] = verejne	 | ||||||
| 
 | 		return context | ||||||
| 		if wn.succ != None: |  | ||||||
| 			stack.append((wn.succ, wd, wr)) |  | ||||||
| 		if isinstance(wn, s.TemaVCisleNode): |  | ||||||
| 			print("TEMA") |  | ||||||
| 			print(wn.tema.id) |  | ||||||
| 			print(tematko.id) |  | ||||||
| 			if wn.tema.id == tematko.id: |  | ||||||
| 				returnVal.append((posledni_cislo, 0)) |  | ||||||
| 				print("PRIDANO") |  | ||||||
| 				wr = True |  | ||||||
| 				wd = 1 |  | ||||||
| 
 |  | ||||||
| 		if wn.srolovatelne: |  | ||||||
| 			tagOpen = s.Text(na_web = "Otevírací srolovací tag") |  | ||||||
| 			tagOpenNode = s.TextNode(text = tagOpen) |  | ||||||
| 			tagClose = s.Text(na_web = "Zavírací srolovací tag") |  | ||||||
| 			tagCloseNode = s.TextNode(text = tagClose) |  | ||||||
| 			stack.append((tagCloseNode, wd, True)) |  | ||||||
| 
 |  | ||||||
| 		if wn.first_child != None: |  | ||||||
| 			stack.append((wn.first_child, wd + 1, wr)) |  | ||||||
| 
 |  | ||||||
| 		if isinstance(wn, s.CisloNode): |  | ||||||
| 			posledni_cislo = wn |  | ||||||
| 		print(wn) |  | ||||||
| 
 |  | ||||||
| 		if wr: |  | ||||||
| 			print("ZAJIMAVE") |  | ||||||
| 			if pouze_zajimave: |  | ||||||
| 				if not wn.zajimave: |  | ||||||
| 					continue |  | ||||||
| 			returnVal.append((wn, wd)) |  | ||||||
| 	return returnVal |  | ||||||
| 
 |  | ||||||
| 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) |  | ||||||
| 	seznam = vytahniZLesaSeznam(tematko_object[0], nastaveni.aktualni_rocnik().rocniknode) |  | ||||||
| 	for node, depth in seznam: |  | ||||||
| 		if node.isinstance(node, s.KonferaNode): |  | ||||||
| 			raise Exception("Not implemented yet") |  | ||||||
| 		if node.isinstance(node, s.PohadkaNode): # Mohu ignorovat, má pod sebou |  | ||||||
| 			pass |  | ||||||
| 
 |  | ||||||
| 	return render(request, 'seminar/tematka/toaletak.html', {}) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def TemataRozcestnikView(request): |  | ||||||
| 	print("=============================================") |  | ||||||
| 	nastaveni = s.Nastaveni.objects.first() |  | ||||||
| 	tematka_objects = s.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") |  | ||||||
| 		print(tematko_object.id) |  | ||||||
| 		odkazy = vytahniZLesaSeznam(tematko_object, nastaveni.aktualni_rocnik().rocniknode, pouze_zajimave = True) #Odkazy jsou tuply (node, depth) v listu |  | ||||||
| 		print(odkazy) |  | ||||||
| 		cisla = [] # List tuplů (nazev cisla, list odkazů) |  | ||||||
| 		vcisle = [] |  | ||||||
| 		cislo = None |  | ||||||
| 		for odkaz	in odkazy: |  | ||||||
| 			if odkaz[1] == 0: |  | ||||||
| 				if cislo != None: |  | ||||||
| 					cisla.append((cislo, vcisle)) |  | ||||||
| 				cislo = (odkaz[0].getOdkazStr(), odkaz[0].getOdkaz()) |  | ||||||
| 				vcisle = [] |  | ||||||
| 			else: |  | ||||||
| 				print(odkaz[0].getOdkaz()) |  | ||||||
| 				vcisle.append((odkaz[0].getOdkazStr(), odkaz[0].getOdkaz())) |  | ||||||
| 		if cislo != None: |  | ||||||
| 			cisla.append((cislo, vcisle)) |  | ||||||
| 
 |  | ||||||
| 		print(cisla) |  | ||||||
| 		tematka.append({ |  | ||||||
| 			"kod" : tematko_object.kod, |  | ||||||
| 			"nazev" : tematko_object.nazev, |  | ||||||
| 			"abstrakt" : tematko_object.abstrakt, |  | ||||||
| 			"obrazek": tematko_object.obrazek, |  | ||||||
| 			"cisla" : cisla |  | ||||||
| 		}) |  | ||||||
| 	return render(request, 'seminar/tematka/rozcestnik.html', {"tematka": tematka, "rocnik" : nastaveni.aktualni_rocnik().rocnik}) |  | ||||||
| 
 | 
 | ||||||
|  | #def ZadaniTemataView(request): | ||||||
|  | #	nastaveni = get_object_or_404(Nastaveni) | ||||||
|  | #	temata = verejna_temata(nastaveni.aktualni_rocnik) | ||||||
|  | #	for t in temata: | ||||||
|  | #		if request.user.is_staff: | ||||||
|  | #			t.prispevky = t.prispevek_set.filter(problem=t) | ||||||
|  | #		else: | ||||||
|  | #			t.prispevky = t.prispevek_set.filter(problem=t, zverejnit=True) | ||||||
|  | #	return render(request, 'seminar/zadani/Temata.html', | ||||||
|  | #		{ | ||||||
|  | #			'temata': temata, | ||||||
|  | #		} | ||||||
|  | #	) | ||||||
|  | # | ||||||
|  | # | ||||||
|  | #   | ||||||
|  | #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) | ||||||
|  | #	seznam = vytahniZLesaSeznam(tematko_object[0], nastaveni.aktualni_rocnik().rocniknode) | ||||||
|  | #	for node, depth in seznam: | ||||||
|  | #		if node.isinstance(node, s.KonferaNode): | ||||||
|  | #			raise Exception("Not implemented yet") | ||||||
|  | #		if node.isinstance(node, s.PohadkaNode): # Mohu ignorovat, má pod sebou | ||||||
|  | #			pass | ||||||
|  | # | ||||||
|  | #	return render(request, 'seminar/tematka/toaletak.html', {}) | ||||||
|  | #	 | ||||||
|  | # | ||||||
|  | #def TemataRozcestnikView(request): | ||||||
|  | #	print("=============================================") | ||||||
|  | #	nastaveni = s.Nastaveni.objects.first() | ||||||
|  | #	tematka_objects = s.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") | ||||||
|  | #		print(tematko_object.id) | ||||||
|  | #		odkazy = vytahniZLesaSeznam(tematko_object, nastaveni.aktualni_rocnik().rocniknode, pouze_zajimave = True) #Odkazy jsou tuply (node, depth) v listu | ||||||
|  | #		print(odkazy) | ||||||
|  | #		cisla = [] # List tuplů (nazev cisla, list odkazů) | ||||||
|  | #		vcisle = [] | ||||||
|  | #		cislo = None | ||||||
|  | #		for odkaz	in odkazy: | ||||||
|  | #			if odkaz[1] == 0: | ||||||
|  | #				if cislo != None: | ||||||
|  | #					cisla.append((cislo, vcisle)) | ||||||
|  | #				cislo = (odkaz[0].getOdkazStr(), odkaz[0].getOdkaz()) | ||||||
|  | #				vcisle = [] | ||||||
|  | #			else: | ||||||
|  | #				print(odkaz[0].getOdkaz()) | ||||||
|  | #				vcisle.append((odkaz[0].getOdkazStr(), odkaz[0].getOdkaz())) | ||||||
|  | #		if cislo != None: | ||||||
|  | #			cisla.append((cislo, vcisle)) | ||||||
|  | #				 | ||||||
|  | #		print(cisla) | ||||||
|  | #		tematka.append({ | ||||||
|  | #			"kod" : tematko_object.kod, | ||||||
|  | #			"nazev" : tematko_object.nazev, | ||||||
|  | #			"abstrakt" : tematko_object.abstrakt,  | ||||||
|  | #			"obrazek": tematko_object.obrazek, | ||||||
|  | #			"cisla" : cisla | ||||||
|  | #		}) | ||||||
|  | #	return render(request, 'seminar/tematka/rozcestnik.html', {"tematka": tematka, "rocnik" : nastaveni.aktualni_rocnik().rocnik}) | ||||||
|  | #     | ||||||
| 
 | 
 | ||||||
| #def ZadaniAktualniVysledkovkaView(request): | #def ZadaniAktualniVysledkovkaView(request): | ||||||
| #	nastaveni = get_object_or_404(Nastaveni) | #	nastaveni = get_object_or_404(Nastaveni) | ||||||
|  | @ -429,27 +403,34 @@ def sloupec_s_poradim(seznam_s_body): | ||||||
| 		aktualni_poradi = aktualni_poradi + velikost_skupiny | 		aktualni_poradi = aktualni_poradi + velikost_skupiny | ||||||
| 	return sloupec_s_poradim | 	return sloupec_s_poradim | ||||||
| 
 | 
 | ||||||
| # spočítá součet bodů získaných daným řešitelem za zadaný problém a všechny jeho podproblémy | ## spočítá součet bodů získaných daným řešitelem za zadaný problém a všechny jeho podproblémy | ||||||
| def __soucet_resitele_problemu(problem, resitel, cislo, soucet): | #def __soucet_resitele_problemu(problem, resitel, cislo, soucet): | ||||||
| 	# sečteme body za daný problém přes všechna řešení daného problému | #	# sečteme body za daný problém přes všechna řešení daného problému | ||||||
| 	# od daného řešitele | #	# od daného řešitele | ||||||
| 	reseni_resitele = problem.hodnoceni_set.filter(reseni__resitele=resitel, | #	reseni_resitele = s.Reseni_Resitele.objects.filter(resitele=resitel) | ||||||
| 				cislo_body=cislo) | #	hodnoceni_resitele = problem.hodnoceni.filter(reseni__in=reseni_resitele, | ||||||
| 	# XXX chyba na řádku výše - řešení může mít více řešitelů, asi chceme contains | #				cislo_body=cislo) | ||||||
| 	# nebo in | #	# XXX chyba na řádku výše - řešení může mít více řešitelů, asi chceme contains | ||||||
| 	for r in reseni_resitele: | #	# nebo in | ||||||
| 		soucet += r.body | #	for r in hodnoceni_resitele: | ||||||
|  | #		soucet += r.body | ||||||
|  | # | ||||||
|  | #	# a přičteme k tomu hodnocení všech podproblémů | ||||||
|  | #	for p in problem.podproblem.all(): | ||||||
|  | #	# i přes jméno by to měla být množina jeho podproblémů | ||||||
|  | #		soucet += __soucet_resitele_problemu(p, resitel, soucet) | ||||||
|  | #	return soucet | ||||||
| 
 | 
 | ||||||
| 	# a přičteme k tomu hodnocení všech podproblémů | ## spočítá součet všech bodů ze všech podproblémů daného problému daného řešitele | ||||||
| 	for p in problem.podproblem.all(): | #def body_resitele_problemu_v_cisle(problem, resitel, cislo): | ||||||
| 	# i přes jméno by to měla být množina jeho podproblémů | #	# probably FIXED: nezohledňuje číslo, do kterého se body počítají | ||||||
| 		soucet += __soucet_resitele_problemu(p, resitel, soucet) | #	return __soucet_resitele_problemu(problem, resitel, cislo, 0) | ||||||
| 	return soucet |  | ||||||
| 
 | 
 | ||||||
| # spočítá součet všech bodů ze všech podproblémů daného problému daného řešitele | # pro daný problém vrátí jeho nejvyšší nadproblém | ||||||
| def body_resitele_problemu_v_cisle(problem, resitel, cislo): | def hlavni_problem(problem): | ||||||
| 	# probably FIXED: nezohledňuje číslo, do kterého se body počítají | 	while not(problem.nadproblem == None): | ||||||
| 	return __soucet_resitele_problemu(problem, resitel, cislo, 0) | 		problem = problem.nadproblem | ||||||
|  | 	return problem | ||||||
| 
 | 
 | ||||||
| # vrátí list všech problémů s body v daném čísle, které již nemají nadproblém | # vrátí list všech problémů s body v daném čísle, které již nemají nadproblém | ||||||
| def hlavni_problemy_cisla(cislo): | def hlavni_problemy_cisla(cislo): | ||||||
|  | @ -464,10 +445,8 @@ def hlavni_problemy_cisla(cislo): | ||||||
| 	# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém) | 	# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém) | ||||||
| 	hlavni_problemy = [] | 	hlavni_problemy = [] | ||||||
| 	for p in problemy: | 	for p in problemy: | ||||||
| 		while not(p.nadproblem == None): | 		hlavni_problemy.append(hlavni_problem(p)) | ||||||
| 			p = p.nadproblem | 		 | ||||||
| 		hlavni_problemy.append(p) |  | ||||||
| 
 |  | ||||||
| 	# zunikátnění | 	# zunikátnění | ||||||
| 	hlavni_problemy_set = set(hlavni_problemy) | 	hlavni_problemy_set = set(hlavni_problemy) | ||||||
| 	hlavni_problemy = list(hlavni_problemy_set) | 	hlavni_problemy = list(hlavni_problemy_set) | ||||||
|  | @ -475,38 +454,83 @@ def hlavni_problemy_cisla(cislo): | ||||||
| 
 | 
 | ||||||
| 	return hlavni_problemy | 	return hlavni_problemy | ||||||
| 
 | 
 | ||||||
| def body_resitele_odjakziva(resitel): | # vrátí slovník řešitel:body obsahující počty bodů zadaných řešitelů za daný ročník | ||||||
| 	body = 0 | # POZOR! Aktuálně počítá jen za posledních 10 let od zadaného ročníku | ||||||
| 	resitelova_hodnoceni = Hodnoceni.objects.select_related('body').all().filter(reseni_resitele=resitel) | def body_resitelu_odjakziva(rocnik, resitele): | ||||||
| 	# TODO: v radku nahore chceme _in nebo _contains | 	body_odjakziva = {} | ||||||
| 	for hodnoceni in resitelova_hodnoceni: | 
 | ||||||
| 		body = body + hodnoceni.body | 	for r in resitele: | ||||||
| 	return body | 		body_odjakziva[str(r.id)] = 0 | ||||||
|  | #	# Body za posledních 10 let je dobrá aproximace pro naše potřeby (výsledkovka | ||||||
|  | #	# s aktivními řešiteli) | ||||||
|  | # | ||||||
|  | #	body_pred_roky = [] | ||||||
|  | #	for i in range(0, 10): | ||||||
|  | #		body_pred_roky.append(body_resitelu_za_rocnik(rocnik-i, resitele)) | ||||||
|  | # | ||||||
|  | #	for r in resitele: | ||||||
|  | #		for i in range(0,10): | ||||||
|  | #			body_odjakziva[str(r.id)] += body_pred_roky[i][str(r.id)] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Nasledující řešení je sice správné, ale moc pomalé:	 | ||||||
|  | 	for res in Reseni.objects.prefetch_related('resitele', 'hodnoceni_set').all(): | ||||||
|  | 		for r in res.resitele.all(): | ||||||
|  | 			# daný řešitel nemusí být v naší podmnožině | ||||||
|  | 			if r not in resitele: continue | ||||||
|  | 			 | ||||||
|  | 			for hodn in res.hodnoceni_set.all(): | ||||||
|  | 				pricti_body(body_odjakziva, r, hodn.body) | ||||||
|  | 	return body_odjakziva | ||||||
|  | 
 | ||||||
|  | # vrátí slovník řešitel:body obsahující počty bodů zadaných řešitelů za daný ročník | ||||||
|  | def body_resitelu_za_rocnik(rocnik, aktivni_resitele): | ||||||
|  | 	body_za_rocnik = {} | ||||||
|  | 	# inicializujeme na 0 pro všechny aktivní řešitele | ||||||
|  | 	for ar in aktivni_resitele: | ||||||
|  | 		body_za_rocnik[str(ar.id)] = 0 | ||||||
|  | 	 | ||||||
|  | 	# spočítáme body řešitelům přes všechna řešení s hodnocením v daném ročníku | ||||||
|  | 	reseni = Reseni.objects.prefetch_related('resitele', 'hodnoceni_set').filter(hodnoceni__cislo_body__rocnik=rocnik) | ||||||
|  | 	for res in reseni: | ||||||
|  | 		for resitel in res.resitele.all(): | ||||||
|  | 			for hodn in res.hodnoceni_set.all(): | ||||||
|  | 				pricti_body(body_za_rocnik, resitel, hodn.body) | ||||||
|  | 	return body_za_rocnik | ||||||
|  | 
 | ||||||
|  | #def body_resitele_odjakziva(resitel): | ||||||
|  | #	body = 0 | ||||||
|  | #	resitelova_hodnoceni = Hodnoceni.objects.select_related('body').all().filter(reseni_resitele=resitel) | ||||||
|  | #	# TODO: v radku nahore chceme _in nebo _contains | ||||||
|  | #	for hodnoceni in resitelova_hodnoceni: | ||||||
|  | #		body = body + hodnoceni.body | ||||||
|  | #	return body | ||||||
| 
 | 
 | ||||||
| # spočítá součet všech bodů řešitele za dané číslo | # spočítá součet všech bodů řešitele za dané číslo | ||||||
| def body_resitele_v_cisle(resitel, cislo): | #def body_resitele_v_cisle(resitel, cislo): | ||||||
| 	hlavni_problemy = hlavni_problemy_cisla(cislo) | #	hlavni_problemy = hlavni_problemy_cisla(cislo) | ||||||
| 	body_resitele = 0 | #	body_resitele = 0 | ||||||
| 	for h in hlavni_problemy: | #	for h in hlavni_problemy: | ||||||
| 		body_resitele = body_resitele + body_resitele_problemu_v_cisle(h, resitel, cislo) | #		body_resitele = body_resitele + body_resitele_problemu_v_cisle(h, resitel, cislo) | ||||||
| 	# TODO: je rozdíl mezi odevzdanou úlohou za 0 a tím, když řešitel nic neodevzdal | #	# TODO: je rozdíl mezi odevzdanou úlohou za 0 a tím, když řešitel nic neodevzdal | ||||||
| 	# řešit přes kontrolu velikosti množiny řešení daného problému do daného čísla? | #	# řešit přes kontrolu velikosti množiny řešení daného problému do daného čísla? | ||||||
| 	# Tady to ale nevadí, tady se počítá součet za číslo. | #	# Tady to ale nevadí, tady se počítá součet za číslo. | ||||||
| 	return body_resitele | #	return body_resitele | ||||||
| 
 | 
 | ||||||
| # spočítá součet všech bodů řešitele za daný rok (nebo jen do daného čísla včetně) | # spočítá součet všech bodů řešitele za daný rok (nebo jen do daného čísla včetně) | ||||||
| def body_resitele_v_rocniku(resitel, rocnik, do_cisla=None): | #def body_resitele_v_rocniku(resitel, rocnik, do_cisla=None): | ||||||
| 	# pokud do_cisla=None, tak do posledního čísla v ročníku | #	# pokud do_cisla=None, tak do posledního čísla v ročníku | ||||||
| 	# do_cisla je objekt Cislo | #	# do_cisla je objekt Cislo | ||||||
| 	cisla = rocnik.cisla.all() # funkce vrátí pole objektů | #	cisla = rocnik.cisla.all() # funkce vrátí pole objektů | ||||||
| 	# Cislo už lexikograficky setřízené, viz models | #	# Cislo už lexikograficky setřízené, viz models | ||||||
| 	body = 0 | #	body = 0 | ||||||
| 	for cislo in cisla: | #	for cislo in cisla: | ||||||
| 		if cislo.poradi == do_cisla.poradi: break | #		if cislo.poradi == do_cisla.poradi: break | ||||||
| 		# druhá část zaručuje, že máme výsledky do daného čísla včetně | #		# druhá část zaručuje, že máme výsledky do daného čísla včetně | ||||||
| 		body = body + body_resitele_v_cisle(resitel, cislo) | #		body = body + body_resitele_v_cisle(resitel, cislo) | ||||||
| 	return body | #	return body | ||||||
| 
 | 
 | ||||||
|  | # TODO: předělat na nový model | ||||||
| #def vysledkovka_rocniku(rocnik, jen_verejne=True): | #def vysledkovka_rocniku(rocnik, jen_verejne=True): | ||||||
| #	"""Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve | #	"""Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve | ||||||
| #	formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html" | #	formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html" | ||||||
|  | @ -588,7 +612,8 @@ class RocnikView(generic.DetailView): | ||||||
| 
 | 
 | ||||||
| 		#context['vysledkovka'] = vysledkovka_rocniku(context["rocnik"]) | 		#context['vysledkovka'] = vysledkovka_rocniku(context["rocnik"]) | ||||||
| 		#context['vysledkovka_s_neverejnymi'] = vysledkovka_rocniku(context["rocnik"], jen_verejne=False) | 		#context['vysledkovka_s_neverejnymi'] = vysledkovka_rocniku(context["rocnik"], jen_verejne=False) | ||||||
| 		context['temata_v_rocniku'] = verejna_temata(context["rocnik"]) | 		#context['temata_v_rocniku'] = verejna_temata(context["rocnik"]) | ||||||
|  | 		# FIXME: opravit vylistování témat v ročníku | ||||||
| 
 | 
 | ||||||
| 		return context | 		return context | ||||||
| 
 | 
 | ||||||
|  | @ -612,19 +637,31 @@ class ProblemView(generic.DetailView): | ||||||
| 		return context | 		return context | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class VysledkyResitele(object): | class RadekVysledkovky(object): | ||||||
| 	"""Pro daného řešitele ukládá počet bodů za jednotlivé úlohy a celkový | 	"""Obsahuje věci, které se hodí vědět při konstruování výsledkovky. | ||||||
| 	počet bodů za konkrétní ročník do daného čísla a za dané číslo.""" | 	Umožňuje snazší práci v templatu (lepší, než seznam).""" | ||||||
| 
 | 
 | ||||||
| 	def __init__(self, resitel, cislo, rocnik): | 	def __init__(self, poradi, resitel, body_problemy_sezn,  | ||||||
|  | 				body_cislo, body_rocnik, body_odjakziva): | ||||||
| 		self.resitel = resitel | 		self.resitel = resitel | ||||||
| 		self.cislo = cislo | 		self.body_cislo = body_cislo | ||||||
| 		self.body_cislo = body_resitele_v_cisle(resitel, cislo) | 		self.body_rocnik = body_rocnik | ||||||
| 		self.body = [] | 		self.body_celkem_odjakziva = body_odjakziva | ||||||
| 		self.rocnik = rocnik | 		self.poradi = poradi | ||||||
| 		self.body_rocnik = body_resitele_v_rocniku(resitel, rocnik, cislo) | 		self.body_problemy_sezn = body_problemy_sezn | ||||||
| 		self.body_celkem_odjakziva = resitel.vsechny_body() | 		self.titul = resitel.get_titul(body_odjakziva) | ||||||
| 		self.poradi = 0 | 	 | ||||||
|  | 
 | ||||||
|  | # přiřazuje danému řešiteli body do slovníku | ||||||
|  | def pricti_body(slovnik, resitel, body): | ||||||
|  | 	# testujeme na None (""), pokud je to první řešení  | ||||||
|  | 	# daného řešitele, předěláme na 0 | ||||||
|  | 	# (v dalším kroku přičteme reálný počet bodů), | ||||||
|  | 	# rozlišujeme tím mezi 0 a neodevzdaným řešením | ||||||
|  | 	if slovnik[str(resitel.id)] == "": | ||||||
|  | 		slovnik[str(resitel.id)] = 0 | ||||||
|  | 	 | ||||||
|  | 	slovnik[str(resitel.id)] += body | ||||||
| 
 | 
 | ||||||
| class CisloView(generic.DetailView): | class CisloView(generic.DetailView): | ||||||
| 	model = Cislo | 	model = Cislo | ||||||
|  | @ -648,48 +685,101 @@ class CisloView(generic.DetailView): | ||||||
| 	def get_context_data(self, **kwargs): | 	def get_context_data(self, **kwargs): | ||||||
| 		context = super(CisloView, self).get_context_data(**kwargs) | 		context = super(CisloView, self).get_context_data(**kwargs) | ||||||
| 
 | 
 | ||||||
| 		## TODO upravit dle nového modelu |  | ||||||
| 		cislo = context['cislo'] | 		cislo = context['cislo'] | ||||||
| 		hlavni_problemy = hlavni_problemy_cisla(cislo) | 		hlavni_problemy = hlavni_problemy_cisla(cislo) | ||||||
|  | 		# TODO setřídit hlavní problémy čísla podle id, ať jsou ve stejném pořadí pokaždé | ||||||
|  | 		# pro každý hlavní problém zavedeme slovník s body za daný hlavní problém  | ||||||
|  | 		# pro jednotlivé řešitele (slovník slovníků hlavních problémů) | ||||||
|  | 		hlavni_problemy_slovnik = {} | ||||||
|  | 		for hp in hlavni_problemy: | ||||||
|  | 			hlavni_problemy_slovnik[str(hp.id)] = {} | ||||||
| 
 | 
 | ||||||
| 		## TODO dostat pro tyto problémy součet v daném čísle pro daného řešitele | 		## TODO dostat pro tyto problémy součet v daném čísle pro daného řešitele | ||||||
| 		## TODO možná chytřeji vybírat aktivní řešitele | 		## TODO možná chytřeji vybírat aktivní řešitele | ||||||
| 		## chceme letos něco poslal | 		# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají  | ||||||
| 		aktivni_resitele = Resitel.objects.filter( | 		# u alespoň jedné hodnoty něco jiného než NULL | ||||||
| 				rok_maturity__gte=cislo.rocnik.druhy_rok()) | 		aktivni_resitele = list(Resitel.objects.filter( | ||||||
|  | 				rok_maturity__gte=cislo.rocnik.druhy_rok())) | ||||||
| 				# TODO: zkusit hodnoceni__rocnik... | 				# TODO: zkusit hodnoceni__rocnik... | ||||||
| 				#.filter(hodnoceni_set__rocnik__eq=cislo_rocnik) | 				#.filter(hodnoceni_set__rocnik__eq=cislo_rocnik) | ||||||
| 		radky_vysledkovky = [] | 		# zakládání prázdných záznamů pro řešitele | ||||||
|  | 		cislobody = {} | ||||||
| 		for ar in aktivni_resitele: | 		for ar in aktivni_resitele: | ||||||
| 			# získáme výsledky řešitele - součty přes číslo a ročník | 			# řešitele převedeme na řetězec pomocí unikátního id | ||||||
| 			vr = VysledkyResitele(ar, cislo, cislo.rocnik) | 			cislobody[str(ar.id)] = "" | ||||||
| 			for hp in hlavni_problemy: | 			for hp in hlavni_problemy: | ||||||
| 				vr.body.append( | 				slovnik = hlavni_problemy_slovnik[str(hp.id)] | ||||||
| 				body_resitele_problemu_v_cisle(hp, ar, cislo)) | 				slovnik[str(ar.id)] = "" | ||||||
| 			radky_vysledkovky.append(vr) | 		 | ||||||
|  | 		# vezmeme všechna řešení s body do daného čísla | ||||||
|  | 		reseni_do_cisla = Reseni.objects.prefetch_related('problem', 'resitele', 'hodnoceni_set').filter(hodnoceni__cislo_body=cislo) | ||||||
|  | 		 | ||||||
|  | 		# projdeme všechna řešení do čísla a přičteme body každému řešiteli do celkových | ||||||
|  | 		# bodů i do bodů za problém | ||||||
|  | 		for reseni in reseni_do_cisla: | ||||||
|  | 			 | ||||||
|  | 			# řešení může řešit více problémů | ||||||
|  | 			for prob in list(reseni.problem.all()): | ||||||
|  | 				nadproblem = hlavni_problem(prob) | ||||||
|  | 				nadproblem_slovnik = hlavni_problemy_slovnik[str(nadproblem.id)] | ||||||
|  | 				 | ||||||
|  | 				# a více hodnocení | ||||||
|  | 				for hodn in list(reseni.hodnoceni_set.all()): | ||||||
|  | 					body = hodn.body	 | ||||||
|  | 					 | ||||||
|  | 					# a více řešitelů | ||||||
|  | 					for resitel in list(reseni.resitele.all()): | ||||||
|  | 						pricti_body(cislobody, resitel, body) | ||||||
|  | 						pricti_body(nadproblem_slovnik, resitel, body) | ||||||
| 
 | 
 | ||||||
| 		# setřídíme řádky výsledkovky/objekty VysledkyResitele podle bodů | 		# zeptáme se na dvojice (řešitel, body) za ročník a setřídíme sestupně | ||||||
| 		radky_vysledkovky.sort(key=lambda vr: vr.body_rocnik, reverse=True) | 		resitel_rocnikbody_slov = body_resitelu_za_rocnik(cislo.rocnik, aktivni_resitele) | ||||||
|  | 		resitel_rocnikbody_sezn = sorted(resitel_rocnikbody_slov.items(), | ||||||
|  | 						key = lambda x: x[1], reverse = True) | ||||||
|  | 	 | ||||||
|  | 		# získáme body odjakživa | ||||||
|  | 		resitel_odjakzivabody_slov = body_resitelu_odjakziva(cislo.rocnik.druhy_rok(),  | ||||||
|  | 									aktivni_resitele) | ||||||
|  | 	 | ||||||
|  | 		# řešitelé setřídění podle bodů za číslo sestupně | ||||||
|  | 		setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn] | ||||||
|  | 		setrizeni_resitele = [Resitel.objects.get(id=i) for i in setrizeni_resitele_id] | ||||||
|  | 				 | ||||||
|  | 		# vytvoříme jednotlivé sloupce výsledkovky | ||||||
|  | 		radky_vysledkovky = [] | ||||||
|  | 		odjakziva_body = [] | ||||||
|  | 		rocnik_body = [] | ||||||
|  | 		cislo_body = [] | ||||||
|  | 		hlavni_problemy_body = [] | ||||||
|  | 		for ar_id in setrizeni_resitele_id: | ||||||
|  | 			# vytáhneme ze slovníků body pro daného řešitele | ||||||
|  | 			odjakziva_body.append(resitel_odjakzivabody_slov[ar_id]) | ||||||
|  | 			rocnik_body.append(resitel_rocnikbody_slov[ar_id]) | ||||||
|  | 			cislo_body.append(cislobody[ar_id]) | ||||||
|  | 			problemy = [] | ||||||
|  | 			for hp in hlavni_problemy: | ||||||
|  | 				problemy.append(hlavni_problemy_slovnik[str(hp.id)][ar_id]) | ||||||
|  | 			hlavni_problemy_body.append(problemy) | ||||||
|  | 			print("{}: body za problémy - {}, číslobody - {}, ročníkbody - {}, odjakživabody - ".format(ar_id, problemy, cislobody[ar_id], resitel_rocnikbody_slov[ar_id])) | ||||||
|  | 		# pořadí určíme pomocí funkce, které dáme celkové body za ročník vzestupně | ||||||
|  | 		poradi = sloupec_s_poradim(rocnik_body) | ||||||
| 
 | 
 | ||||||
| 		# generujeme sloupec s pořadím pomocí stejně zvané funkce | 		radky_vysledkovky = [] | ||||||
| 		pocty_bodu = [rv.body_rocnik for rv in radky_vysledkovky] | 		for i in range(0, len(setrizeni_resitele_id)): | ||||||
| 		sloupec_poradi = sloupec_s_poradim(pocty_bodu) | 			radek = RadekVysledkovky(poradi[i], setrizeni_resitele[i], | ||||||
| 
 | 				hlavni_problemy_body[i], cislo_body[i], rocnik_body[i], | ||||||
| 		# každému řádku výsledkovky přidáme jeho pořadí | 				odjakziva_body[i]) | ||||||
| 		i = 0 | 			radky_vysledkovky.append(radek) | ||||||
| 		for rv in radky_vysledkovky: | 			print("Přikládám {}-tý řádek.".format(i)) | ||||||
| 			rv.poradi = sloupec_poradi[i] |  | ||||||
| 			i = i + 1 |  | ||||||
| 
 | 
 | ||||||
|  | 		print("Následuje předávání do kontextu.") | ||||||
| 		# vytahané informace předáváme do kontextu | 		# vytahané informace předáváme do kontextu | ||||||
| 		context['cislo'] = cislo | 		context['cislo'] = cislo | ||||||
| 		context['radky_vysledkovky'] = radky_vysledkovky | 		context['radky_vysledkovky'] = radky_vysledkovky | ||||||
| 		context['problemy'] = hlavni_problemy | 		context['problemy'] = hlavni_problemy | ||||||
| #		context['v_cisle_zadane'] = TODO | #		context['v_cisle_zadane'] = TODO | ||||||
| #		context['resene_problemy'] = resene_problemy | #		context['resene_problemy'] = resene_problemy | ||||||
| 		#XXX testovat | 		print("Předávám kontext.") | ||||||
| 		#XXX opravit to, že se nezobrazují body za jednotlivé úlohy |  | ||||||
| 
 |  | ||||||
| 		return context | 		return context | ||||||
| 
 | 
 | ||||||
| #		problemy = sorted(set(r.problem for r in reseni), key=lambda x:(poradi_typu[x.typ], x.kod_v_rocniku())) | #		problemy = sorted(set(r.problem for r in reseni), key=lambda x:(poradi_typu[x.typ], x.kod_v_rocniku())) | ||||||
|  | @ -917,7 +1007,7 @@ def soustredeniUcastniciExportView(request,soustredeni): | ||||||
| class ClankyResitelView(generic.ListView): | class ClankyResitelView(generic.ListView): | ||||||
| 	model = Problem | 	model = Problem | ||||||
| 	template_name = 'seminar/clanky/resitelske_clanky.html' | 	template_name = 'seminar/clanky/resitelske_clanky.html' | ||||||
| 	queryset = Clanek.objects.filter(stav=Problem.STAV_ZADANY).select_related('cislo_zadani__rocnik').order_by('-cislo_zadani__rocnik__rocnik', 'kod') | 	queryset = Clanek.objects.filter(stav=Problem.STAV_ZADANY, resitelsky=True).select_related('cislo__rocnik').order_by('-cislo__rocnik__rocnik', 'kod') | ||||||
| 
 | 
 | ||||||
| # FIXME: pokud chceme orgoclanky, tak nejak zavest do modelu a podle toho odkomentovat a upravit | # FIXME: pokud chceme orgoclanky, tak nejak zavest do modelu a podle toho odkomentovat a upravit | ||||||
| #class ClankyOrganizatorView(generic.ListView)<F12>: | #class ClankyOrganizatorView(generic.ListView)<F12>: | ||||||
|  | @ -1160,6 +1250,42 @@ class AddSolutionView(LoginRequiredMixin, FormView): | ||||||
| 	form_class = f.VlozReseniForm | 	form_class = f.VlozReseniForm | ||||||
| 	success_url = '/' | 	success_url = '/' | ||||||
| 
 | 
 | ||||||
|  | class SubmitSolutionView(LoginRequiredMixin, CreateView): | ||||||
|  | 	model = s.Reseni | ||||||
|  | 	template_name = 'seminar/nahraj_reseni.html' | ||||||
|  | 	form_class = f.NahrajReseniForm | ||||||
|  | 	success_url = '/' | ||||||
|  | 
 | ||||||
|  | 	def get_context_data(self,**kwargs): | ||||||
|  | 		data = super().get_context_data(**kwargs) | ||||||
|  | 		if self.request.POST: | ||||||
|  | 			data['prilohy'] = f.ReseniSPrilohamiFormSet(self.request.POST,self.request.FILES)  | ||||||
|  | 		else: | ||||||
|  | 			data['prilohy'] = f.ReseniSPrilohamiFormSet()  | ||||||
|  | 		return data | ||||||
|  | 
 | ||||||
|  | 	# FIXME prepsat tak, aby form_valid se volalo jen tehdy, kdyz je form i formset validni | ||||||
|  | 	# Inspirace: https://stackoverflow.com/questions/41599809/using-a-django-filefield-in-an-inline-formset | ||||||
|  | 	def form_valid(self,form): | ||||||
|  | 		context = self.get_context_data() | ||||||
|  | 		prilohy = context['prilohy'] | ||||||
|  | 		if not prilohy.is_valid(): | ||||||
|  | 			return super().form_invalid(form) | ||||||
|  | 		with transaction.atomic(): | ||||||
|  | 			self.object = form.save() | ||||||
|  | 			self.object.resitele.add(Resitel.objects.get(osoba__user = self.request.user)) | ||||||
|  | 			self.object.cas_doruceni = timezone.now()  | ||||||
|  | 			self.object.forma = s.Reseni.FORMA_UPLOAD | ||||||
|  | 			self.object.save() | ||||||
|  | 
 | ||||||
|  | 			prilohy.instance = self.object | ||||||
|  | 			prilohy.save() | ||||||
|  | 
 | ||||||
|  | 		return HttpResponseRedirect(self.get_success_url()) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| def resetPasswordView(request): | def resetPasswordView(request): | ||||||
| 	pass | 	pass | ||||||
| 
 | 
 | ||||||
|  | @ -1321,58 +1447,9 @@ def prihlaskaView(request): | ||||||
| 
 | 
 | ||||||
| 	return render(request, 'seminar/prihlaska.html', {'form': form}) | 	return render(request, 'seminar/prihlaska.html', {'form': form}) | ||||||
| 
 | 
 | ||||||
| class SkolaAutocomplete(autocomplete.Select2QuerySetView): |  | ||||||
| 	def get_queryset(self): |  | ||||||
|         	# Don't forget to filter out results depending on the visitor ! |  | ||||||
| 		qs = Skola.objects.all() |  | ||||||
| 		if self.q: |  | ||||||
| 			qs = qs.filter( |  | ||||||
| 				Q(nazev__istartswith=self.q)| |  | ||||||
| 				Q(kratky_nazev__istartswith=self.q)| |  | ||||||
| 				Q(ulice__istartswith=self.q)| |  | ||||||
| 				Q(mesto__istartswith=self.q)) |  | ||||||
| 
 |  | ||||||
| 		return qs |  | ||||||
| 
 |  | ||||||
| class LoginRequiredAjaxMixin(object): |  | ||||||
| 	def dispatch(self, request, *args, **kwargs): |  | ||||||
| 		#if request.is_ajax() and not request.user.is_authenticated: # Pokud to otevřu jako stránku, tak se omezení neuplatní, takže to asi nechceme |  | ||||||
| 		if not request.user.is_authenticated: |  | ||||||
| 			return JsonResponse(data={'results': [], 'pagination': {}}, status=401) |  | ||||||
| 		return super(LoginRequiredAjaxMixin, self).dispatch(request, *args, **kwargs) |  | ||||||
| 
 |  | ||||||
| class ResitelAutocomplete(LoginRequiredAjaxMixin,autocomplete.Select2QuerySetView): |  | ||||||
| 	def get_queryset(self): |  | ||||||
| 		qs = Resitel.objects.all() |  | ||||||
| 		if self.q: |  | ||||||
| 			qs = qs.filter( |  | ||||||
| 				Q(osoba__jmeno__startswith=self.q)| |  | ||||||
| 				Q(osoba__prijmeni__startswith=self.q)| |  | ||||||
| 				Q(osoba__prezdivka__startswith=self.q) |  | ||||||
| 				) |  | ||||||
| 		return qs |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # Ceka na autocomplete v3 | 
 | ||||||
| # class OrganizatorAutocomplete(autocomplete.Select2QuerySetView): |  | ||||||
| # 	def get_queryset(self): |  | ||||||
| # 		if not self.request.user.is_authenticated(): |  | ||||||
| # 			return Organizator.objects.none() |  | ||||||
| # |  | ||||||
| # 		qs = aktivniOrganizatori() |  | ||||||
| # |  | ||||||
| # 		if self.q: |  | ||||||
| # 			if self.q[0] == "!": |  | ||||||
| # 				qs = Organizator.objects.all() |  | ||||||
| # 				query = self.q[1:] |  | ||||||
| # 			else: |  | ||||||
| # 				query = self.q |  | ||||||
| # 			qs = qs.filter( |  | ||||||
| # 				Q(prezdivka__isstartswith=query)| |  | ||||||
| # 				Q(user__first_name__isstartswith=query)| |  | ||||||
| # 				Q(user__last_name__isstartswith=query)) |  | ||||||
| # |  | ||||||
| # 		return qs |  | ||||||
| 
 | 
 | ||||||
| # FIXME: Tohle asi vlastně vůbec nepatří do aplikace 'seminar' | # FIXME: Tohle asi vlastně vůbec nepatří do aplikace 'seminar' | ||||||
| class LoginView(auth_views.LoginView): | class LoginView(auth_views.LoginView): | ||||||
		Loading…
	
		Reference in a new issue
	
	 Kateřina Čížková
						Kateřina Čížková