merge správných novinek
This commit is contained in:
		
						commit
						6b9d9f35a7
					
				
					 12 changed files with 281 additions and 1846 deletions
				
			
		
							
								
								
									
										16
									
								
								init_local.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										16
									
								
								init_local.sh
									
									
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,16 @@ | |||
| #!/bin/sh | ||||
| 
 | ||||
| set -e # Spadni pokud něco spadne | ||||
| 
 | ||||
| # Check venvu | ||||
| # NOTE: zkontroluje i správnou složku -- existencí Makefilu | ||||
| make venv_check | ||||
| 
 | ||||
| # Vygenerujeme testdata | ||||
| ./manage.py testdata | ||||
| 
 | ||||
| # Nahrajeme statický obsah modelů | ||||
| ./manage.py loaddata flat.json sitetree_new.json | ||||
| 
 | ||||
| # Posbíráme statické soubory | ||||
| ./manage.py collectstatic | ||||
							
								
								
									
										1603
									
								
								obalky/lisak.eps
									
									
									
									
									
								
							
							
						
						
									
										1603
									
								
								obalky/lisak.eps
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -1,78 +0,0 @@ | |||
| \input czech.sty | ||||
| \input epsf | ||||
| \special{landscape} | ||||
| \nopagenumbers | ||||
| \hoffset=-1in | ||||
| \voffset=-1in | ||||
| \advance\voffset by 0.9cm | ||||
| %nove pridano, aby to fungovalo... | ||||
| \advance\hoffset by 6.5cm | ||||
| \hsize=22cm | ||||
| \vsize=16cm | ||||
| 
 | ||||
| \font\adrfonta=csssbx10 at 14pt | ||||
| \font\adrfontb=csssbx10 at 12pt | ||||
| \font\adrfontc=csss12 | ||||
| \font\tofont=csr12 at 16pt | ||||
| 
 | ||||
| \newdimen\fromskip | ||||
| \newdimen\toskip | ||||
| \fromskip=4.35cm | ||||
| \toskip=13.2cm | ||||
| 
 | ||||
| \def\first{\relax} | ||||
| 
 | ||||
| \catcode`_=13 | ||||
| %\catcode`_=12 | ||||
| 
 | ||||
| \def_{$\_$} | ||||
| 
 | ||||
| \advance\voffset by 2.5cm | ||||
| 
 | ||||
| 
 | ||||
| \def\obalka#1#2#3#4#5#6#7{ | ||||
| 
 | ||||
| \def\jmeno{#1} | ||||
| \def\prijm{#2} | ||||
| \def\skola{#3} | ||||
| \def\popis{} | ||||
| \def\first{} | ||||
| \def\ulice{#4} | ||||
| \def\PSC{#5} | ||||
| \def\mesto{#6} | ||||
| \def\stat{#7} | ||||
| \vskip-4mm\vbox to 0pt{\hbox to 0pt{\hskip1.4cm\epsfysize=2.55cm\epsfbox{lisak.eps}\hss}\vss} | ||||
| 
 | ||||
| \baselineskip=13pt | ||||
| \parindent=\fromskip | ||||
| \line{\indent\adrfonta Časopis M\&M,\hfil} | ||||
| \vskip3pt | ||||
| \line{\indent\adrfontb OPMK UK MFF\hfil} | ||||
| \vskip3pt | ||||
| \line{\indent\adrfontc Ke Karlovu 3, 121 16 Praha 2\hfil} | ||||
| \line{\indent\adrfontc Tel.: +420 221 911 235\hss} | ||||
| \line{\indent\adrfontc mam@atrey.karlin.mff.cuni.cz\hfil} | ||||
| 
 | ||||
| \vskip6.15cm | ||||
| \vbox to 0pt{\parindent=1.4cm\hsize=\toskip\advance\hsize by -1cm | ||||
| \vbox to 60pt{\vfil} \popis\vss} | ||||
| \parindent=\toskip | ||||
| \baselineskip=18pt | ||||
| \line{\indent\tofont\first\hfil} | ||||
| \line{\indent\tofont\jmeno\ \prijm\hfil} | ||||
| \ifx \skola \empty | ||||
| {} | ||||
| \else | ||||
| \line{\indent\tofont\skola\hfil} | ||||
| \fi | ||||
| \line{\indent\tofont\ulice\hfil} | ||||
| \line{\tofont\item{\PSC} \mesto\hfil} | ||||
| \vskip5pt | ||||
| \line{\indent\tofont\stat\hfil} | ||||
| 
 | ||||
| \vfil\eject | ||||
| } | ||||
| 
 | ||||
| %\obalka{Lenka}{Kopfová}{Leknínová 10}{746 01}{Opava} | ||||
| 
 | ||||
| 
 | ||||
|  | @ -1,53 +0,0 @@ | |||
| #!/usr/bin/python | ||||
| #coding: utf-8 | ||||
| 
 | ||||
| import psycopg2 | ||||
| import sys | ||||
| import subprocess | ||||
| import re | ||||
| 
 | ||||
| dbname="mam-prod" | ||||
| user="mam" | ||||
| 
 | ||||
| conn = psycopg2.connect("dbname={0} user={1}".format(dbname,user)) | ||||
| cur = conn.cursor() | ||||
| 
 | ||||
| names = [] | ||||
| 
 | ||||
| with open("obalky.sql") as qfile, open("obalky-template.tex") as texheader, open("obalky.tex","w") as texout : | ||||
|     texout.write(texheader.read()) | ||||
|     cur.execute(qfile.read()) | ||||
|     for row in cur.fetchall(): | ||||
|         (muz,jmeno,prijmeni,skola,ulice,mesto,psc,stat)=row | ||||
|         if (stat=='CZ'): | ||||
|             stat = "" | ||||
|         elif (stat=='SK'): | ||||
|             stat = "Slovenská republika" | ||||
|         else: | ||||
|             print("Neznamy stat: {}\n".format(stat)) | ||||
|         if (skola==None): | ||||
|             skola="" | ||||
|         psc = psc.replace(" ","") | ||||
|         psc = psc[0:3]+" "+psc[3:] | ||||
|          | ||||
|         texout.write("\\obalka{{{0}}}{{{1}}}{{{2}}}{{{3}}}{{{4}}}{{{5}}}{{{6}}}\n".format(jmeno,prijmeni,skola,ulice,psc,mesto,stat)) | ||||
|         names.append((jmeno,prijmeni)) | ||||
|     texout.write("\\bye\n") | ||||
| 
 | ||||
| cur.close() | ||||
| conn.close() | ||||
| 
 | ||||
| print("Spoustim csplain ...") | ||||
| output = subprocess.check_output(["csplain","obalky.tex"],stderr=subprocess.STDOUT) | ||||
| page = 0 | ||||
| for line in output.decode("utf-8").splitlines(): | ||||
|     pmatch = re.search("\[([0-9]+)\]",line) | ||||
|     if pmatch: | ||||
|         page = int(pmatch.group(1)) | ||||
|     errmatch = re.match("Overfull",line) | ||||
|     if errmatch: | ||||
|         print("Preteceni na strane",page,"u osoby",names[page][0],names[page][1]) | ||||
|          | ||||
| print("Spoustim dvipdf ...") | ||||
| subprocess.call(["dvipdf","obalky.dvi"]) | ||||
| print("Hotovo.") | ||||
|  | @ -1,26 +0,0 @@ | |||
| WITH akt_rocnik AS ( | ||||
| 	SELECT rocnik FROM seminar_rocniky  | ||||
| 	WHERE id=(SELECT aktualni_rocnik_id FROM seminar_nastaveni) | ||||
| ), id_rocniku AS ( | ||||
| 	SELECT id,prvni_rok  FROM seminar_rocniky  | ||||
| 	WHERE rocnik=(SELECT * FROM akt_rocnik) OR rocnik=(SELECT * FROM akt_rocnik)-1 | ||||
| ), id_cisel AS ( | ||||
| 	SELECT seminar_cisla.id FROM seminar_cisla  | ||||
| 	INNER JOIN id_rocniku ON rocnik_id=id_rocniku.id | ||||
| ), problemy AS ( | ||||
| 	SELECT seminar_problemy.id FROM seminar_problemy  | ||||
| 	INNER JOIN id_cisel ON cislo_zadani_id = id_cisel.id | ||||
| ), resitele AS( | ||||
| 	SELECT DISTINCT resitel_id FROM seminar_reseni  | ||||
| 	INNER JOIN problemy ON problem_id=problemy.id | ||||
| ) | ||||
| SELECT pohlavi_muz,jmeno,prijmeni,NULL AS skola,ulice,mesto,psc,stat FROM seminar_resitele  | ||||
| INNER JOIN resitele ON seminar_resitele.id=resitel_id | ||||
| WHERE zasilat='domu' AND rok_maturity > (SELECT MAX(prvni_rok) FROM id_rocniku) | ||||
| UNION | ||||
| SELECT res.pohlavi_muz,res.jmeno,res.prijmeni,sk.nazev,sk.ulice,sk.mesto,sk.psc,sk.stat  | ||||
| FROM seminar_resitele AS res | ||||
| INNER JOIN resitele ON res.id=resitel_id | ||||
| INNER JOIN seminar_skoly AS sk ON sk.id=skola_id | ||||
| WHERE zasilat='do_skoly' AND rok_maturity > (SELECT MAX(prvni_rok) FROM id_rocniku) | ||||
| ORDER BY prijmeni ASC, jmeno ASC | ||||
|  | @ -9,6 +9,7 @@ from .models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni | |||
| #from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva | ||||
| from .ovvpfile import OvvpFile | ||||
| from seminar import views | ||||
| from seminar.utils import aktivniResitele | ||||
| 
 | ||||
| class ExportIndexView(generic.View): | ||||
| 	def get(self, request): | ||||
|  | @ -76,7 +77,7 @@ class ExportRocnikView(generic.View): | |||
| 
 | ||||
| 		rocnik = get_object_or_404(Rocnik, prvni_rok=pr, exportovat=True) | ||||
| 		cislo = rocnik.posledni_zverejnena_vysledkovka_cislo() | ||||
| 		resitele = views.aktivniResitele(cislo.rocnik.rocnik, cislo.poradi, True) | ||||
| 		resitele = aktivniResitele(cislo, True) | ||||
| 		slovnik_body = views.secti_body_za_rocnik(cislo, resitele) | ||||
| 		_, setrizeni_resitele, setrizene_body = views.setrid_resitele_a_body(slovnik_body) | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,25 +5,32 @@ import django.db.models.deletion | |||
| from seminar.treelib import get_parent | ||||
| 
 | ||||
| def najdi_cislo(apps, schema_editor): | ||||
| 	# | ||||
| 	Clanek = apps.get_model('seminar', 'Clanek') | ||||
| 	Hodnoceni = apps.get_model('seminar', 'Hodnoceni') | ||||
| 	Reseni = apps.get_model('seminar', 'Reseni') | ||||
| 	ReseniNode = apps.get_model('seminar', 'ReseniNode') | ||||
| 	CisloNode = apps.get_model('seminar', 'CisloNode') | ||||
| 	ContentType = apps.get_model('contenttypes', 'ContentType') | ||||
| 	 | ||||
| 	for c in Clanek.objects.all(): | ||||
| 		reseni = c.reseni_set | ||||
| 		if (len(reseni) != 1): | ||||
| 		if (reseni.count() != 1):	# Pozor, reseni_set je Manager, takže se na něj musí trošku jinak | ||||
| 			raise ValueError("Článek k sobě má nejedno řešení!") | ||||
| 		r = reseni[0] | ||||
| 		aktualniNode = r.reseninode | ||||
| 		r = reseni.first() | ||||
| 		aktualniNode = r.text_cely # Hlavní ReseniNode pro řešení | ||||
| 		while aktualniNode is not None: | ||||
| 			if isinstance(aktualniNode, CisloNode): | ||||
| 				c.cislo = aktualniNode.cislo | ||||
| 			#if isinstance(aktualniNode, CisloNode): # Nejde, protože aktualniNode se tváří jako obecný TreeNode... | ||||
| 			# Ale v dokumentaci k django-polymorphic se píše, že tam jsou nastavené nějaké fieldy na známé hodnoty :-) | ||||
| 			# https://django-polymorphic.readthedocs.io/en/stable/migrating.html | ||||
| 			cislonode_ct = ContentType.objects.get_for_model(CisloNode) | ||||
| 			akt_ct = aktualniNode.polymorphic_ctype | ||||
| 			if akt_ct == cislonode_ct: | ||||
| 				# Zneužíváme tu opačnou vazbu k treenode_ptr, protože | ||||
| 				# aktualniNode je "jen" TreeNode a ne CisloNode | ||||
| 				c.cislo = aktualniNode.cislonode.cislo | ||||
| 				c.save() | ||||
| 				break | ||||
| 			aktualniNode = get_parent() | ||||
| 			aktualniNode = get_parent(aktualniNode) | ||||
| 		 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -39,5 +46,5 @@ class Migration(migrations.Migration): | |||
|             name='cislo', | ||||
|             field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vydane_clanky', to='seminar.Cislo', verbose_name='číslo vydání'), | ||||
|         ), | ||||
| 	migrations.RunPython(najdi_cislo), | ||||
|     migrations.RunPython(najdi_cislo, migrations.RunPython.noop), | ||||
|     ] | ||||
|  |  | |||
							
								
								
									
										32
									
								
								seminar/migrations/0085_nepovinna_prezdivka.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								seminar/migrations/0085_nepovinna_prezdivka.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | |||
| # Generated by Django 2.2.13 on 2020-06-24 22:57 | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| 
 | ||||
| def smaz_prezdivku(apps, schema_editor): | ||||
|     Osoba = apps.get_model('seminar', 'Osoba') | ||||
| 
 | ||||
|     for o in Osoba.objects.filter(prezdivka=''): | ||||
|         o.prezdivka = None | ||||
|         o.save() | ||||
| 
 | ||||
| def pridej_prezdivku(apps, schema_editor): | ||||
|     Osoba = apps.get_model('seminar', 'Osoba') | ||||
| 
 | ||||
|     for o in Osoba.objects.filter(prezdivka=None): | ||||
|         o.prezdivka = '' | ||||
|         o.save() | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('seminar', '0084_clanek_cislo'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.AlterField( | ||||
|             model_name='osoba', | ||||
|             name='prezdivka', | ||||
|             field=models.CharField(blank=True, max_length=256, null=True, verbose_name='přezdívka'), | ||||
|         ), | ||||
|     migrations.RunPython(smaz_prezdivku, pridej_prezdivku), | ||||
|     ] | ||||
|  | @ -13,7 +13,7 @@ from django.utils.encoding import force_text | |||
| from django.utils.text import slugify | ||||
| from django.urls import reverse | ||||
| from django.core.cache import cache | ||||
| from django.core.exceptions import ObjectDoesNotExist | ||||
| from django.core.exceptions import ObjectDoesNotExist, ValidationError | ||||
| from django.contrib.contenttypes.models import ContentType | ||||
| from django.utils.text import get_valid_filename | ||||
| from imagekit.models import ImageSpecField, ProcessedImageField | ||||
|  | @ -66,7 +66,7 @@ class Osoba(SeminarModelBase): | |||
| 
 | ||||
| 	prijmeni = models.CharField('příjmení', max_length=256) | ||||
| 
 | ||||
| 	prezdivka = models.CharField('přezdívka', max_length=256)  | ||||
| 	prezdivka = models.CharField('přezdívka', blank=True, null=True, max_length=256) | ||||
| 
 | ||||
| 	# User, pokud má na webu účet | ||||
| 	user = models.OneToOneField(settings.AUTH_USER_MODEL, blank=True, null=True,  | ||||
|  | @ -581,6 +581,11 @@ class Organizator(SeminarModelBase): | |||
| 		help_text="Škola, např. MFF, VŠCHT, VUT, ... prostě aby se nemuselo psát do studuje" | ||||
| 		"školu, ale jen obor, možnost zobrazit zvlášť") | ||||
| 
 | ||||
| 	def clean(self): | ||||
| 		if self.organizuje_od > self.organizuje_do: | ||||
| 			raise ValidationError("Organizátor nemůže skončit s organizováním dříve než začal!") | ||||
| 		super().clean() | ||||
| 
 | ||||
| 	def __str__(self): | ||||
| 		if self.osoba.prezdivka: | ||||
| 			return "{} '{}' {}".format(self.osoba.jmeno, | ||||
|  |  | |||
|  | @ -681,6 +681,77 @@ def otec_syn(otec, syn): | |||
| 	syn.save() | ||||
| 	otec.save() | ||||
| 
 | ||||
| def gen_clanek(rnd): | ||||
| 	logger.info("Generuji článek do čísla 22.2") | ||||
| 	clanek = m.Clanek.objects.create( | ||||
| 		nazev="Článek o Lorem ipsum", | ||||
| 		nadproblem=None, | ||||
| 		stav='vyreseny', | ||||
| 		zamereni=['I'], | ||||
| 		garant=rnd.choice(m.Organizator.objects.all()), | ||||
| 		kod='cl', | ||||
| 		) | ||||
| 	clanek.save() | ||||
| 
 | ||||
| 	reseni = m.Reseni.objects.create( | ||||
| 		zverejneno=True, | ||||
| 		) | ||||
| 	reseni.resitele.add(rnd.choice(m.Resitel.objects.all())) | ||||
| 	reseni.save() | ||||
| 
 | ||||
| 	cislo = m.Cislo.objects.get(rocnik__rocnik=22, poradi=2) | ||||
| 	cislonode = cislo.cislonode | ||||
| 
 | ||||
| 	hodnoceni = m.Hodnoceni.objects.create( | ||||
| 		body=15.0, | ||||
| 		cislo_body=cislo, | ||||
| 		reseni=reseni, | ||||
| 		problem=clanek, | ||||
| 		) | ||||
| 	hodnoceni.save() | ||||
| 
 | ||||
| 	reseninode = m.ReseniNode.objects.create( | ||||
| 		reseni=reseni | ||||
| 		) | ||||
| 	reseninode.save() | ||||
| 
 | ||||
| 	# Bude to celý text | ||||
| 	reseni.text_cely = reseninode | ||||
| 	reseni.save() | ||||
| 	 | ||||
| 	from seminar.treelib import insert_last_child, create_child | ||||
| 	insert_last_child(cislonode, reseninode) | ||||
| 
 | ||||
| 	# Vyrobíme nějaký obsah | ||||
| 	# FIXME: Ten, kdo vymyslel TreeLib (mj. týž, kdo psal tenhle kód), | ||||
| 	# nevyrobil vhodnou funkci, takže to postavíme pozpátku pomocí create_child | ||||
| 	# (které vyrábí _prvního_ syna) | ||||
| 	create_child(reseninode, m.CastNode, nadpis="Lorem ipsum") | ||||
| 	# Taky ten člověk nevyrobil vracení nových věcí... | ||||
| 	castnode = reseninode.first_child | ||||
| 	 | ||||
| 	# Úvodní odstaveček | ||||
| 	obsah = "Tohle je zamyšlení o textu lorem ipsum. Začneme a skončíme ukázkou." | ||||
| 	text = m.Text.objects.create( | ||||
| 		na_web=obsah, | ||||
| 		do_cisla=obsah, | ||||
| 		) | ||||
| 	text.save() | ||||
| 	create_child(reseninode, m.TextNode, text=text) | ||||
| 
 | ||||
| 	# Několik odstavců lorem ipsum | ||||
| 	for _ in range(rnd.randint(3, 7)): | ||||
| 		lipsum = lorem.paragraph() | ||||
| 		text = m.Text.objects.create( | ||||
| 			na_web=lipsum, | ||||
| 			do_cisla=lipsum, | ||||
| 			) | ||||
| 		text.save() | ||||
| 		create_child(castnode, m.TextNode, text=text) | ||||
| 	logger.info(f"Článek vygenerován (reseni={reseni.id}, treenode={reseninode.id})") | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| @transaction.atomic | ||||
| def create_test_data(size = 6, rnd = None): | ||||
| 	logger.info('Vyrábím testovací data (size={})...'.format(size)) | ||||
|  | @ -768,6 +839,8 @@ def create_test_data(size = 6, rnd = None): | |||
| 			# TODO: mezičíslo node | ||||
| 			# TODO: přidat ke konferám řešení a dát je do čísel | ||||
| 
 | ||||
| 	# Dohackované vytvoření jednoho článku | ||||
| 	gen_clanek(rnd) | ||||
| 
 | ||||
| 
 | ||||
| 	# obecné nastavení semináře, musí být už přidané ročníky a čísla, jinak se nastaví divně | ||||
|  |  | |||
							
								
								
									
										116
									
								
								seminar/utils.py
									
									
									
									
									
								
							
							
						
						
									
										116
									
								
								seminar/utils.py
									
									
									
									
									
								
							|  | @ -2,20 +2,26 @@ | |||
| 
 | ||||
| import datetime | ||||
| from django.contrib.auth.decorators import user_passes_test | ||||
| from html.parser import HTMLParser  | ||||
| from html.parser import HTMLParser | ||||
| from django.contrib.contenttypes.models import ContentType | ||||
| from django.core.exceptions import ObjectDoesNotExist | ||||
| 
 | ||||
| import seminar.models as m | ||||
| import seminar.treelib as t | ||||
| 
 | ||||
| staff_member_required = user_passes_test(lambda u: u.is_staff) | ||||
| 
 | ||||
| 
 | ||||
| class FirstTagParser(HTMLParser): | ||||
| 	def __init__(self, *args, **kwargs): | ||||
| 		self.firstTag = None | ||||
| 		super().__init__(*args, **kwargs) | ||||
| 
 | ||||
| 	def handle_data(self, data): | ||||
| 		if self.firstTag == None: | ||||
| 			self.firstTag = data | ||||
| 	 | ||||
| 
 | ||||
| 
 | ||||
| def histogram(seznam): | ||||
| 	d = {} | ||||
| 	for i in seznam: | ||||
|  | @ -24,9 +30,10 @@ def histogram(seznam): | |||
| 		d[i] += 1 | ||||
| 	return d | ||||
| 
 | ||||
| # Pozor: zarovnáno velmi netradičně pro přehlednost | ||||
| roman_numerals = zip((1000, 900, 500, 400,  100, 90,   50,  40,   10,  9,    5,   4,    1), | ||||
|                      ('M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I')) | ||||
| 
 | ||||
| roman_numerals = zip((1000, 900,  500, 400, 100,  90, 50,  40, 10,  9,   5,  4,   1), | ||||
| 					 ('M',  'CM', 'D', 'CD','C', 'XC','L','XL','X','IX','V','IV','I')) | ||||
| 
 | ||||
| def roman(num): | ||||
| 	res = "" | ||||
|  | @ -35,6 +42,7 @@ def roman(num): | |||
| 		num %= i | ||||
| 	return res | ||||
| 
 | ||||
| 
 | ||||
| def from_roman(rom): | ||||
| 	if not rom: | ||||
| 		return 0 | ||||
|  | @ -58,9 +66,9 @@ def seznam_problemu(): | |||
| 				except: | ||||
| 					url = None | ||||
| 				if url: | ||||
| 					s += '<a href="%s">%s</a>, ' % (url, o.pk, ) | ||||
| 					s += '<a href="%s">%s</a>, ' % (url, o.pk,) | ||||
| 				else: | ||||
| 					s += '%s, ' % (o.pk, ) | ||||
| 					s += '%s, ' % (o.pk,) | ||||
| 			s = s[:-2] + ']' | ||||
| 		problemy.append(s) | ||||
| 
 | ||||
|  | @ -73,7 +81,7 @@ def seznam_problemu(): | |||
| 		jmena[j].append(r) | ||||
| 	for j in jmena: | ||||
| 		if len(jmena[j]) > 1: | ||||
| 			prb(m.Resitel, 'Duplicitní jméno "%s"' % (j, ), jmena[j]) | ||||
| 			prb(m.Resitel, 'Duplicitní jméno "%s"' % (j,), jmena[j]) | ||||
| 
 | ||||
| 	# Data maturity a narození | ||||
| 	for r in m.Resitel.objects.all(): | ||||
|  | @ -81,11 +89,99 @@ def seznam_problemu(): | |||
| 			prb(m.Resitel, 'Neznámý rok maturity', [r]) | ||||
| 		if r.rok_maturity and (r.rok_maturity < 1990 or r.rok_maturity > datetime.date.today().year + 10): | ||||
| 			prb(m.Resitel, 'Podezřelé datum maturity', [r]) | ||||
| 		if r.osoba.datum_narozeni and (r.osoba.datum_narozeni.year < 1970 or r.osoba.datum_narozeni.year > datetime.date.today().year - 12): | ||||
| 		if r.osoba.datum_narozeni and ( | ||||
| 				r.osoba.datum_narozeni.year < 1970 or r.osoba.datum_narozeni.year > datetime.date.today().year - 12): | ||||
| 			prb(m.Resitel, 'Podezřelé datum narození', [r]) | ||||
| #        if not r.email: | ||||
| #            prb(Resitel, u'Neznámý email', [r]) | ||||
| #		if not r.email: | ||||
| #			prb(Resitel, u'Neznámý email', [r]) | ||||
| 
 | ||||
| 	## Kontroly konzistence databáze a TreeNodů | ||||
| 
 | ||||
| 	# Články | ||||
| 	for clanek in m.Clanek.objects.all(): | ||||
| 		# získáme řešení svázané se článkem a z něj node ve stromě | ||||
| 		reseni = clanek.reseni_set | ||||
| 		if (reseni.count() != 1): | ||||
| 			raise ValueError("Článek k sobě má nejedno řešení!") | ||||
| 		r = reseni.first() | ||||
| 		clanek_node = r.text_cely	# vazba na ReseniNode z Reseni | ||||
| 		# content type je věc pomáhající rozeznávat různé typy objektů v django-polymorphic | ||||
| 		# protože isinstance vrátí vždy jen TreeNode | ||||
| 		# https://django-polymorphic.readthedocs.io/en/stable/migrating.html | ||||
| 		cislonode_ct = ContentType.objects.get_for_model(m.CisloNode) | ||||
| 		node = clanek_node | ||||
| 		while node is not None: | ||||
| 			node_ct = node.polymorphic_ctype | ||||
| 			if node_ct == cislonode_ct:	# dostali jsme se k CisloNode | ||||
| 				# zkontrolujeme, že stromové číslo odpovídá atributu | ||||
| 				# .cislonode je opačná vazba k treenode_ptr, abychom z TreeNode dostali | ||||
| 				# CisloNode | ||||
| 				if clanek.cislo != node.cislonode.cislo: | ||||
| 					prb(m.Clanek, "Číslo otištění uložené u článku nesedí s " | ||||
| 						"číslem otištění podle struktury treenodů.", [clanek]) | ||||
| 				break | ||||
| 			node = t.get_parent(node) | ||||
| 
 | ||||
| 	return problemy | ||||
| 
 | ||||
| 
 | ||||
| ### Generovani obalek | ||||
| def resi_v_rocniku(rocnik, cislo=None): | ||||
| 	""" Vrátí seznam řešitelů, co vyřešili nějaký problém v daném ročníku, do daného čísla. | ||||
| 	Parametry: | ||||
| 		rocnik (typu Rocnik)	ročník, ze kterého chci řešitele, co něco odevzdali | ||||
| 		cislo (typu Cislo)	číslo, do kterého včetně se počítá, že v daném | ||||
| 					ročníku řešitel něco poslal. | ||||
| 					Pokud není zadané, počítají se všechna řešení z daného ročníku. | ||||
| 	Výstup: | ||||
| 		QuerySet objektů typu Resitel """ | ||||
| 
 | ||||
| 	if cislo is None: | ||||
| 		# filtrujeme pouze podle ročníku | ||||
| 		letosni_reseni = m.Reseni.objects.filter(hodnoceni__cislo_body__rocnik=rocnik) | ||||
| 	else:  # filtrujeme podle ročníku i čísla | ||||
| 		letosni_reseni = m.Reseni.objects.filter(hodnoceni__cislo_body__rocnik=rocnik, | ||||
| 												 hodnoceni__cislo_body__poradi__lte=cislo.poradi) | ||||
| 
 | ||||
| 	# vygenerujeme queryset řešitelů, co letos něco poslali | ||||
| 	letosni_resitele = m.Resitel.objects.none() | ||||
| 	for reseni in letosni_reseni: | ||||
| 		letosni_resitele = letosni_resitele | reseni.resitele.filter(rok_maturity__gte=rocnik.druhy_rok()) | ||||
| 	return letosni_resitele.distinct() | ||||
| 
 | ||||
| 
 | ||||
| def aktivniResitele(cislo, pouze_letosni=False): | ||||
| 	""" Vrací QuerySet aktivních řešitelů, což jsou ti, co ještě neodmaturovali | ||||
| 	a letos něco poslali (anebo loni něco poslali, pokud jde o první tři čísla). | ||||
| 	Parametry: | ||||
| 		cislo (typu Cislo)	číslo, o které se jedná | ||||
| 		pouze_letosni		jen řešitelé, kteří tento rok něco poslali | ||||
| 
 | ||||
| 	""" | ||||
| 	letos = cislo.rocnik | ||||
| 
 | ||||
| 	# detekujeme, zda jde o první tři čísla či nikoli (tj. zda spamovat řešitele z minulého roku) | ||||
| 	zacatek_rocniku = True | ||||
| 	try: | ||||
| 		if int(cislo.poradi) > 3: | ||||
| 			zacatek_rocniku = False | ||||
| 	except ValueError: | ||||
| 		if cislo.poradi != '7-8': | ||||
| 			raise ValueError(f'{cislo} je neplatné číslo čísla (není int a není 7-8)') | ||||
| 		zacatek_rocniku = False | ||||
| 
 | ||||
| 	# nehledě na číslo chceme jen řešitele, kteří letos něco odevzdali | ||||
| 	if pouze_letosni: | ||||
| 		zacatek_rocniku = False | ||||
| 
 | ||||
| 	try: | ||||
| 		loni = m.Rocnik.objects.get(rocnik=letos.rocnik - 1) | ||||
| 	except ObjectDoesNotExist: | ||||
| 		# Pro první ročník neexistuje ročník předchozí | ||||
| 		zacatek_rocniku = False | ||||
| 
 | ||||
| 	if not zacatek_rocniku: | ||||
| 		return resi_v_rocniku(letos, cislo) | ||||
| 	else: | ||||
| 		# spojíme querysety s řešiteli loni a letos do daného čísla | ||||
| 		return (resi_v_rocniku(loni) | resi_v_rocniku(letos, cislo)).distinct() | ||||
|  |  | |||
|  | @ -41,6 +41,8 @@ import csv | |||
| import logging | ||||
| import time | ||||
| 
 | ||||
| from seminar.utils import aktivniResitele, resi_v_rocniku | ||||
| 
 | ||||
| 
 | ||||
| def verejna_temata(rocnik): | ||||
| 	"""Vrací queryset zveřejněných témat v daném ročníku. | ||||
|  | @ -51,7 +53,7 @@ def temata_v_rocniku(rocnik): | |||
| 	return Problem.objects.filter(typ=Problem.TYP_TEMA, rocnik=rocnik) | ||||
| 
 | ||||
| def get_problemy_k_tematu(tema): | ||||
| 	return Problemy.objects.filter(nadproblem = tema) | ||||
| 	return Problem.objects.filter(nadproblem = tema) | ||||
| 
 | ||||
| 
 | ||||
| class VlozBodyView(generic.ListView): | ||||
|  | @ -227,10 +229,29 @@ class AktualniZadaniView(TreeNodeView): | |||
| 
 | ||||
| ### Titulni strana | ||||
| 
 | ||||
| def spravne_novinky(request): | ||||
| 	""" | ||||
| 	Vrátí správný QuerySet novinek, tedy ten, který daný uživatel smí vidět. | ||||
| 	Tj. Organizátorům všechny, ostatním jen veřejné | ||||
| 	""" | ||||
| 	user = request.user | ||||
| 	# Využíváme líné vyhodnocování QuerySetů | ||||
| 	qs = Novinky.objects.all() | ||||
| 	# TODO: Tohle by mělo spíš kontrolovat, že je/není někdo org, než že může do Adminu. | ||||
| 	if not user.is_staff: | ||||
| 		qs = qs.filter(zverejneno=True) | ||||
| 	return qs.order_by('-datum') | ||||
| 
 | ||||
| 
 | ||||
| class TitulniStranaView(generic.ListView): | ||||
| 	model = Novinky | ||||
| 	template_name='seminar/titulnistrana.html' | ||||
| <<<<<<< HEAD | ||||
| 	queryset = Novinky.objects.order_by('-datum')[:2] | ||||
| ======= | ||||
| 
 | ||||
| 	def get_queryset(self): | ||||
| 		return spravne_novinky(self.request)[:5] | ||||
| >>>>>>> data_migrations | ||||
| 
 | ||||
| 	def get_context_data(self, **kwargs): | ||||
| 		context = super(TitulniStranaView, self).get_context_data(**kwargs) | ||||
|  | @ -267,9 +288,10 @@ class TitulniStranaView(generic.ListView): | |||
| 		return context | ||||
| 
 | ||||
| class StareNovinkyView(generic.ListView): | ||||
| 	model = Novinky | ||||
| 	template_name = 'seminar/stare_novinky.html' | ||||
| 	queryset = Novinky.objects.filter(zverejneno=True).order_by('-datum') | ||||
| 
 | ||||
| 	def get_queryset(self): | ||||
| 		return spravne_novinky(self.request) | ||||
| 
 | ||||
| ### Co je M&M | ||||
| 
 | ||||
|  | @ -707,7 +729,7 @@ def vysledkovka_cisla(cislo, context=None): | |||
| 	## TODO možná chytřeji vybírat aktivní řešitele | ||||
| 	# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají  | ||||
| 	# u alespoň jedné hodnoty něco jiného než NULL | ||||
| 	aktivni_resitele = list(aktivniResitele(cislo.rocnik.rocnik, cislo.poradi)) | ||||
| 	aktivni_resitele = list(aktivniResitele(cislo)) | ||||
| 
 | ||||
| 	# získáme body za číslo | ||||
| 	hlavni_problemy_slovnik, cislobody = secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy) | ||||
|  | @ -758,7 +780,7 @@ class CisloView(generic.DetailView): | |||
| 	model = Cislo | ||||
| 	template_name = 'seminar/archiv/cislo.html' | ||||
| 
 | ||||
|        # Vlastni ziskavani objektu z databaze podle (Rocnik.rocnik) | ||||
| 	# Vlastni ziskavani objektu z databaze podle (Rocnik.rocnik) | ||||
| 	def get_object(self, queryset=None): | ||||
| 		if queryset is None: | ||||
| 			queryset = self.get_queryset() | ||||
|  | @ -814,66 +836,9 @@ class RocnikVysledkovkaView(RocnikView): | |||
| 	content_type = 'text/plain; charset=UTF8' | ||||
| 	#vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani | ||||
| 
 | ||||
| ### Generovani obalek | ||||
| def resi_v_rocniku(rocnik, cislo=None): | ||||
| 	""" Vrátí seznam řešitelů, co vyřešili nějaký problém v daném ročníku, do daného čísla. | ||||
| 	Parametry: | ||||
| 		rocnik (typu Rocnik)	ročník, ze kterého chci řešitele, co něco odevzdali | ||||
| 		cislo (typu Cislo)	číslo, do kterého včetně se počítá, že v daném  | ||||
| 					ročníku řešitel něco poslal. | ||||
| 					Pokud není zadané, počítají se všechna řešení z daného ročníku. | ||||
| 	Výstup: | ||||
| 		QuerySet objektů typu Resitel """ | ||||
| 
 | ||||
| 	if cislo is None: | ||||
| 		# filtrujeme pouze podle ročníku | ||||
| 		letosni_reseni = Reseni.objects.filter(hodnoceni__cislo_body__rocnik = rocnik) | ||||
| 	else: # filtrujeme podle ročníku i čísla | ||||
| 		letosni_reseni = Reseni.objects.filter(hodnoceni__cislo_body__rocnik = rocnik, | ||||
| 					hodnoceni__cislo_body__poradi__lte=cislo.poradi) | ||||
| 
 | ||||
| 	# vygenerujeme queryset řešitelů, co letos něco poslali | ||||
| 	letosni_resitele = Resitel.objects.none() | ||||
| 	for reseni in letosni_reseni: | ||||
| 		letosni_resitele = letosni_resitele | reseni.resitele.filter(rok_maturity__gte=rocnik.druhy_rok()) | ||||
| 	return letosni_resitele.distinct() | ||||
| 	 | ||||
| 		 | ||||
| def aktivniResitele(rocnik, cislo, pouze_realni=False): | ||||
| 	""" Vrací QuerySet aktivních řešitelů, což jsou ti, co ještě neodmaturovali | ||||
| 	a letos něco poslali (anebo loni něco poslali, pokud jde o první tři čísla). | ||||
| 	Parametry: | ||||
| 		rocnik (typu int)	číslo ročníku, o který se jedná | ||||
| 		cislo (typu int)	pořadí čísla, o které se jedná | ||||
| 		pouze_realni		jen řešitelé, kteří tento rok něco poslali | ||||
| 
 | ||||
| 	""" | ||||
| 	letos = Rocnik.objects.get(rocnik = rocnik) | ||||
| 	#TODO: co se stane, když zadané kombinace neexistují? ošetřit | ||||
| 	aktualni_cislo = Cislo.objects.get(rocnik = rocnik, poradi = cislo) | ||||
| 	loni = Rocnik.objects.get(rocnik = rocnik - 1) | ||||
| 
 | ||||
|         # detekujeme, zda jde o první tři čísla či nikoli | ||||
| 	zacatek_rocniku = True | ||||
| 	try: | ||||
| 		if int(aktualni_cislo.poradi) > 3: | ||||
| 			zacatek_rocniku = False | ||||
| 	except ValueError: | ||||
|        		# pravděpodobně se jedná o číslo 7-8 | ||||
| 		zacatek_rocniku = False | ||||
| 	 | ||||
| 	# nehledě na číslo chceme jen řešitele, kteří letos něco odevzdali | ||||
| 	if pouze_realni: | ||||
| 		zacatek_rocniku = False | ||||
| 
 | ||||
| 	if not zacatek_rocniku: | ||||
| 		return resi_v_rocniku(letos)	 | ||||
| 	else: | ||||
| 		# spojíme querysety s řešiteli loni a letos do daného čísla | ||||
| 		return (resi_v_rocniku(loni) | resi_v_rocniku(letos, aktualni_cislo)).distinct() | ||||
| 
 | ||||
| def cisloObalkyView(request, rocnik, cislo): | ||||
| 	return obalkyView(request, aktivniResitele(rocnik, cislo)) | ||||
| 	realne_cislo = Cislo.objects.get(poradi=cislo, rocnik__rocnik=rocnik) | ||||
| 	return obalkyView(request, aktivniResitele(realne_cislo)) | ||||
| 
 | ||||
| 
 | ||||
| def obalkyView(request, resitele): | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Kateřina Č
						Kateřina Č