Merge pull request 'Odstrel Modelu Odevzdavatko' (!64) from odstrel_modelu_odevzdavatko into master
Reviewed-on: #64
This commit is contained in:
		
						commit
						b8f377b15d
					
				
					 20 changed files with 550 additions and 251 deletions
				
			
		
							
								
								
									
										99
									
								
								odevzdavatko/migrations/0001_create.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								odevzdavatko/migrations/0001_create.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,99 @@ | ||||||
|  | # Generated by Django 4.2.13 on 2024-10-22 22:51 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations, models | ||||||
|  | import django.utils.timezone | ||||||
|  | import odevzdavatko.models | ||||||
|  | 
 | ||||||
|  | def nastav_nove_contenttypes(apps, schema_editor): | ||||||
|  |     ContentType = apps.get_model('contenttypes', 'ContentType') | ||||||
|  |     for m in ('reseni', 'hodnoceni', 'reseni_resitele', 'prilohareseni'): | ||||||
|  |         ContentType.objects.filter(app_label='seminar', model=m).update(app_label='odevzdavatko') | ||||||
|  | 
 | ||||||
|  | def nastav_stare_contenttypes(apps, schema_editor): | ||||||
|  |     ContentType = apps.get_model('contenttypes', 'ContentType') | ||||||
|  |     for m in ('reseni', 'hodnoceni', 'reseni_resitele', 'prilohareseni'): | ||||||
|  |         ContentType.objects.filter(app_label='odevzdavatko', model=m).update(app_label='seminar') | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     initial = True | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('seminar', '0132_unmanage_odevzdavatko'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name='Hodnoceni', | ||||||
|  |             fields=[ | ||||||
|  |                 ('id', models.AutoField(primary_key=True, serialize=False)), | ||||||
|  |                 ('body', models.DecimalField(blank=True, decimal_places=1, max_digits=8, null=True, verbose_name='body')), | ||||||
|  |                 ('feedback', models.TextField(blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)', verbose_name='zpětná vazba')), | ||||||
|  |                 ('cislo_body', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='seminar.cislo', verbose_name='číslo pro body')), | ||||||
|  |                 ('deadline_body', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='seminar.deadline', verbose_name='deadline pro body')), | ||||||
|  |                 ('problem', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='seminar.problem', verbose_name='problém')), | ||||||
|  |                 ('reseni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='odevzdavatko.reseni', verbose_name='řešení')), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 'verbose_name': 'Hodnocení', | ||||||
|  |                 'verbose_name_plural': 'Hodnocení', | ||||||
|  |                 'db_table': 'seminar_hodnoceni', | ||||||
|  |                 'managed': False, | ||||||
|  |             }, | ||||||
|  |         ), | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name='PrilohaReseni', | ||||||
|  |             fields=[ | ||||||
|  |                 ('id', models.AutoField(primary_key=True, serialize=False)), | ||||||
|  |                 ('vytvoreno', models.DateTimeField(blank=True, default=django.utils.timezone.now, editable=False, verbose_name='vytvořeno')), | ||||||
|  |                 ('soubor', models.FileField(upload_to=odevzdavatko.models.generate_filename, verbose_name='soubor')), | ||||||
|  |                 ('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu', verbose_name='neveřejná poznámka')), | ||||||
|  |                 ('res_poznamka', models.TextField(blank=True, help_text='Poznámka k příloze řešení, např. co daný soubor obsahuje', verbose_name='poznámka řešitele')), | ||||||
|  |                 ('reseni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='prilohy', to='odevzdavatko.reseni', verbose_name='řešení')), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 'verbose_name': 'Příloha řešení', | ||||||
|  |                 'verbose_name_plural': 'Přílohy řešení', | ||||||
|  |                 'db_table': 'seminar_priloha_reseni', | ||||||
|  |                 'ordering': ['reseni', 'vytvoreno'], | ||||||
|  |                 'managed': False, | ||||||
|  |             }, | ||||||
|  |         ), | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name='Reseni', | ||||||
|  |             fields=[ | ||||||
|  |                 ('id', models.AutoField(primary_key=True, serialize=False)), | ||||||
|  |                 ('cas_doruceni', models.DateTimeField(blank=True, default=django.utils.timezone.now, verbose_name='čas_doručení')), | ||||||
|  |                 ('forma', models.CharField(choices=[('papir', 'Papírové řešení'), ('email', 'Emailem'), ('upload', 'Upload přes web')], default='email', max_length=16, verbose_name='forma řešení')), | ||||||
|  |                 ('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k řešení (plain text)', verbose_name='neveřejná poznámka')), | ||||||
|  |                 ('zverejneno', models.BooleanField(default=False, help_text='Udává, zda je řešení zveřejněno', verbose_name='řešení zveřejněno')), | ||||||
|  |                 ('problem', models.ManyToManyField(help_text='Problém', through='odevzdavatko.Hodnoceni', to='seminar.problem', verbose_name='problém')), | ||||||
|  |                 ('resitele', models.ManyToManyField(help_text='Seznam autorů řešení', through='odevzdavatko.Reseni_Resitele', to='personalni.resitel', verbose_name='autoři řešení')), | ||||||
|  |                 ('text_cely', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='reseni_cely_set', to='seminar.reseninode', verbose_name='Plná verze textu řešení')), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 'verbose_name': 'Řešení', | ||||||
|  |                 'verbose_name_plural': 'Řešení', | ||||||
|  |                 'db_table': 'seminar_reseni', | ||||||
|  |                 'ordering': ['-cas_doruceni'], | ||||||
|  |                 'managed': False, | ||||||
|  |             }, | ||||||
|  |         ), | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name='Reseni_Resitele', | ||||||
|  |             fields=[ | ||||||
|  |                 ('id', models.AutoField(primary_key=True, serialize=False)), | ||||||
|  |                 ('reseni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='odevzdavatko.reseni', verbose_name='řešení')), | ||||||
|  |                 ('resitele', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='personalni.resitel', verbose_name='řešitel')), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 'verbose_name': 'Řešení řešitelů', | ||||||
|  |                 'verbose_name_plural': 'Řešení řešitelů', | ||||||
|  |                 'db_table': 'seminar_reseni_resitele', | ||||||
|  |                 'ordering': ['reseni', 'resitele'], | ||||||
|  |                 'managed': False, | ||||||
|  |             }, | ||||||
|  |         ), | ||||||
|  |         migrations.RunPython(nastav_nove_contenttypes, nastav_stare_contenttypes), | ||||||
|  | 
 | ||||||
|  |     ] | ||||||
							
								
								
									
										30
									
								
								odevzdavatko/migrations/0002_manage.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								odevzdavatko/migrations/0002_manage.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,30 @@ | ||||||
|  | # Generated by Django 4.2.13 on 2024-10-23 21:07 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('odevzdavatko', '0001_create'), | ||||||
|  |         ('seminar', '0134_delete_odevzdavatko'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AlterModelOptions( | ||||||
|  |             name='hodnoceni', | ||||||
|  |             options={'verbose_name': 'Hodnocení', 'verbose_name_plural': 'Hodnocení'}, | ||||||
|  |         ), | ||||||
|  |         migrations.AlterModelOptions( | ||||||
|  |             name='prilohareseni', | ||||||
|  |             options={'ordering': ['reseni', 'vytvoreno'], 'verbose_name': 'Příloha řešení', 'verbose_name_plural': 'Přílohy řešení'}, | ||||||
|  |         ), | ||||||
|  |         migrations.AlterModelOptions( | ||||||
|  |             name='reseni', | ||||||
|  |             options={'ordering': ['-cas_doruceni'], 'verbose_name': 'Řešení', 'verbose_name_plural': 'Řešení'}, | ||||||
|  |         ), | ||||||
|  |         migrations.AlterModelOptions( | ||||||
|  |             name='reseni_resitele', | ||||||
|  |             options={'ordering': ['reseni', 'resitele'], 'verbose_name': 'Řešení řešitelů', 'verbose_name_plural': 'Řešení řešitelů'}, | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
							
								
								
									
										13
									
								
								odevzdavatko/migrations/0003_odstrel_odevzdavatka_post.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								odevzdavatko/migrations/0003_odstrel_odevzdavatka_post.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | ||||||
|  | # Generated by Django 4.2.13 on 2024-10-23 21:10 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('odevzdavatko', '0002_manage'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |     ] | ||||||
							
								
								
									
										239
									
								
								odevzdavatko/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								odevzdavatko/models.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,239 @@ | ||||||
|  | import os | ||||||
|  | 
 | ||||||
|  | import reversion | ||||||
|  | 
 | ||||||
|  | from django.contrib.sites.shortcuts import get_current_site | ||||||
|  | from django.db import models | ||||||
|  | from django.db.models import Sum | ||||||
|  | from django.urls import reverse_lazy | ||||||
|  | from django.utils import timezone | ||||||
|  | from django.conf import settings | ||||||
|  | 
 | ||||||
|  | import seminar.models as am # tvorba | ||||||
|  | from seminar.models import base as bm | ||||||
|  | 
 | ||||||
|  | from odevzdavatko.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet | ||||||
|  | from personalni.models import Resitel | ||||||
|  | 
 | ||||||
|  | @reversion.register(ignore_duplicates=True) | ||||||
|  | class Reseni(bm.SeminarModelBase): | ||||||
|  | 
 | ||||||
|  | 	class Meta: | ||||||
|  | 		db_table = 'seminar_reseni' | ||||||
|  | 		verbose_name = 'Řešení' | ||||||
|  | 		verbose_name_plural = 'Řešení' | ||||||
|  | 		#ordering = ['-problem', 'resitele']	# FIXME: Takhle to chceme, ale nefunguje to. | ||||||
|  | 		ordering = ['-cas_doruceni'] | ||||||
|  | 
 | ||||||
|  | 	# Interní ID | ||||||
|  | 	id = models.AutoField(primary_key = True) | ||||||
|  | 
 | ||||||
|  | 	# Ke každé dvojici řešní a problém existuje nanejvýš jedno hodnocení, doplnění vazby. | ||||||
|  | 	problem = models.ManyToManyField(am.Problem, verbose_name='problém', help_text='Problém', | ||||||
|  | 									 through='Hodnoceni') | ||||||
|  | 
 | ||||||
|  | 	resitele = models.ManyToManyField(Resitel, verbose_name='autoři řešení', | ||||||
|  | 									  help_text='Seznam autorů řešení', through='Reseni_Resitele') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	cas_doruceni = models.DateTimeField('čas_doručení', default=timezone.now, blank=True) | ||||||
|  | 
 | ||||||
|  | 	FORMA_PAPIR = 'papir' | ||||||
|  | 	FORMA_EMAIL = 'email' | ||||||
|  | 	FORMA_UPLOAD = 'upload' | ||||||
|  | 	FORMA_CHOICES = [ | ||||||
|  | 		(FORMA_PAPIR, 'Papírové řešení'), | ||||||
|  | 		(FORMA_EMAIL, 'Emailem'), | ||||||
|  | 		(FORMA_UPLOAD, 'Upload přes web'), | ||||||
|  | 	] | ||||||
|  | 	forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False, | ||||||
|  | 							 default=FORMA_EMAIL) | ||||||
|  | 
 | ||||||
|  | 	text_cely = models.OneToOneField('seminar.ReseniNode', verbose_name='Plná verze textu řešení', | ||||||
|  | 									 blank=True, null=True, related_name="reseni_cely_set", | ||||||
|  | 									 on_delete=models.PROTECT) | ||||||
|  | 
 | ||||||
|  | 	poznamka = models.TextField('neveřejná poznámka', blank=True, | ||||||
|  | 								help_text='Neveřejná poznámka k řešení (plain text)') | ||||||
|  | 
 | ||||||
|  | 	zverejneno = models.BooleanField('řešení zveřejněno', default=False, | ||||||
|  | 									 help_text='Udává, zda je řešení zveřejněno') | ||||||
|  | 
 | ||||||
|  | 	def verejne_url(self): | ||||||
|  | 		return str(reverse_lazy('odevzdavatko_detail_reseni', args=[self.id])) | ||||||
|  | 
 | ||||||
|  | 	def absolute_url(self): | ||||||
|  | 		return "https://" + str(get_current_site(None)) + self.verejne_url() | ||||||
|  | 
 | ||||||
|  | 	# má OneToOneField s: | ||||||
|  | 	# Konfera | ||||||
|  | 
 | ||||||
|  | 	# má ForeignKey s: | ||||||
|  | 	# Hodnoceni | ||||||
|  | 
 | ||||||
|  | 	def sum_body(self): | ||||||
|  | 		return self.hodnoceni_set.all().aggregate(Sum('body'))["body__sum"] | ||||||
|  | 
 | ||||||
|  | 	def __str__(self): | ||||||
|  | 		return "{}({}): {}({})".format(self.resitele.first(),len(self.resitele.all()), self.problem.first() ,len(self.problem.all())) | ||||||
|  | 	# NOTE: Potenciální DB HOG (bez select_related) | ||||||
|  | 
 | ||||||
|  | 	def deadline_reseni(self): | ||||||
|  | 		return am.Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first() | ||||||
|  | 
 | ||||||
|  | ## Pravdepodobne uz nebude potreba: | ||||||
|  | #	def save(self, *args, **kwargs): | ||||||
|  | #		if ((self.cislo_body is None) and (self.problem.cislo_reseni) and | ||||||
|  | #				(self.problem.typ == Problem.TYP_ULOHA)): | ||||||
|  | #			self.cislo_body = self.problem.cislo_reseni | ||||||
|  | #		super(Reseni, self).save(*args, **kwargs) | ||||||
|  | 
 | ||||||
|  | class Hodnoceni(bm.SeminarModelBase): | ||||||
|  | 	class Meta: | ||||||
|  | 		db_table = 'seminar_hodnoceni' | ||||||
|  | 		verbose_name = 'Hodnocení' | ||||||
|  | 		verbose_name_plural = 'Hodnocení' | ||||||
|  | 
 | ||||||
|  | 	# Interní ID | ||||||
|  | 	id = models.AutoField(primary_key = True) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body', | ||||||
|  | 							   blank=True, null=True) | ||||||
|  | 
 | ||||||
|  | 	cislo_body = models.ForeignKey(am.Cislo, verbose_name='číslo pro body', | ||||||
|  | 								   related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT) | ||||||
|  | 
 | ||||||
|  | 	# V ročníku < 26 nastaveno na deadline vygenerovaný pro původní cislo_body | ||||||
|  | 	deadline_body = models.ForeignKey(am.Deadline, verbose_name='deadline pro body', | ||||||
|  | 								   related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT) | ||||||
|  | 
 | ||||||
|  | 	reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) | ||||||
|  | 
 | ||||||
|  | 	problem = models.ForeignKey(am.Problem, verbose_name='problém', | ||||||
|  | 								related_name='hodnoceni', on_delete=models.PROTECT) | ||||||
|  | 
 | ||||||
|  | 	feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)') | ||||||
|  | 
 | ||||||
|  | 	@property | ||||||
|  | 	def body_celkem(self): | ||||||
|  | 		# FIXME řeším jen prvního řešitele. | ||||||
|  | 		return Hodnoceni.objects.filter(problem=self.problem, reseni__resitele=self.reseni.resitele.first(), body__isnull=False).aggregate(Sum("body"))["body__sum"] | ||||||
|  | 
 | ||||||
|  | 	@body_celkem.setter | ||||||
|  | 	def body_celkem(self, value): | ||||||
|  | 		if value is None: | ||||||
|  | 			self.body = None | ||||||
|  | 		else: | ||||||
|  | 			if self.body is None: | ||||||
|  | 				self.body = 0 | ||||||
|  | 			if self.body_celkem is None: | ||||||
|  | 				self.body += value | ||||||
|  | 			else: | ||||||
|  | 				self.body += value - self.body_celkem | ||||||
|  | 
 | ||||||
|  | 	@property | ||||||
|  | 	def body_neprepocitane(self): | ||||||
|  | 		if self.body is None: | ||||||
|  | 			return None | ||||||
|  | 		return inverze_vzorecku_na_prepocet(self.body, self.reseni.resitele.count()) | ||||||
|  | 
 | ||||||
|  | 	@body_neprepocitane.setter | ||||||
|  | 	def body_neprepocitane(self, value): | ||||||
|  | 		if value is None: | ||||||
|  | 			self.body = None | ||||||
|  | 		else: | ||||||
|  | 			self.body = vzorecek_na_prepocet(value, self.reseni.resitele.count()) | ||||||
|  | 
 | ||||||
|  | 	@property | ||||||
|  | 	def body_neprepocitane_celkem(self): | ||||||
|  | 		if self.body_celkem is None: | ||||||
|  | 			return None | ||||||
|  | 		return inverze_vzorecku_na_prepocet(self.body_celkem, self.reseni.resitele.count()) | ||||||
|  | 
 | ||||||
|  | 	@body_neprepocitane_celkem.setter | ||||||
|  | 	def body_neprepocitane_celkem(self, value): | ||||||
|  | 		if value is None: | ||||||
|  | 			self.body = None | ||||||
|  | 		else: | ||||||
|  | 			self.body_celkem = vzorecek_na_prepocet(value, self.reseni.resitele.count()) | ||||||
|  | 
 | ||||||
|  | 	@property | ||||||
|  | 	def body_max(self): | ||||||
|  | 		if self.body_neprepocitane_max is None: | ||||||
|  | 			return None | ||||||
|  | 		return vzorecek_na_prepocet(self.body_neprepocitane_max, self.reseni.resitele.count()) | ||||||
|  | 
 | ||||||
|  | 	@property | ||||||
|  | 	def body_neprepocitane_max(self): | ||||||
|  | 		if not isinstance(self.problem.get_real_instance(), am.Uloha): | ||||||
|  | 			return None | ||||||
|  | 		return self.problem.uloha.max_body | ||||||
|  | 
 | ||||||
|  | 	def __str__(self): | ||||||
|  | 		return "{}, {}, {}".format(self.problem, self.reseni, self.body) | ||||||
|  | 
 | ||||||
|  | def generate_filename(self, filename): | ||||||
|  | 	return os.path.join( | ||||||
|  | 		settings.SEMINAR_RESENI_DIR, | ||||||
|  | 		am.aux_generate_filename(self, filename) | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @reversion.register(ignore_duplicates=True) | ||||||
|  | class PrilohaReseni(bm.SeminarModelBase): | ||||||
|  | 
 | ||||||
|  | 	class Meta: | ||||||
|  | 		db_table = 'seminar_priloha_reseni' | ||||||
|  | 		verbose_name = 'Příloha řešení' | ||||||
|  | 		verbose_name_plural = 'Přílohy řešení' | ||||||
|  | 		ordering = ['reseni', 'vytvoreno'] | ||||||
|  | 
 | ||||||
|  | 	# Interní ID | ||||||
|  | 	id = models.AutoField(primary_key = True) | ||||||
|  | 
 | ||||||
|  | 	reseni = models.ForeignKey(Reseni, verbose_name='řešení', related_name='prilohy', | ||||||
|  | 							   on_delete=models.CASCADE) | ||||||
|  | 
 | ||||||
|  | 	vytvoreno = models.DateTimeField('vytvořeno', default=timezone.now, blank=True, editable=False) | ||||||
|  | 
 | ||||||
|  | 	soubor = models.FileField('soubor', upload_to = generate_filename) | ||||||
|  | 
 | ||||||
|  | 	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') | ||||||
|  | 
 | ||||||
|  | 	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): | ||||||
|  | 		return str(self.soubor) | ||||||
|  | 
 | ||||||
|  | 	def split(self): | ||||||
|  | 		"Vrátí cestu rozsekanou po složkách. To se hodí v templatech" | ||||||
|  | 		# Věřím, že tohle funguje, případně použít os.path nebo pathlib. | ||||||
|  | 		return self.soubor.url.split('/') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Vazebna tabulka. Mozna se generuje automaticky. | ||||||
|  | @reversion.register(ignore_duplicates=True) | ||||||
|  | class Reseni_Resitele(models.Model): | ||||||
|  | 
 | ||||||
|  | 	class Meta: | ||||||
|  | 		db_table = 'seminar_reseni_resitele' | ||||||
|  | 		verbose_name = 'Řešení řešitelů' | ||||||
|  | 		verbose_name_plural = 'Řešení řešitelů' | ||||||
|  | 		ordering = ['reseni', 'resitele'] | ||||||
|  | 
 | ||||||
|  | 	# Interní ID | ||||||
|  | 	id = models.AutoField(primary_key = True) | ||||||
|  | 
 | ||||||
|  | 	resitele = models.ForeignKey(Resitel, verbose_name='řešitel', on_delete=models.PROTECT) | ||||||
|  | 
 | ||||||
|  | 	reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) | ||||||
|  | 
 | ||||||
|  | 	# podil - jakou merou se ktery resitel podilel na danem reseni | ||||||
|  | 	#	- pouziti v budoucnu, pokud by resitele nemeli dostat vsichni stejne bodu za spolecne reseni | ||||||
|  | 
 | ||||||
|  | 	def __str__(self): | ||||||
|  | 		return '{} od {}'.format(self.reseni, self.resitel) | ||||||
|  | 	# NOTE: Poteciální DB HOG bez select_related | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| import datetime | import datetime | ||||||
| import random | import random | ||||||
| 
 | 
 | ||||||
| from seminar.models.odevzdavatko import Reseni, Hodnoceni | from odevzdavatko.models import Reseni, Hodnoceni | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def gen_reseni_ulohy(rnd, cisla, uloha, pocet_resitelu, poradi_cisla, resitele_cisla, resitele): | def gen_reseni_ulohy(rnd, cisla, uloha, pocet_resitelu, poradi_cisla, resitele_cisla, resitele): | ||||||
|  |  | ||||||
							
								
								
									
										13
									
								
								personalni/migrations/0012_odstrel_odevzdavatka_pre.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								personalni/migrations/0012_odstrel_odevzdavatka_pre.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | ||||||
|  | # Generated by Django 4.2.13 on 2024-10-22 22:17 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('personalni', '0011_osloveni_vsechny_choices'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |     ] | ||||||
							
								
								
									
										14
									
								
								personalni/migrations/0013_odstrel_odevzdavatka_post.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								personalni/migrations/0013_odstrel_odevzdavatka_post.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | ||||||
|  | # Generated by Django 4.2.13 on 2024-10-23 21:10 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('personalni', '0012_odstrel_odevzdavatka_pre'), | ||||||
|  |         ('odevzdavatko', '0003_odstrel_odevzdavatka_post'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |     ] | ||||||
|  | @ -296,7 +296,7 @@ class Resitel(SeminarModelBase): | ||||||
| 	def vsechny_body(self): | 	def vsechny_body(self): | ||||||
| 		"Spočítá body odjakživa." | 		"Spočítá body odjakživa." | ||||||
| 		vsechna_reseni = self.reseni_set.all() | 		vsechna_reseni = self.reseni_set.all() | ||||||
| 		from seminar.models.odevzdavatko import Hodnoceni | 		from odevzdavatko.models import Hodnoceni | ||||||
| 		vsechna_hodnoceni = Hodnoceni.objects.filter( | 		vsechna_hodnoceni = Hodnoceni.objects.filter( | ||||||
| 			reseni__in=vsechna_reseni) | 			reseni__in=vsechna_reseni) | ||||||
| 		return sum(h.body for h in list(vsechna_hodnoceni) if h.body is not None) | 		return sum(h.body for h in list(vsechna_hodnoceni) if h.body is not None) | ||||||
|  | @ -343,7 +343,7 @@ class Resitel(SeminarModelBase): | ||||||
| 		#  - body z 25. ročníku a dříve byly shledány dvakrát hodnotnějšími | 		#  - body z 25. ročníku a dříve byly shledány dvakrát hodnotnějšími | ||||||
| 		#  - proto se započítávají dvojnásobně a byly posunuté hranice titulů | 		#  - proto se započítávají dvojnásobně a byly posunuté hranice titulů | ||||||
| 		#  - staré tituly se ale nemají odebrat, pokud řešitel v t.č. minulém (26.) ročníku měl titul, má ho mít pořád. | 		#  - staré tituly se ale nemají odebrat, pokud řešitel v t.č. minulém (26.) ročníku měl titul, má ho mít pořád. | ||||||
| 		from seminar.models.odevzdavatko import Hodnoceni | 		from odevzdavatko.models import Hodnoceni | ||||||
| 		hodnoceni_do_25_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=25,reseni__in=self.reseni_set.all()) | 		hodnoceni_do_25_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=25,reseni__in=self.reseni_set.all()) | ||||||
| 		novejsi_hodnoceni = Hodnoceni.objects.filter(reseni__in=self.reseni_set.all()).difference(hodnoceni_do_25_rocniku) | 		novejsi_hodnoceni = Hodnoceni.objects.filter(reseni__in=self.reseni_set.all()).difference(hodnoceni_do_25_rocniku) | ||||||
| 
 | 
 | ||||||
|  | @ -381,7 +381,7 @@ class Resitel(SeminarModelBase): | ||||||
| 			else: | 			else: | ||||||
| 				return Titul.akad | 				return Titul.akad | ||||||
| 
 | 
 | ||||||
| 		from seminar.models.odevzdavatko import Hodnoceni | 		from odevzdavatko.models import Hodnoceni | ||||||
| 		hodnoceni_do_26_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=26,reseni__in=self.reseni_set.all()) | 		hodnoceni_do_26_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=26,reseni__in=self.reseni_set.all()) | ||||||
| 		novejsi_body = body_z_hodnoceni( | 		novejsi_body = body_z_hodnoceni( | ||||||
| 			Hodnoceni.objects.filter(reseni__in=self.reseni_set.all()) | 			Hodnoceni.objects.filter(reseni__in=self.reseni_set.all()) | ||||||
|  |  | ||||||
							
								
								
									
										14
									
								
								seminar/migrations/0131_odstrel_odevzdavatka_pre.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								seminar/migrations/0131_odstrel_odevzdavatka_pre.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | ||||||
|  | # Generated by Django 4.2.13 on 2024-10-22 22:17 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('seminar', '0130_clanek_strana'), | ||||||
|  |         ('personalni', '0012_odstrel_odevzdavatka_pre'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |     ] | ||||||
							
								
								
									
										29
									
								
								seminar/migrations/0132_unmanage_odevzdavatko.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								seminar/migrations/0132_unmanage_odevzdavatko.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | ||||||
|  | # Generated by Django 4.2.13 on 2024-10-22 22:31 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('seminar', '0131_odstrel_odevzdavatka_pre'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AlterModelOptions( | ||||||
|  |             name='hodnoceni', | ||||||
|  |             options={'managed': False, 'verbose_name': 'Hodnocení', 'verbose_name_plural': 'Hodnocení'}, | ||||||
|  |         ), | ||||||
|  |         migrations.AlterModelOptions( | ||||||
|  |             name='prilohareseni', | ||||||
|  |             options={'managed': False, 'ordering': ['reseni', 'vytvoreno'], 'verbose_name': 'Příloha řešení', 'verbose_name_plural': 'Přílohy řešení'}, | ||||||
|  |         ), | ||||||
|  |         migrations.AlterModelOptions( | ||||||
|  |             name='reseni', | ||||||
|  |             options={'managed': False, 'ordering': ['-cas_doruceni'], 'verbose_name': 'Řešení', 'verbose_name_plural': 'Řešení'}, | ||||||
|  |         ), | ||||||
|  |         migrations.AlterModelOptions( | ||||||
|  |             name='reseni_resitele', | ||||||
|  |             options={'managed': False, 'ordering': ['reseni', 'resitele'], 'verbose_name': 'Řešení řešitelů', 'verbose_name_plural': 'Řešení řešitelů'}, | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
							
								
								
									
										20
									
								
								seminar/migrations/0133_relink_odevzdavatko.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								seminar/migrations/0133_relink_odevzdavatko.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | ||||||
|  | # Generated by Django 4.2.13 on 2024-10-23 19:53 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations, models | ||||||
|  | import django.db.models.deletion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('odevzdavatko', '0001_create'), | ||||||
|  |         ('seminar', '0132_unmanage_odevzdavatko'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='reseninode', | ||||||
|  |             name='reseni', | ||||||
|  |             field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='odevzdavatko.reseni', verbose_name='reseni'), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
							
								
								
									
										50
									
								
								seminar/migrations/0134_delete_odevzdavatko.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								seminar/migrations/0134_delete_odevzdavatko.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,50 @@ | ||||||
|  | # Generated by Django 4.2.13 on 2024-10-23 19:56 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('seminar', '0133_relink_odevzdavatko'), | ||||||
|  |         ('odevzdavatko', '0001_create'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.RemoveField( | ||||||
|  |             model_name='prilohareseni', | ||||||
|  |             name='reseni', | ||||||
|  |         ), | ||||||
|  |         migrations.RemoveField( | ||||||
|  |             model_name='reseni', | ||||||
|  |             name='problem', | ||||||
|  |         ), | ||||||
|  |         migrations.RemoveField( | ||||||
|  |             model_name='reseni', | ||||||
|  |             name='resitele', | ||||||
|  |         ), | ||||||
|  |         migrations.RemoveField( | ||||||
|  |             model_name='reseni', | ||||||
|  |             name='text_cely', | ||||||
|  |         ), | ||||||
|  |         migrations.RemoveField( | ||||||
|  |             model_name='reseni_resitele', | ||||||
|  |             name='reseni', | ||||||
|  |         ), | ||||||
|  |         migrations.RemoveField( | ||||||
|  |             model_name='reseni_resitele', | ||||||
|  |             name='resitele', | ||||||
|  |         ), | ||||||
|  |         migrations.DeleteModel( | ||||||
|  |             name='Hodnoceni', | ||||||
|  |         ), | ||||||
|  |         migrations.DeleteModel( | ||||||
|  |             name='PrilohaReseni', | ||||||
|  |         ), | ||||||
|  |         migrations.DeleteModel( | ||||||
|  |             name='Reseni', | ||||||
|  |         ), | ||||||
|  |         migrations.DeleteModel( | ||||||
|  |             name='Reseni_Resitele', | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
							
								
								
									
										14
									
								
								seminar/migrations/0135_odstrel_odevzdavatka_post.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								seminar/migrations/0135_odstrel_odevzdavatka_post.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | ||||||
|  | # Generated by Django 4.2.13 on 2024-10-23 21:10 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('seminar', '0134_delete_odevzdavatko'), | ||||||
|  |         ('odevzdavatko', '0003_odstrel_odevzdavatka_post'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |     ] | ||||||
|  | @ -8,6 +8,9 @@ from various.models import Nastaveni | ||||||
| from personalni.models import Organizator, Resitel, Skola, Prijemce, Osoba | from personalni.models import Organizator, Resitel, Skola, Prijemce, Osoba | ||||||
| from soustredeni.models import Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Konfera, Konfery_Ucastnici | from soustredeni.models import Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Konfera, Konfery_Ucastnici | ||||||
| from novinky.models import Novinky | from novinky.models import Novinky | ||||||
|  | from odevzdavatko.models import Reseni, PrilohaReseni, Reseni_Resitele, Hodnoceni | ||||||
| 
 | 
 | ||||||
| # Kvůli migr. 0041 | # Kvůli migr. 0041 | ||||||
| from soustredeni.models import generate_filename_konfera | from soustredeni.models import generate_filename_konfera | ||||||
|  | # migr. 0001 | ||||||
|  | from odevzdavatko.models import generate_filename | ||||||
|  |  | ||||||
|  | @ -14,8 +14,9 @@ class SeminarModelBase(models.Model): | ||||||
| 	# 	return "https://" + str(get_current_site(None)) + self.verejne_url() | 	# 	return "https://" + str(get_current_site(None)) + self.verejne_url() | ||||||
| 
 | 
 | ||||||
| 	def admin_url(self): | 	def admin_url(self): | ||||||
| 		model_name = self.__class__.__name__.lower() | 		app_name = self._meta.app_label | ||||||
| 		return reverse('admin:seminar_{}_change'.format(model_name), args=(self.id, )) | 		model_name = self._meta.model_name | ||||||
|  | 		return reverse('admin:{}_{}_change'.format(app_name, model_name), args=(self.id, )) | ||||||
| 
 | 
 | ||||||
| # def verejne_url(self): | # def verejne_url(self): | ||||||
| # 	return None | # 	return None | ||||||
|  |  | ||||||
|  | @ -1,244 +1,7 @@ | ||||||
| import os |  | ||||||
| 
 |  | ||||||
| import reversion |  | ||||||
| 
 |  | ||||||
| from django.contrib.sites.shortcuts import get_current_site |  | ||||||
| from django.db import models | from django.db import models | ||||||
| from django.db.models import Sum |  | ||||||
| from django.urls import reverse_lazy |  | ||||||
| from django.utils import timezone |  | ||||||
| from django.conf import settings |  | ||||||
| 
 | 
 | ||||||
| from seminar.models import tvorba as am |  | ||||||
| from seminar.models import treenode as tm | from seminar.models import treenode as tm | ||||||
| from seminar.models import base as bm | from odevzdavatko.models import Reseni | ||||||
| 
 |  | ||||||
| from odevzdavatko.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet |  | ||||||
| from personalni.models import Resitel |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @reversion.register(ignore_duplicates=True) |  | ||||||
| class Reseni(bm.SeminarModelBase): |  | ||||||
| 
 |  | ||||||
| 	class Meta: |  | ||||||
| 		db_table = 'seminar_reseni' |  | ||||||
| 		verbose_name = 'Řešení' |  | ||||||
| 		verbose_name_plural = 'Řešení' |  | ||||||
| 		#ordering = ['-problem', 'resitele']	# FIXME: Takhle to chceme, ale nefunguje to. |  | ||||||
| 		ordering = ['-cas_doruceni'] |  | ||||||
| 
 |  | ||||||
| 	# Interní ID |  | ||||||
| 	id = models.AutoField(primary_key = True) |  | ||||||
| 
 |  | ||||||
| 	# Ke každé dvojici řešní a problém existuje nanejvýš jedno hodnocení, doplnění vazby. |  | ||||||
| 	problem = models.ManyToManyField(am.Problem, verbose_name='problém', help_text='Problém', |  | ||||||
| 									 through='Hodnoceni') |  | ||||||
| 
 |  | ||||||
| 	resitele = models.ManyToManyField(Resitel, verbose_name='autoři řešení', |  | ||||||
| 									  help_text='Seznam autorů řešení', through='Reseni_Resitele') |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 	cas_doruceni = models.DateTimeField('čas_doručení', default=timezone.now, blank=True) |  | ||||||
| 
 |  | ||||||
| 	FORMA_PAPIR = 'papir' |  | ||||||
| 	FORMA_EMAIL = 'email' |  | ||||||
| 	FORMA_UPLOAD = 'upload' |  | ||||||
| 	FORMA_CHOICES = [ |  | ||||||
| 		(FORMA_PAPIR, 'Papírové řešení'), |  | ||||||
| 		(FORMA_EMAIL, 'Emailem'), |  | ||||||
| 		(FORMA_UPLOAD, 'Upload přes web'), |  | ||||||
| 	] |  | ||||||
| 	forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False, |  | ||||||
| 							 default=FORMA_EMAIL) |  | ||||||
| 
 |  | ||||||
| 	text_cely = models.OneToOneField('ReseniNode', verbose_name='Plná verze textu řešení', |  | ||||||
| 									 blank=True, null=True, related_name="reseni_cely_set", |  | ||||||
| 									 on_delete=models.PROTECT) |  | ||||||
| 
 |  | ||||||
| 	poznamka = models.TextField('neveřejná poznámka', blank=True, |  | ||||||
| 								help_text='Neveřejná poznámka k řešení (plain text)') |  | ||||||
| 
 |  | ||||||
| 	zverejneno = models.BooleanField('řešení zveřejněno', default=False, |  | ||||||
| 									 help_text='Udává, zda je řešení zveřejněno') |  | ||||||
| 
 |  | ||||||
| 	def verejne_url(self): |  | ||||||
| 		return str(reverse_lazy('odevzdavatko_detail_reseni', args=[self.id])) |  | ||||||
| 
 |  | ||||||
| 	def absolute_url(self): |  | ||||||
| 		return "https://" + str(get_current_site(None)) + self.verejne_url() |  | ||||||
| 
 |  | ||||||
| 	# má OneToOneField s: |  | ||||||
| 	# Konfera |  | ||||||
| 
 |  | ||||||
| 	# má ForeignKey s: |  | ||||||
| 	# Hodnoceni |  | ||||||
| 
 |  | ||||||
| 	def sum_body(self): |  | ||||||
| 		return self.hodnoceni_set.all().aggregate(Sum('body'))["body__sum"] |  | ||||||
| 
 |  | ||||||
| 	def __str__(self): |  | ||||||
| 		return "{}({}): {}({})".format(self.resitele.first(),len(self.resitele.all()), self.problem.first() ,len(self.problem.all())) |  | ||||||
| 	# NOTE: Potenciální DB HOG (bez select_related) |  | ||||||
| 
 |  | ||||||
| 	def deadline_reseni(self): |  | ||||||
| 		return am.Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first() |  | ||||||
| 
 |  | ||||||
| ## Pravdepodobne uz nebude potreba: |  | ||||||
| #	def save(self, *args, **kwargs): |  | ||||||
| #		if ((self.cislo_body is None) and (self.problem.cislo_reseni) and |  | ||||||
| #				(self.problem.typ == Problem.TYP_ULOHA)): |  | ||||||
| #			self.cislo_body = self.problem.cislo_reseni |  | ||||||
| #		super(Reseni, self).save(*args, **kwargs) |  | ||||||
| 
 |  | ||||||
| class Hodnoceni(bm.SeminarModelBase): |  | ||||||
| 	class Meta: |  | ||||||
| 		db_table = 'seminar_hodnoceni' |  | ||||||
| 		verbose_name = 'Hodnocení' |  | ||||||
| 		verbose_name_plural = 'Hodnocení' |  | ||||||
| 
 |  | ||||||
| 	# Interní ID |  | ||||||
| 	id = models.AutoField(primary_key = True) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 	body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body', |  | ||||||
| 							   blank=True, null=True) |  | ||||||
| 
 |  | ||||||
| 	cislo_body = models.ForeignKey(am.Cislo, verbose_name='číslo pro body', |  | ||||||
| 								   related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT) |  | ||||||
| 
 |  | ||||||
| 	# V ročníku < 26 nastaveno na deadline vygenerovaný pro původní cislo_body |  | ||||||
| 	deadline_body = models.ForeignKey(am.Deadline, verbose_name='deadline pro body', |  | ||||||
| 								   related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT) |  | ||||||
| 
 |  | ||||||
| 	reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) |  | ||||||
| 
 |  | ||||||
| 	problem = models.ForeignKey(am.Problem, verbose_name='problém', |  | ||||||
| 								related_name='hodnoceni', on_delete=models.PROTECT) |  | ||||||
| 
 |  | ||||||
| 	feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)') |  | ||||||
| 
 |  | ||||||
| 	@property |  | ||||||
| 	def body_celkem(self): |  | ||||||
| 		# FIXME řeším jen prvního řešitele. |  | ||||||
| 		return Hodnoceni.objects.filter(problem=self.problem, reseni__resitele=self.reseni.resitele.first(), body__isnull=False).aggregate(Sum("body"))["body__sum"] |  | ||||||
| 
 |  | ||||||
| 	@body_celkem.setter |  | ||||||
| 	def body_celkem(self, value): |  | ||||||
| 		if value is None: |  | ||||||
| 			self.body = None |  | ||||||
| 		else: |  | ||||||
| 			if self.body is None: |  | ||||||
| 				self.body = 0 |  | ||||||
| 			if self.body_celkem is None: |  | ||||||
| 				self.body += value |  | ||||||
| 			else: |  | ||||||
| 				self.body += value - self.body_celkem |  | ||||||
| 
 |  | ||||||
| 	@property |  | ||||||
| 	def body_neprepocitane(self): |  | ||||||
| 		if self.body is None: |  | ||||||
| 			return None |  | ||||||
| 		return inverze_vzorecku_na_prepocet(self.body, self.reseni.resitele.count()) |  | ||||||
| 
 |  | ||||||
| 	@body_neprepocitane.setter |  | ||||||
| 	def body_neprepocitane(self, value): |  | ||||||
| 		if value is None: |  | ||||||
| 			self.body = None |  | ||||||
| 		else: |  | ||||||
| 			self.body = vzorecek_na_prepocet(value, self.reseni.resitele.count()) |  | ||||||
| 
 |  | ||||||
| 	@property |  | ||||||
| 	def body_neprepocitane_celkem(self): |  | ||||||
| 		if self.body_celkem is None: |  | ||||||
| 			return None |  | ||||||
| 		return inverze_vzorecku_na_prepocet(self.body_celkem, self.reseni.resitele.count()) |  | ||||||
| 
 |  | ||||||
| 	@body_neprepocitane_celkem.setter |  | ||||||
| 	def body_neprepocitane_celkem(self, value): |  | ||||||
| 		if value is None: |  | ||||||
| 			self.body = None |  | ||||||
| 		else: |  | ||||||
| 			self.body_celkem = vzorecek_na_prepocet(value, self.reseni.resitele.count()) |  | ||||||
| 
 |  | ||||||
| 	@property |  | ||||||
| 	def body_max(self): |  | ||||||
| 		if self.body_neprepocitane_max is None: |  | ||||||
| 			return None |  | ||||||
| 		return vzorecek_na_prepocet(self.body_neprepocitane_max, self.reseni.resitele.count()) |  | ||||||
| 
 |  | ||||||
| 	@property |  | ||||||
| 	def body_neprepocitane_max(self): |  | ||||||
| 		if not isinstance(self.problem.get_real_instance(), am.Uloha): |  | ||||||
| 			return None |  | ||||||
| 		return self.problem.uloha.max_body |  | ||||||
| 
 |  | ||||||
| 	def __str__(self): |  | ||||||
| 		return "{}, {}, {}".format(self.problem, self.reseni, self.body) |  | ||||||
| 
 |  | ||||||
| def generate_filename(self, filename): |  | ||||||
| 	return os.path.join( |  | ||||||
| 		settings.SEMINAR_RESENI_DIR, |  | ||||||
| 		am.aux_generate_filename(self, filename) |  | ||||||
| 	) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @reversion.register(ignore_duplicates=True) |  | ||||||
| class PrilohaReseni(bm.SeminarModelBase): |  | ||||||
| 
 |  | ||||||
| 	class Meta: |  | ||||||
| 		db_table = 'seminar_priloha_reseni' |  | ||||||
| 		verbose_name = 'Příloha řešení' |  | ||||||
| 		verbose_name_plural = 'Přílohy řešení' |  | ||||||
| 		ordering = ['reseni', 'vytvoreno'] |  | ||||||
| 
 |  | ||||||
| 	# Interní ID |  | ||||||
| 	id = models.AutoField(primary_key = True) |  | ||||||
| 
 |  | ||||||
| 	reseni = models.ForeignKey(Reseni, verbose_name='řešení', related_name='prilohy', |  | ||||||
| 							   on_delete=models.CASCADE) |  | ||||||
| 
 |  | ||||||
| 	vytvoreno = models.DateTimeField('vytvořeno', default=timezone.now, blank=True, editable=False) |  | ||||||
| 
 |  | ||||||
| 	soubor = models.FileField('soubor', upload_to = generate_filename) |  | ||||||
| 
 |  | ||||||
| 	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') |  | ||||||
| 
 |  | ||||||
| 	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): |  | ||||||
| 		return str(self.soubor) |  | ||||||
| 
 |  | ||||||
| 	def split(self): |  | ||||||
| 		"Vrátí cestu rozsekanou po složkách. To se hodí v templatech" |  | ||||||
| 		# Věřím, že tohle funguje, případně použít os.path nebo pathlib. |  | ||||||
| 		return self.soubor.url.split('/') |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Vazebna tabulka. Mozna se generuje automaticky. |  | ||||||
| @reversion.register(ignore_duplicates=True) |  | ||||||
| class Reseni_Resitele(models.Model): |  | ||||||
| 
 |  | ||||||
| 	class Meta: |  | ||||||
| 		db_table = 'seminar_reseni_resitele' |  | ||||||
| 		verbose_name = 'Řešení řešitelů' |  | ||||||
| 		verbose_name_plural = 'Řešení řešitelů' |  | ||||||
| 		ordering = ['reseni', 'resitele'] |  | ||||||
| 
 |  | ||||||
| 	# Interní ID |  | ||||||
| 	id = models.AutoField(primary_key = True) |  | ||||||
| 
 |  | ||||||
| 	resitele = models.ForeignKey(Resitel, verbose_name='řešitel', on_delete=models.PROTECT) |  | ||||||
| 
 |  | ||||||
| 	reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) |  | ||||||
| 
 |  | ||||||
| 	# podil - jakou merou se ktery resitel podilel na danem reseni |  | ||||||
| 	#	- pouziti v budoucnu, pokud by resitele nemeli dostat vsichni stejne bodu za spolecne reseni |  | ||||||
| 
 |  | ||||||
| 	def __str__(self): |  | ||||||
| 		return '{} od {}'.format(self.reseni, self.resitel) |  | ||||||
| 	# NOTE: Poteciální DB HOG bez select_related |  | ||||||
| 
 | 
 | ||||||
| class ReseniNode(tm.TreeNode): | class ReseniNode(tm.TreeNode): | ||||||
| 	class Meta: | 	class Meta: | ||||||
|  |  | ||||||
|  | @ -10,7 +10,7 @@ from django.conf import settings | ||||||
| from personalni.models import Resitel, Organizator | from personalni.models import Resitel, Organizator | ||||||
| 
 | 
 | ||||||
| from seminar.models.base import SeminarModelBase | from seminar.models.base import SeminarModelBase | ||||||
| from seminar.models import tvorba as am | import seminar.models as am # tvorba | ||||||
| 
 | 
 | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
|  | @ -77,10 +77,6 @@ class Soustredeni(SeminarModelBase): | ||||||
| 		#return reverse('seminar_soustredeni', kwargs={'pk': self.id}) | 		#return reverse('seminar_soustredeni', kwargs={'pk': self.id}) | ||||||
| 		return reverse('seminar_seznam_soustredeni') | 		return reverse('seminar_seznam_soustredeni') | ||||||
| 
 | 
 | ||||||
| 	def admin_url(self): |  | ||||||
| 		model_name = self.__class__.__name__.lower() |  | ||||||
| 		return reverse('admin:soustredeni_{}_change'.format(model_name), args=(self.id, )) |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| @reversion.register(ignore_duplicates=True) | @reversion.register(ignore_duplicates=True) | ||||||
| class Soustredeni_Ucastnici(SeminarModelBase): | class Soustredeni_Ucastnici(SeminarModelBase): | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ from typing import Sequence | ||||||
| import lorem | import lorem | ||||||
| 
 | 
 | ||||||
| from .models import Soustredeni, Konfera | from .models import Soustredeni, Konfera | ||||||
| import seminar.models.tvorba as am | import seminar.models as am # tvorba | ||||||
| import personalni.models as pm | import personalni.models as pm | ||||||
| 
 | 
 | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
|  |  | ||||||
|  | @ -5,5 +5,6 @@ makemigrations | ||||||
| ! Doplnit ForeignKeys (Vypadá to, že se dá vesměs zkopírovat předpis z models.py, jen místo prvního fieldu dát `to='app.model'. Dokonce asi funguje použít už novou aplikaci pro vazby v rámci aplikace.) | ! Doplnit ForeignKeys (Vypadá to, že se dá vesměs zkopírovat předpis z models.py, jen místo prvního fieldu dát `to='app.model'. Dokonce asi funguje použít už novou aplikaci pro vazby v rámci aplikace.) | ||||||
| 	To samé s ManyToManyFieldy (through= musí taky být 'app.model') | 	To samé s ManyToManyFieldy (through= musí taky být 'app.model') | ||||||
| (Zdá se, že jde dobastlit tuhle migraci polozpětně – doplnit co chybělo až podle toho, co vygeneruje migrace po zamanagování nového modelu.) | (Zdá se, že jde dobastlit tuhle migraci polozpětně – doplnit co chybělo až podle toho, co vygeneruje migrace po zamanagování nového modelu.) | ||||||
|  | 	Alternativa: zagitovat si unmanaged model, upravit ho na `managed = True`, vyrobit migrace, vyrobit je ještě jednou (z nějakého důvodu) a vykrást ty. Pak `models.py` vrátit do unmanaged stavu a soubory s novými migracemi smazat bez náhrady (obdobné vzniknou znovu v případě potřeby). | ||||||
| doplnit závislost na unmanage | doplnit závislost na unmanage | ||||||
| migrate | migrate | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ from django.utils.safestring import mark_safe | ||||||
| 
 | 
 | ||||||
| import soustredeni.models | import soustredeni.models | ||||||
| 
 | 
 | ||||||
| from seminar.models.tvorba import Rocnik, ZmrazenaVysledkovka, Deadline, Uloha, Problem, Tema, Clanek, Cislo | from seminar.models import Rocnik, ZmrazenaVysledkovka, Deadline, Uloha, Problem, Tema, Clanek, Cislo # tvorba | ||||||
| 
 | 
 | ||||||
| admin.site.register(Rocnik) | admin.site.register(Rocnik) | ||||||
| admin.site.register(ZmrazenaVysledkovka) | admin.site.register(ZmrazenaVysledkovka) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue