Merge pull request 'Rozstřílení seminářové aplikace' (!60) from split into master
Reviewed-on: #60
|  | @ -8,7 +8,7 @@ from django.utils.encoding import force_str | ||||||
| from .utils import default_ovvpfile | from .utils import default_ovvpfile | ||||||
| from seminar.models import Rocnik, Soustredeni | from seminar.models import Rocnik, Soustredeni | ||||||
| from vysledkovky import utils | from vysledkovky import utils | ||||||
| from seminar.utils import aktivniResitele | from tvorba.utils import aktivniResitele | ||||||
| 
 | 
 | ||||||
| class ExportIndexView(generic.View): | class ExportIndexView(generic.View): | ||||||
| 	def get(self, request): | 	def get(self, request): | ||||||
|  |  | ||||||
|  | @ -1,8 +1,7 @@ | ||||||
| from django.test import TestCase, tag | from django.test import TestCase, tag | ||||||
| from django.urls import reverse | from django.urls import reverse | ||||||
| import seminar.models as m | import seminar.models as m | ||||||
| import seminar.views as v | from personalni.utils import sync_skoly | ||||||
| from seminar.utils import sync_skoly |  | ||||||
| 
 | 
 | ||||||
| @tag('stejny-model-na-produkci') | @tag('stejny-model-na-produkci') | ||||||
| class OrgSkolyAutocompleteTestCase(TestCase): | class OrgSkolyAutocompleteTestCase(TestCase): | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| from django.urls import path | from django.urls import path | ||||||
| from . import views | from . import views | ||||||
| from seminar.utils import org_required | from personalni.utils import org_required | ||||||
| 
 | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
| 	# Export škol | 	# Export škol | ||||||
|  |  | ||||||
|  | @ -116,7 +116,7 @@ Aktuálně: Jakýsi coding style zhruba existuje, není popsaný, šíří se li | ||||||
| - Nesmí být striktně vynucovaný | - Nesmí být striktně vynucovaný | ||||||
| - Musel by být hodně nastavitelný | - Musel by být hodně nastavitelný | ||||||
|     - Nechceme mít kód plný `#NOQA: WTF42` |     - Nechceme mít kód plný `#NOQA: WTF42` | ||||||
| - Nejspíš vždycky bude mít false positives (`seminar.utils.roman_numerals`) i false negatives (`seminar.models.tvorba.Cislo.posli_cislo_mailem`) | - Nejspíš vždycky bude mít false positives (`tvorba.utils.roman_numerals`) i false negatives (`seminar.models.tvorba.Cislo.posli_cislo_mailem`) | ||||||
|     - Možná dobrý sluha, ale určitě špatný pán (also: špatná zkušenost ☺) |     - Možná dobrý sluha, ale určitě špatný pán (also: špatná zkušenost ☺) | ||||||
| - __Důsledek:__ Hrozí, že těch falešných varování bude moc, čímž to ztratí smysl úplně | - __Důsledek:__ Hrozí, že těch falešných varování bude moc, čímž to ztratí smysl úplně | ||||||
|     - Potenciálně by šlo aplikovat jen lokálně na změny? |     - Potenciálně by šlo aplikovat jen lokálně na změny? | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| from django.urls import path | from django.urls import path | ||||||
| from seminar.utils import org_required | from personalni.utils import org_required | ||||||
| from . import views | from . import views | ||||||
| 
 | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| from django.urls import path | from django.urls import path | ||||||
| from seminar.utils import org_required | from personalni.utils import org_required | ||||||
| from . import views | from . import views | ||||||
| 
 | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
|  |  | ||||||
|  | @ -54,7 +54,7 @@ SESSION_EXPIRE_AT_BROWSER_CLOSE = True | ||||||
| DOBA_ODHLASENI_PRI_ZASKRTNUTI_NEODHLASOVAT = 365 * 24 * 3600  # rok | DOBA_ODHLASENI_PRI_ZASKRTNUTI_NEODHLASOVAT = 365 * 24 * 3600  # rok | ||||||
| 
 | 
 | ||||||
| # View pro chybu s CSRF tokenem (např. se sušenkami) | # View pro chybu s CSRF tokenem (např. se sušenkami) | ||||||
| CSRF_FAILURE_VIEW = 'various.views.csrf_error' | CSRF_FAILURE_VIEW = 'various.views.csrf.csrf_error' | ||||||
| 
 | 
 | ||||||
| # Modules configuration | # Modules configuration | ||||||
| 
 | 
 | ||||||
|  | @ -131,6 +131,7 @@ INSTALLED_APPS = ( | ||||||
| 	# MaMweb | 	# MaMweb | ||||||
| 	'mamweb', | 	'mamweb', | ||||||
| 	'seminar', | 	'seminar', | ||||||
|  | 	'tvorba', | ||||||
| 	'galerie', | 	'galerie', | ||||||
| 	'korektury', | 	'korektury', | ||||||
| 	'prednasky', | 	'prednasky', | ||||||
|  |  | ||||||
|  | @ -17,8 +17,8 @@ urlpatterns = [ | ||||||
| 	path('admin/', admin.site.urls),  # NOQA | 	path('admin/', admin.site.urls),  # NOQA | ||||||
| 	path('ckeditor/', include('ckeditor_uploader.urls')), | 	path('ckeditor/', include('ckeditor_uploader.urls')), | ||||||
| 
 | 
 | ||||||
| 	# Seminarova aplikace (ma vlastni podadresare) | 	# Tvorba = ročníky, čísla, problémy atd. (ma vlastni podadresare) | ||||||
| 	path('', include('seminar.urls')), | 	path('', include('tvorba.urls')), | ||||||
| 
 | 
 | ||||||
| 	# Odevzdavatko (ma vlastni podadresare) | 	# Odevzdavatko (ma vlastni podadresare) | ||||||
| 	path('', include('odevzdavatko.urls')), | 	path('', include('odevzdavatko.urls')), | ||||||
|  | @ -39,6 +39,9 @@ urlpatterns = [ | ||||||
| 	# Autentizační aplikace (ma vlastni podadresare) | 	# Autentizační aplikace (ma vlastni podadresare) | ||||||
| 	path('', include('various.autentizace.urls')), | 	path('', include('various.autentizace.urls')), | ||||||
| 
 | 
 | ||||||
|  | 	# Novinková aplikace (ma vlastni podadresare) | ||||||
|  | 	path('', include('novinky.urls')), | ||||||
|  | 
 | ||||||
| 	# Api (ma vlastni podadresare) (autocomplete apod.) | 	# Api (ma vlastni podadresare) (autocomplete apod.) | ||||||
| 	path('', include('api.urls')), | 	path('', include('api.urls')), | ||||||
| 
 | 
 | ||||||
|  | @ -48,6 +51,9 @@ urlpatterns = [ | ||||||
| 	# Aesop (ma vlastni podadresare) | 	# Aesop (ma vlastni podadresare) | ||||||
| 	path('', include('aesop.urls')), | 	path('', include('aesop.urls')), | ||||||
| 
 | 
 | ||||||
|  | 	# Various = co se nevešlo jinam | ||||||
|  | 	path('', include('various.urls')), | ||||||
|  | 
 | ||||||
| 	# REST API | 	# REST API | ||||||
| #	path('api/', include(router.urls)), | #	path('api/', include(router.urls)), | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | """ | ||||||
|  | Obsahuje vše okolo novinek (zpráv „Co je nového?“ na titulní straně). | ||||||
|  | """ | ||||||
|  | @ -8,6 +8,6 @@ | ||||||
|     {% endblock %} |     {% endblock %} | ||||||
|   </h1> |   </h1> | ||||||
| 
 | 
 | ||||||
|   {% include 'seminar/novinky.html' %} |   {% include 'novinky/novinky.html' %} | ||||||
| 
 | 
 | ||||||
| {% endblock %} | {% endblock %} | ||||||
							
								
								
									
										27
									
								
								novinky/testutils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,27 @@ | ||||||
|  | import logging | ||||||
|  | 
 | ||||||
|  | from .models import Novinky | ||||||
|  | 
 | ||||||
|  | logger = logging.getLogger(__name__) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def gen_novinky(rnd, organizatori): | ||||||
|  | 	logger.info('Generuji novinky...') | ||||||
|  | 
 | ||||||
|  | 	jake = ["zábavné", "veselé", "dobrodružné", "skvělé"] | ||||||
|  | 	co = ["soustředění", "Fyziklání", "víkendové setkání"] | ||||||
|  | 	kde = ["na Šumavě", "v Praze", "u Plzně", "na Marsu"] | ||||||
|  | 	kdy = ["Zítra bude", "10. 10. 2020 bude", "V prosinci bude", "V létě bude"] | ||||||
|  | 
 | ||||||
|  | 	for i in range(5): | ||||||
|  | 		text_novinky = " ".join([ | ||||||
|  | 			rnd.choice(kdy), rnd.choice(kde), | ||||||
|  | 			rnd.choice(jake), rnd.choice(co), | ||||||
|  | 		]) | ||||||
|  | 		novinka = Novinky.objects.create( | ||||||
|  | 			id=i, autor=rnd.choice(organizatori), | ||||||
|  | 			text=(text_novinky+", těšíme se na vás!"), | ||||||
|  | 			zverejneno=rnd.choice([True, False]), | ||||||
|  | 		) | ||||||
|  | 		novinka.save() | ||||||
|  | 	return | ||||||
							
								
								
									
										7
									
								
								novinky/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,7 @@ | ||||||
|  | from django.urls import path | ||||||
|  | 
 | ||||||
|  | from .views import StareNovinkyView | ||||||
|  | 
 | ||||||
|  | urlpatterns = [ | ||||||
|  | 	path('stare-novinky/', StareNovinkyView.as_view(), name='stare_novinky'), | ||||||
|  | ] | ||||||
|  | @ -0,0 +1,23 @@ | ||||||
|  | from django.views import generic | ||||||
|  | 
 | ||||||
|  | from .models import Novinky | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 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() | ||||||
|  | 	if not user.je_org: | ||||||
|  | 		qs = qs.filter(zverejneno=True) | ||||||
|  | 	return qs.order_by('-datum') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class StareNovinkyView(generic.ListView): | ||||||
|  | 	template_name = 'novinky/stare_novinky.html' | ||||||
|  | 
 | ||||||
|  | 	def get_queryset(self): | ||||||
|  | 		return spravne_novinky(self.request) | ||||||
|  | @ -1,6 +1,5 @@ | ||||||
| {% extends "base.html" %} | {% extends "base.html" %} | ||||||
| 
 | 
 | ||||||
| {% load utils %} {# Možná by mohlo být někde výš v hierarchii templatů... #} |  | ||||||
| {% load barvy_reseni %} | {% load barvy_reseni %} | ||||||
| 
 | 
 | ||||||
| {% block content %} | {% block content %} | ||||||
|  |  | ||||||
							
								
								
									
										40
									
								
								odevzdavatko/testutils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,40 @@ | ||||||
|  | import datetime | ||||||
|  | import random | ||||||
|  | 
 | ||||||
|  | from seminar.models.odevzdavatko import Reseni, Hodnoceni | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def gen_reseni_ulohy(rnd, cisla, uloha, pocet_resitelu, poradi_cisla, resitele_cisla, resitele): | ||||||
|  | 	pocet_reseni = rnd.randint(pocet_resitelu//4, pocet_resitelu * 4) | ||||||
|  | 	# generujeme náhodný počet řešení vzhledem k počtu řešitelů čísla | ||||||
|  | 	for _ in range(pocet_reseni): | ||||||
|  | 		#print("Generuji {}-té řešení".format(reseni)) | ||||||
|  | 		if rnd.randint(1, 10) == 1: | ||||||
|  | 			# cca desetina řešení od více řešitelů | ||||||
|  | 			res_vyber = rnd.sample(resitele_cisla, rnd.randint(2, 5)) | ||||||
|  | 		else: | ||||||
|  | 			res_vyber = rnd.sample(resitele_cisla, 1) | ||||||
|  | 		if resitele[0] in res_vyber: # speciální řešitel, který nemá žádné body | ||||||
|  | 			res_vyber.remove(resitele[0]) | ||||||
|  | 
 | ||||||
|  | 		# Vytvoření řešení. | ||||||
|  | 		if uloha.cislo_zadani.zlomovy_deadline_pro_papirove_cislo() is not None: | ||||||
|  | 			# combine, abychom dostali plný čas a ne jen datum | ||||||
|  | 			cas_doruceni = uloha.cislo_zadani.deadline_v_cisle.first().deadline - datetime.timedelta(days=random.randint(0, 40)) - datetime.timedelta(minutes=random.randint(0, 60*24)) | ||||||
|  | 			# astimezone, protože jinak vyhazuje warning o nenastavené TZ | ||||||
|  | 			res = Reseni.objects.create(forma=rnd.choice(Reseni.FORMA_CHOICES)[0], cas_doruceni=cas_doruceni.astimezone(datetime.timezone.utc)) | ||||||
|  | 		else: | ||||||
|  | 			res = Reseni.objects.create(forma=rnd.choice(Reseni.FORMA_CHOICES)[0]) | ||||||
|  | 		# Problém a řešitele přiřadíme později, ManyToManyField | ||||||
|  | 		# se nedá vyplnit v create(). | ||||||
|  | 		res.resitele.set(res_vyber) | ||||||
|  | 		res.save() | ||||||
|  | 
 | ||||||
|  | 		# Vytvoření hodnocení. | ||||||
|  | 		hod = Hodnoceni.objects.create( | ||||||
|  | 			body=rnd.randint(0, uloha.max_body), | ||||||
|  | 			cislo_body=cisla[poradi_cisla - 1], | ||||||
|  | 			reseni=res, | ||||||
|  | 			problem=uloha | ||||||
|  | 		) | ||||||
|  | 	return | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| from django.urls import path | from django.urls import path | ||||||
| 
 | 
 | ||||||
| from seminar.utils import org_required, resitel_required, viewMethodSwitch, \ | from personalni.utils import org_required, resitel_required, resitel_or_org_required | ||||||
| 	resitel_or_org_required | from various.views.generic import viewMethodSwitch | ||||||
| from . import views | from . import views | ||||||
| 
 | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
|  |  | ||||||
							
								
								
									
										11
									
								
								odevzdavatko/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,11 @@ | ||||||
|  | import decimal | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def vzorecek_na_prepocet(body, resitelu): | ||||||
|  | 	""" Vzoreček na přepočet plných bodů na parciálni, když má řešení více řešitelů. """ | ||||||
|  | 	return body * 3 / (resitelu + 2) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def inverze_vzorecku_na_prepocet(body: decimal.Decimal, resitelu) -> decimal.Decimal: | ||||||
|  | 	""" Vzoreček na přepočet parciálních bodů na plné, když má řešení více řešitelů. """ | ||||||
|  | 	return round(body * (resitelu + 2) / 3, 1) | ||||||
|  | @ -20,8 +20,8 @@ import logging | ||||||
| import seminar.models as m | import seminar.models as m | ||||||
| from . import forms as f | from . import forms as f | ||||||
| from .forms import OdevzdavatkoTabulkaFiltrForm as FiltrForm | from .forms import OdevzdavatkoTabulkaFiltrForm as FiltrForm | ||||||
| from seminar.utils import resi_v_rocniku | from tvorba.utils import resi_v_rocniku | ||||||
| from seminar.views import formularOKView | from various.views.pomocne import formularOKView | ||||||
| 
 | 
 | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
| Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB | 
|  | @ -46,7 +46,7 @@ | ||||||
|     {% if org.osoba.foto %} |     {% if org.osoba.foto %} | ||||||
|       <a href="{{org.osoba.foto.url}}"><img src="{{org.osoba.foto_male.url}}" height="{{org.osoba.foto_male.height}}" alt="{{org.osoba.jmeno}} {{org.osoba.prijmeni}}"></a> |       <a href="{{org.osoba.foto.url}}"><img src="{{org.osoba.foto_male.url}}" height="{{org.osoba.foto_male.height}}" alt="{{org.osoba.jmeno}} {{org.osoba.prijmeni}}"></a> | ||||||
|     {% else %} {# pokud osoba nemá fotku, zobrazuje se defaultní obrázek #} |     {% else %} {# pokud osoba nemá fotku, zobrazuje se defaultní obrázek #} | ||||||
|       {% load static %} <img src="{% static 'images/no-photo.png' %}" height=200px alt="{{org.osoba.jmeno}} {{org.osoba.prijmeni}}"> |       {% load static %} <img src="{% static 'personalni/no-photo.png' %}" height=200px alt="{{org.osoba.jmeno}} {{org.osoba.prijmeni}}"> | ||||||
|     {% endif %} |     {% endif %} | ||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
							
								
								
									
										235
									
								
								personalni/testutils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,235 @@ | ||||||
|  | import datetime | ||||||
|  | import logging | ||||||
|  | import unidecode | ||||||
|  | 
 | ||||||
|  | from django.contrib.auth.models import Permission | ||||||
|  | from django.contrib.auth.models import Group | ||||||
|  | import django.contrib.auth | ||||||
|  | 
 | ||||||
|  | from .models import Osoba, Skola, Organizator, Resitel, Prijemce | ||||||
|  | 
 | ||||||
|  | logger = logging.getLogger(__name__) | ||||||
|  | 
 | ||||||
|  | User = django.contrib.auth.get_user_model() | ||||||
|  | 
 | ||||||
|  | zlinska = None  # tohle bude speciální škola, které později dodáme kontaktní osobu | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # 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): | ||||||
|  | 	logger.info('Generuji osoby (size={})...'.format(size)) | ||||||
|  | 
 | ||||||
|  | 	jmena_m = ['Aleš', 'Tomáš', 'Martin', 'Jakub', 'Petr', 'Lukáš', 'Cyril', 'Pavel Karel'] | ||||||
|  | 	jmena_f = ['Eva', 'Karolína', 'Zuzana', 'Sylvie', 'Iva', 'Jana', 'Marie', 'Marta Iva', 'Shu Shan'] | ||||||
|  | 	prijmeni_m = ['Novotný', 'Svoboda', 'Pecha', 'Kořen', 'Holan', 'Uhlíř', 'Chytráček', 'Pokora', 'Koch', 'Szegedy', 'Rudý', "von Neumann", "d'Este"] | ||||||
|  | 	prijmeni_f = ['Novotná', 'Svobodová', 'Machová', 'Zelená', 'Yu-Xin', 'Mlsná', 'Dubná', 'Mrkvová', 'Suchá', 'Lovelace', 'Holcová', 'Rui', "Nováčková Tydlitátová"] | ||||||
|  | 	prezdivky = ['Kaki', 'Hurdur', 'Maracuja', 'Bobbo', "", "", "", "", "", "", "", 'Riki', 'Sapa', "", '', '---', 'Koko'] | ||||||
|  | 	domain = ['example.com', 'dolujeme.eu', 'mff.cuni.cz', 'strcprstskrzkrk.cz', 'british.co.uk', 'splachni.to', 'haha.org'] | ||||||
|  | 	seznam_ulic = ['Krátká', 'Vlhká', 'Jungmanova', '17. listopadu', '4. října', 'Roztocká', 'Forstova', 'Generála Františka Janouška', 'Náměstí Války', 'Svratecké náměstí', 'Zelená lhota', 'Z Plynu', 'K Jezeru', 'U Kocourkova', 'Uštěpačná', 'Ostrorepská', 'Zubří'] | ||||||
|  | 	seznam_mest = ['Praha', 'Brno', 'Ostrava', 'Horní Jelení', 'Dolní Zábrdovice', 'Prdelkov', 'Stará myslivna', 'Kocourkov', 'Šalingrad', 'Medvědí hora', 'Basilej', 'Unterschiedlich', 'Old York', 'Lancastershire', 'Vóloďháza'] | ||||||
|  | 
 | ||||||
|  | 	osoby = [] | ||||||
|  | 	# 30 je náhodná konstanta, size je použité na víc místech a | ||||||
|  | 	# říká, jak velká asi chceme testovací data | ||||||
|  | 	for i in range(30 * size): | ||||||
|  | 		pohlavi_idx = rnd.randint(0, 2)  # 2 = nebinární | ||||||
|  | 		osloveni = [Osoba.OSLOVENI_MUZSKE, Osoba.OSLOVENI_ZENSKE, Osoba.OSLOVENI_ZADNE][pohlavi_idx] | ||||||
|  | 		jmeno = rnd.choice([jmena_m, jmena_f, jmena_m + jmena_f][pohlavi_idx]) | ||||||
|  | 		prijmeni = rnd.choice([prijmeni_m, prijmeni_f, prijmeni_m + prijmeni_f][pohlavi_idx]) | ||||||
|  | 		if pohlavi_idx == 2: logger.debug(f'Testdata: nebinární osoba: {jmeno} {prijmeni}.') | ||||||
|  | 		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, jmena_m + jmena_f][pohlavi_idx]) | ||||||
|  | 			prijmeni = rnd.choice([prijmeni_m, prijmeni_f, prijmeni_m + prijmeni_f][pohlavi_idx]) | ||||||
|  | 			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) | ||||||
|  | 		email = "@".join([unidecode.unidecode(jmeno), rnd.choice(domain)]) | ||||||
|  | 		telefon = "".join([str(rnd.choice([k for k in range(10)])) for _ in range(9)]) | ||||||
|  | 		narozeni = datetime.date( | ||||||
|  | 			rnd.randint(1980, datetime.datetime.now().year), | ||||||
|  | 			rnd.randint(1, 12), | ||||||
|  | 			rnd.randint(1, 28) | ||||||
|  | 		) | ||||||
|  | 		ulic = rnd.choice(seznam_ulic) | ||||||
|  | 		cp = rnd.randint(1, 99) | ||||||
|  | 		ulice = " ".join([ulic, str(cp)]) | ||||||
|  | 		mesto = rnd.choice(seznam_mest) | ||||||
|  | 		psc = "".join([str(rnd.choice([k for k in range(10)])) for _ in range(5)]) | ||||||
|  | 
 | ||||||
|  | 		osoby.append(Osoba.objects.create( | ||||||
|  | 			jmeno=jmeno, prijmeni=prijmeni, | ||||||
|  | 			prezdivka=prezdivka, osloveni=osloveni, | ||||||
|  | 			email=email, telefon=telefon, | ||||||
|  | 			datum_narozeni=narozeni, | ||||||
|  | 			ulice=ulice, mesto=mesto, psc=psc, | ||||||
|  | 			datum_registrace=datetime.date( | ||||||
|  | 				rnd.randint(2019, 2029), rnd.randint(1, 12), rnd.randint(1, 28) | ||||||
|  | 			) | ||||||
|  | 		)) | ||||||
|  | 
 | ||||||
|  | 	# TODO pridat foto male a velke. Jak? | ||||||
|  | 	# Pavel tvrdí, že to necháme a přidáme až do adminu | ||||||
|  | 
 | ||||||
|  | 	return osoby | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def gen_skoly():  # TODO někdy to přepsat, aby jich bylo více | ||||||
|  | 	logger.info('Generuji školy...') | ||||||
|  | 
 | ||||||
|  | 	skoly = [] | ||||||
|  | 	prvnizs = Skola.objects.create( | ||||||
|  | 		mesto='Praha', stat='CZ', psc='101 00', | ||||||
|  | 		ulice='Krátká 5', nazev='První ZŠ', je_zs=True, je_ss=False, | ||||||
|  | 	) | ||||||
|  | 	skoly.append(prvnizs) | ||||||
|  | 	skoly.append(Skola.objects.create( | ||||||
|  | 		mesto='Praha', stat='CZ', psc='101 00', | ||||||
|  | 		ulice='Krátká 5', nazev='První SŠ', je_zs=False, je_ss=True, | ||||||
|  | 	)) | ||||||
|  | 	skoly.append(Skola.objects.create( | ||||||
|  | 		mesto='Praha', stat='CZ', psc='102 00', | ||||||
|  | 		ulice='Dlouhá 5', nazev='Druhá SŠ', je_zs=False, je_ss=True, | ||||||
|  | 	)) | ||||||
|  | 	skoly.append(Skola.objects.create( | ||||||
|  | 		mesto='Praha', stat='CZ', psc='103 00', | ||||||
|  | 		ulice='Široká 3', nazev='Třetí SŠ a ZŠ', je_zs=True, je_ss=True, | ||||||
|  | 	)) | ||||||
|  | 	skoly.append(Skola.objects.create( | ||||||
|  | 		mesto='Ostrava', stat='CZ', psc='700 00', | ||||||
|  | 		ulice='Hluboká 42', nazev='Hutní gympl', je_zs=False, je_ss=True, | ||||||
|  | 	)) | ||||||
|  | 	skoly.append(Skola.objects.create( | ||||||
|  | 		mesto='Humenné', stat='SK', psc='012 34', | ||||||
|  | 		ulice='Pltká 1', nazev='Sredná škuola', je_zs=False, je_ss=True, | ||||||
|  | 	)) | ||||||
|  | 	global zlinska | ||||||
|  | 	zlinska = Skola.objects.create( | ||||||
|  | 		mesto='Zlín', stat='CZ', psc='76001', | ||||||
|  | 		ulice='náměstí T.G. Masaryka 2734-9', | ||||||
|  | 		nazev='Gymnázium a Střední jazyková škola s právem SJZ', | ||||||
|  | 		kratky_nazev="GaSJŠspSJZ", je_zs=True, je_ss=True, | ||||||
|  | 	) | ||||||
|  | 	skoly.append(zlinska) | ||||||
|  | 	return skoly | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def gen_resitele(rnd, osoby, skoly): | ||||||
|  | 	logger.info('Generuji řešitele...') | ||||||
|  | 
 | ||||||
|  | 	resitele = [] | ||||||
|  | 	x = 0 | ||||||
|  | 	resitel_perm = Permission.objects.filter(codename__exact='resitel').first() | ||||||
|  | 	resitel_group = Group.objects.filter(name__exact='resitel').first() | ||||||
|  | 	for os in osoby: | ||||||
|  | 		rand = rnd.randint(0, 8) | ||||||
|  | 		if not (rand % 8 == 0): | ||||||
|  | 			if not os.user: | ||||||
|  | 				if x: | ||||||
|  | 					user = User.objects.create_user( | ||||||
|  | 						username='r'+str(x), email=os.email, password='r', | ||||||
|  | 					) | ||||||
|  | 				else: | ||||||
|  | 					user = User.objects.create_user( | ||||||
|  | 						username='r', email=os.email, password='r', | ||||||
|  | 					) | ||||||
|  | 				x += 1 | ||||||
|  | 				os.user = user | ||||||
|  | 				os.save() | ||||||
|  | 				os.user.user_permissions.add(resitel_perm) | ||||||
|  | 				os.user.groups.add(resitel_group) | ||||||
|  | 			resitele.append(Resitel.objects.create( | ||||||
|  | 				osoba=os, skola=rnd.choice(skoly), | ||||||
|  | 				rok_maturity=os.datum_narozeni.year + rnd.randint(18, 21), | ||||||
|  | 				zasilat=rnd.choice(Resitel.ZASILAT_CHOICES)[0], | ||||||
|  | 			)) | ||||||
|  | 	return resitele | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def gen_prijemci(rnd, osoby, kolik=10): | ||||||
|  | 	logger.info('Generuji příjemce (kolik={})...'.format(kolik)) | ||||||
|  | 	prijemci = [] | ||||||
|  | 	for i in rnd.sample(osoby, kolik): | ||||||
|  | 		prijemci.append(Prijemce.objects.create(osoba=i)) | ||||||
|  | 
 | ||||||
|  | 	global zlinska | ||||||
|  | 	if zlinska is not None: | ||||||
|  | 		zlinska.kontaktni_osoba=rnd.choice(osoby) | ||||||
|  | 		zlinska.save() | ||||||
|  | 
 | ||||||
|  | 	return prijemci | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def gen_organizatori(rnd, osoby, last_rocnik): | ||||||
|  | 	logger.info('Generuji organizátory...') | ||||||
|  | 	organizatori = [] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	seznam_konicku = ["vařím", "jezdím na kole", "řeším diferenciální rovnice", "koukám z okna", "tancuji", "programuji", "jezdím vlakem", "nedělám nic"] | ||||||
|  | 	seznam_oboru = ["matematiku", "matematiku", "matematiku", "fyziku", "literaturu", "informatiku", "informatiku", "běhání dokolečka"] | ||||||
|  | 
 | ||||||
|  | 	x = 0 | ||||||
|  | 	org_perm = Permission.objects.filter(codename__exact='org').first() | ||||||
|  | 	org_group = Group.objects.filter(name__exact='org').first() | ||||||
|  | 	for os in osoby: | ||||||
|  | 		rand = rnd.randint(0, 8) | ||||||
|  | 		if rand % 8 == 0: | ||||||
|  | 			pusobnost = rnd.randint(1, last_rocnik) | ||||||
|  | 			od = datetime.datetime( | ||||||
|  | 				year=1993 + pusobnost, | ||||||
|  | 				month=rnd.randint(1, 12), | ||||||
|  | 				day=rnd.randint(1, 28), | ||||||
|  | 				tzinfo=datetime.timezone.utc, | ||||||
|  | 			) | ||||||
|  | 			do = datetime.datetime( | ||||||
|  | 				year=od.year + rnd.randint(1, 6), | ||||||
|  | 				month=rnd.randint(1, 12), | ||||||
|  | 				day=rnd.randint(1, 28), | ||||||
|  | 				tzinfo=datetime.timezone.utc, | ||||||
|  | 			) | ||||||
|  | 			# aktualni organizatori jeste nemaji vyplnene organizuje_do | ||||||
|  | 
 | ||||||
|  | 			# popis orga | ||||||
|  | 			konicek1 = rnd.choice(seznam_konicku) | ||||||
|  | 			konicek2 = rnd.choice(seznam_konicku) | ||||||
|  | 			obor = rnd.choice(seznam_oboru) | ||||||
|  | 			popis_orga = "Ve volném čase " + konicek1 + " a také " + konicek2 + ". Studuji " + obor + " a moc mě to baví." | ||||||
|  | 
 | ||||||
|  | 			if do.year > datetime.datetime.now().year: | ||||||
|  | 				do = None | ||||||
|  | 			if not os.user: | ||||||
|  | 				if x: | ||||||
|  | 					user = User.objects.create_user( | ||||||
|  | 						username='o'+str(x), email=os.email, password='o', | ||||||
|  | 					) | ||||||
|  | 				else: | ||||||
|  | 					user = User.objects.create_user( | ||||||
|  | 						username='o', email=os.email, password='o', | ||||||
|  | 					) | ||||||
|  | 				x += 1 | ||||||
|  | 				os.user = user | ||||||
|  | 				os.save() | ||||||
|  | 				os.user.user_permissions.add(org_perm) | ||||||
|  | 				os.user.groups.add(org_group) | ||||||
|  | 				os.user.is_staff = True | ||||||
|  | 				os.user.save() | ||||||
|  | 			organizatori.append(Organizator.objects.create( | ||||||
|  | 				osoba=os, | ||||||
|  | 				organizuje_od=od, organizuje_do=do, | ||||||
|  | 				strucny_popis_organizatora=popis_orga, | ||||||
|  | 			)) | ||||||
|  | 	return organizatori | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| from django.urls import path | from django.urls import path | ||||||
| from django.contrib.auth.decorators import login_required | from django.contrib.auth.decorators import login_required | ||||||
| from . import views | from . import views | ||||||
| from seminar.utils import org_required | from personalni.utils import org_required | ||||||
| 
 | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
| 	path( | 	path( | ||||||
|  | @ -21,4 +21,16 @@ urlpatterns = [ | ||||||
| 	# Obecný view na profil -- orgům dá rozcestník, řešitelům jejich stránku | 	# Obecný view na profil -- orgům dá rozcestník, řešitelům jejich stránku | ||||||
| 	path('profil/', views.profilView, name='profil'), | 	path('profil/', views.profilView, name='profil'), | ||||||
| 
 | 
 | ||||||
|  | 	# Seznam organizátorů | ||||||
|  | 	path( | ||||||
|  | 		'o-nas/organizatori/', | ||||||
|  | 		views.CojemamOrganizatoriView.as_view(), | ||||||
|  | 		name='organizatori' | ||||||
|  | 	), | ||||||
|  | 	path( | ||||||
|  | 		'o-nas/organizatori/organizovali/', | ||||||
|  | 		views.CojemamOrganizatoriStariView.as_view(), | ||||||
|  | 		name='stari_organizatori' | ||||||
|  | 	), | ||||||
|  | 
 | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | @ -2,10 +2,183 @@ import seminar.models as m | ||||||
| from various.utils import bez_diakritiky_translate | from various.utils import bez_diakritiky_translate | ||||||
| import re | import re | ||||||
| 
 | 
 | ||||||
| def normalizuj_jmeno(o: m.Osoba) -> str: | from django.contrib.auth import get_user_model | ||||||
|  | from django.contrib.auth.decorators import permission_required, user_passes_test | ||||||
|  | from django.contrib.auth.models import AnonymousUser | ||||||
|  | from django.db import transaction | ||||||
|  | 
 | ||||||
|  | import seminar.models as m | ||||||
|  | import soustredeni.models | ||||||
|  | 
 | ||||||
|  | from .models import Osoba, Organizator, Skola, Resitel, Prijemce | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | org_required = permission_required('auth.org') | ||||||
|  | resitel_required = permission_required('auth.resitel') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # inspirováno django.contrib.auth.decorators permission_required | ||||||
|  | def check_perms(user): | ||||||
|  | 	if user.has_perms(('auth.resitel',)): | ||||||
|  | 		return True | ||||||
|  | 	if user.has_perms(('auth.org',)): | ||||||
|  | 		return True | ||||||
|  | 	return False | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | resitel_or_org_required = user_passes_test(check_perms) | ||||||
|  | 
 | ||||||
|  | User = get_user_model() | ||||||
|  | # Není to úplně hezké, ale budeme doufat, že to je funkční... | ||||||
|  | User.je_org = property(lambda self: self.has_perm('auth.org')) | ||||||
|  | User.je_resitel = property(lambda self: self.has_perm('auth.resitel')) | ||||||
|  | AnonymousUser.je_org = False | ||||||
|  | AnonymousUser.je_resitel = False | ||||||
|  | 
 | ||||||
|  | def normalizuj_jmeno(o: Osoba) -> str: | ||||||
| 	# FIXME: Možná není potřeba vázat na model? | 	# FIXME: Možná není potřeba vázat na model? | ||||||
| 	cele_jmeno = f'{o.jmeno} {o.prijmeni}' | 	cele_jmeno = f'{o.jmeno} {o.prijmeni}' | ||||||
| 	cele_jmeno = cele_jmeno.translate(bez_diakritiky_translate) | 	cele_jmeno = cele_jmeno.translate(bez_diakritiky_translate) | ||||||
| 	cele_jmeno = re.sub(r'[^a-zA-Z- ]', '', cele_jmeno) | 	cele_jmeno = re.sub(r'[^a-zA-Z- ]', '', cele_jmeno) | ||||||
| 	return cele_jmeno | 	return cele_jmeno | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | def sync_skoly(base_url): | ||||||
|  | 	"""Stáhne všechny školy z mamwebu na adrese <base_url> a uloží je do databáze""" | ||||||
|  | 	from django.urls import reverse | ||||||
|  | 	full_url = base_url.rstrip('/') + reverse('export_skoly') | ||||||
|  | 	import requests | ||||||
|  | 	from django.core import serializers | ||||||
|  | 	json =  requests.get(full_url, stream=True).content | ||||||
|  | 	for skola in serializers.deserialize('json', json): | ||||||
|  | 		skola.save() | ||||||
|  | 
 | ||||||
|  | @transaction.atomic | ||||||
|  | def merge_resitele(cilovy, zdrojovy): | ||||||
|  | 	"""Spojí dva řešitelské objekty do cílového. | ||||||
|  | 
 | ||||||
|  | 	Pojmenování "zdrojový" je silně nepřiléhající, ale co už…""" | ||||||
|  | 
 | ||||||
|  | 	# Postup: | ||||||
|  | 	# Sjednotit / upravit informace cílového řešitele | ||||||
|  | 	print('Upravuji data modelu') | ||||||
|  | 	fieldy_shoda = ['skola', 'rok_maturity', 'zasilat', 'zasilat_cislo_emailem', 'zasilat_cislo_papirove'] | ||||||
|  | 
 | ||||||
|  | 	for f in fieldy_shoda: | ||||||
|  | 		zf = getattr(zdrojovy, f) | ||||||
|  | 		cf = getattr(cilovy, f) | ||||||
|  | 		if cf == zf: | ||||||
|  | 			print(f' Údaj {f} je shodný ({zf})') | ||||||
|  | 		else: | ||||||
|  | 			if zf is None: | ||||||
|  | 				print(f' Údaj {f} je pouze v cílovém, používám') | ||||||
|  | 				continue | ||||||
|  | 			if cf is None: | ||||||
|  | 				setattr(cilovy, f, zf) | ||||||
|  | 				cilovy.poznamka += f'\nDEBUG: Merge: doplnéný údaj {f} ze zdrojového: {zf}' | ||||||
|  | 				print(f" Přiřazuji {f} ze zdrojového: {zf}") | ||||||
|  | 				continue | ||||||
|  | 			# Jsou fakt různé… | ||||||
|  | 			# FIXME: chybí možnost na vlastní úpravu… | ||||||
|  | 			verdikt = input(f"\n\n Údaj {f} se u řešitele {cilovy} ({cilovy.id}) liší:\n  Zdrojový: {zf}\n  Cílový: {cf}\n Který použít, [z]drojový, [c]ílový? ") | ||||||
|  | 			verdikt = verdikt[0].casefold() | ||||||
|  | 			if verdikt == 'z': | ||||||
|  | 				setattr(cilovy, f, zf) | ||||||
|  | 				cilovy.poznamka += f'\nDEBUG: Merge: pro {f} použit údaj {zf} (zdrojový), nepoužit {cf} (cílový)' | ||||||
|  | 			elif verdikt == 'c': | ||||||
|  | 				cilovy.poznamka += f'\nDEBUG: Merge: pro {f} použit údaj {cf} (cílový), nepoužit {zf} (zdrojový)' | ||||||
|  | 			else: raise ValueError('Špatná odpověď, řešitel pravděpodobně neuložen') | ||||||
|  | 	# poznámku chceme nezahodit… | ||||||
|  | 	cilovy.poznamka += f'\nDEBUG: Merge: Původní poznámka: {zdrojovy.poznamka}' | ||||||
|  | 	print(f' Výsledný řešitel: {cilovy.__dict__}, ukládám') | ||||||
|  | 	cilovy.save() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	# Přepojit všechny vazby ze zdrojového na cílového | ||||||
|  | 	print('Přepojuji vazby') | ||||||
|  | 	# Vazby: Škola (hotovo), Řešení_Řešitelé, Konfery_Účastníci, Soustředění_Účastníci, Osoba (vyřeší se později, nejde přepojit) | ||||||
|  | 	ct = m.Reseni_Resitele.objects.filter(resitele=zdrojovy).update(resitele=cilovy) | ||||||
|  | 	print(f' Přepojeno {ct} řešení') | ||||||
|  | 	ct = soustredeni.models.Konfery_Ucastnici.objects.filter(resitel=zdrojovy).update(resitel=cilovy) | ||||||
|  | 	print(f' Přepojeno {ct} konfer') | ||||||
|  | 	ct = soustredeni.models.Soustredeni_Ucastnici.objects.filter(resitel=zdrojovy).update(resitel=cilovy) | ||||||
|  | 	print(f' Přepojeno {ct} sousů') | ||||||
|  | 
 | ||||||
|  | 	# Teď by na zdrojovém řešiteli nemělo nic viset, smazat ho, pamatujíce si jeho Osobu | ||||||
|  | 	zdrosoba = zdrojovy.osoba | ||||||
|  | 	print(f'Mažu zdrojového řešitele {zdrojovy.__dict__}') | ||||||
|  | 	zdrojovy.delete() | ||||||
|  | 	# Spojit osoby (separátní funkce). | ||||||
|  | 	merge_osoby(cilovy.osoba, zdrosoba) | ||||||
|  | 
 | ||||||
|  | 	input("Potvrdit transakci řešitelů (^C pro zrušení) ") | ||||||
|  | 
 | ||||||
|  | @transaction.atomic | ||||||
|  | def merge_osoby(cilova, zdrojova): | ||||||
|  | 	""" Spojí dvě osoby do cílové | ||||||
|  | 
 | ||||||
|  | 	Nehlídá omezení typu "max 1 řešitel na osobu", to by měla hlídat databáze (OneToOneField).""" | ||||||
|  | 	# Sjednocení dat | ||||||
|  | 	print('Sjednocuji data osob') | ||||||
|  | 	# ID, User neřešíme, poznámku vyřešíme separátně. | ||||||
|  | 	fieldy = ['datum_narozeni', 'datum_registrace', 'datum_souhlasu_udaje', | ||||||
|  | 			  'datum_souhlasu_zasilani', 'email', 'foto', 'jmeno', 'mesto', | ||||||
|  | 			  'osloveni', 'prezdivka', 'prijmeni', 'psc', 'stat', 'telefon', 'ulice'] | ||||||
|  | 	for f in fieldy: | ||||||
|  | 		zf = getattr(zdrojova, f) | ||||||
|  | 		cf = getattr(cilova, f) | ||||||
|  | 		if cf == zf: | ||||||
|  | 			print(f' Údaj {f} je shodný ({zf})') | ||||||
|  | 		else: | ||||||
|  | 			if zf is None: | ||||||
|  | 				print(f' Údaj {f} je pouze v cílové, používám') | ||||||
|  | 				continue | ||||||
|  | 			if cf is None: | ||||||
|  | 				setattr(cilova, f, zf) | ||||||
|  | 				cilova.poznamka += f'\nDEBUG: Merge: doplnéný údaj {f} ze zdrojové: {zf}' | ||||||
|  | 				print(f" Přiřazuji {f} ze zdrojové: {zf}") | ||||||
|  | 				continue | ||||||
|  | 			# Jsou fakt různé… | ||||||
|  | 			# FIXME: chybí možnost na vlastní úpravu… | ||||||
|  | 			verdikt = input(f"\n\n Údaj {f} se u osoby {cilova} ({cilova.id}) liší:\n  Zdrojový: {zf}\n  Cílový: {cf}\n Který použít, [z]drojový, [c]ílový? ") | ||||||
|  | 			verdikt = verdikt[0].casefold() | ||||||
|  | 			if verdikt == 'z': | ||||||
|  | 				setattr(cilova, f, zf) | ||||||
|  | 				cilova.poznamka += f'\nDEBUG: Merge: pro {f} použit údaj {zf} (zdrojová), nepoužit {cf} (cílová)' | ||||||
|  | 			elif verdikt == 'c': | ||||||
|  | 				cilova.poznamka += f'\nDEBUG: Merge: pro {f} použit údaj {cf} (cílová), nepoužit {zf} (zdrojová)' | ||||||
|  | 			else: raise ValueError('Špatná odpověď, řešitel pravděpodobně neuložen') | ||||||
|  | 	# poznámku chceme nezahodit… | ||||||
|  | 	cilova.poznamka += f'\nDEBUG: Merge: Původní poznámka: {zdrojova.poznamka}' | ||||||
|  | 	print(f' Výsledná osoba: {cilova.__dict__}, ukládám') | ||||||
|  | 	cilova.save() | ||||||
|  | 
 | ||||||
|  | 	# Vazby: Řešitel, User, Příjemce, Organizátor, Škola.kontaktní_osoba | ||||||
|  | 	print('Přepojuji vazby') | ||||||
|  | 	ct = Skola.objects.filter(kontaktni_osoba=zdrojova).update(kontaktni_osoba=cilova) | ||||||
|  | 	print(f' Přepojeno {ct} kontaktních osob') | ||||||
|  | 	# Ostatní vazby vyřeší OneToOneFieldy, ale někdy nemusí existovat… | ||||||
|  | 	ct = Resitel.objects.filter(osoba=zdrojova).update(osoba=cilova) | ||||||
|  | 	print(f' Přepojeno {ct} řešitelů') | ||||||
|  | 	ct = Prijemce.objects.filter(osoba=zdrojova).update(osoba=cilova) | ||||||
|  | 	print(f' Přepojeno {ct} příjemců') | ||||||
|  | 	ct = Organizator.objects.filter(osoba=zdrojova).update(osoba=cilova) | ||||||
|  | 	print(f' Přepojeno {ct} organizátorů') | ||||||
|  | 	# Uživatelé vedou opačným směrem, radši chceme zkontrolovat, že jsou různí ručně: | ||||||
|  | 	if zdrojova.user != cilova.user: | ||||||
|  | 		# Jeden z nich může být nenastavený… | ||||||
|  | 		if zdrojova.user is None: | ||||||
|  | 			print('Uživatel je již v cílové osobě') | ||||||
|  | 		elif cilova.user is None: | ||||||
|  | 			print('Používám uživatele zdrojové osoby') | ||||||
|  | 			cilova.user = zdrojova.user | ||||||
|  | 		# Teď nemůžeme uložit, protože kolize uživatelů. Ukládat cílovou budeme až po smazání zdrojové. | ||||||
|  | 		else: raise ValueError('Osoby mají obě uživatele, radši padám') | ||||||
|  | 
 | ||||||
|  | 	# Uložení a mazání | ||||||
|  | 	print(f'Mažu zdrojovou osobu {zdrojova.__dict__}') | ||||||
|  | 	zdrojova.delete() | ||||||
|  | 	print(f'Ukládám cílovou osobu {cilova.__dict__}') | ||||||
|  | 	cilova.save() | ||||||
|  | 
 | ||||||
|  | 	input("Potvrdit transakci osob (^C pro zrušení) ") | ||||||
|  |  | ||||||
|  | @ -1,3 +1,8 @@ | ||||||
|  | import tempfile | ||||||
|  | import subprocess | ||||||
|  | import shutil | ||||||
|  | import http | ||||||
|  | 
 | ||||||
| from django.shortcuts import render | from django.shortcuts import render | ||||||
| from django.urls import reverse | from django.urls import reverse | ||||||
| from django.views import generic | from django.views import generic | ||||||
|  | @ -6,8 +11,10 @@ from django.views.decorators.debug import sensitive_post_parameters | ||||||
| from django.views.generic.base import TemplateView | from django.views.generic.base import TemplateView | ||||||
| from django.contrib.auth.models import User, Permission, Group, AnonymousUser | from django.contrib.auth.models import User, Permission, Group, AnonymousUser | ||||||
| from django.contrib.auth.mixins import LoginRequiredMixin | from django.contrib.auth.mixins import LoginRequiredMixin | ||||||
|  | from django.contrib.staticfiles.finders import find | ||||||
| from django.db import transaction | from django.db import transaction | ||||||
| from django.http import HttpResponse | from django.http import HttpResponse | ||||||
|  | from django.utils import timezone | ||||||
| 
 | 
 | ||||||
| import seminar.models as s | import seminar.models as s | ||||||
| import seminar.models as m | import seminar.models as m | ||||||
|  | @ -17,12 +24,65 @@ from datetime import date | ||||||
| import logging | import logging | ||||||
| import csv | import csv | ||||||
| 
 | 
 | ||||||
| from seminar.views import formularOKView | from various.views.pomocne import formularOKView | ||||||
| from various.autentizace.views import LoginView | from various.autentizace.views import LoginView | ||||||
| from various.autentizace.utils import posli_reset_hesla | from various.autentizace.utils import posli_reset_hesla | ||||||
| 
 | 
 | ||||||
| from django.forms.models import model_to_dict | from django.forms.models import model_to_dict | ||||||
| 
 | 
 | ||||||
|  | from .models import Organizator | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def aktivniOrganizatori(datum=timezone.now()): | ||||||
|  | 	return Organizator.objects.exclude( | ||||||
|  | 		organizuje_do__isnull=False, | ||||||
|  | 		organizuje_do__lt=datum | ||||||
|  | 	).order_by('osoba__jmeno') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class CojemamOrganizatoriView(generic.ListView): | ||||||
|  | 	model = Organizator | ||||||
|  | 	template_name = 'personalni/organizatori.html' | ||||||
|  | 	queryset = aktivniOrganizatori() | ||||||
|  | 
 | ||||||
|  | 	def get_context_data(self, **kwargs): | ||||||
|  | 		context = super(CojemamOrganizatoriView, self).get_context_data(**kwargs) | ||||||
|  | 		context['aktivni'] = True | ||||||
|  | 		return context | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class CojemamOrganizatoriStariView(generic.ListView): | ||||||
|  | 	model = Organizator | ||||||
|  | 	template_name = 'personalni/organizatori.html' | ||||||
|  | 	queryset = Organizator.objects.exclude( | ||||||
|  | 		id__in=aktivniOrganizatori() | ||||||
|  | 	).order_by('-organizuje_do') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def obalkyView(request, resitele): | ||||||
|  | 	if len(resitele) == 0: | ||||||
|  | 		return HttpResponse( | ||||||
|  | 			render(request, 'universal.html', { | ||||||
|  | 				'title': 'Není pro koho vyrobit obálky.', | ||||||
|  | 				'text': 'Právě ses pokusil/a vygenerovat obálky pro prázdnou množinu lidí. Můžeš to zkusit změnit, případně se zeptej webařů :-)', | ||||||
|  | 			}), | ||||||
|  | 			status=http.HTTPStatus.NOT_FOUND, | ||||||
|  | 		) | ||||||
|  | 
 | ||||||
|  | 	tex = render(request, 'personalni/obalky.tex', { | ||||||
|  | 		'resitele': resitele | ||||||
|  | 	}).content | ||||||
|  | 
 | ||||||
|  | 	with tempfile.TemporaryDirectory() as tempdir: | ||||||
|  | 		with open(tempdir+"/obalky.tex", "w") as texfile: | ||||||
|  | 			texfile.write(tex.decode()) | ||||||
|  | 		shutil.copy(find('personalni/lisak.pdf'), tempdir) | ||||||
|  | 		subprocess.call(["pdflatex", "obalky.tex"], cwd=tempdir) | ||||||
|  | 
 | ||||||
|  | 		with open(tempdir+"/obalky.pdf", "rb") as pdffile: | ||||||
|  | 			response = HttpResponse(pdffile.read(), content_type='application/pdf') | ||||||
|  | 	return response | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class OrgoRozcestnikView(TemplateView): | class OrgoRozcestnikView(TemplateView): | ||||||
| 	""" Zobrazí organizátorský rozcestník.""" | 	""" Zobrazí organizátorský rozcestník.""" | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| from django.urls import path | from django.urls import path | ||||||
| from seminar.utils import org_required, resitel_or_org_required | from personalni.utils import org_required, resitel_or_org_required | ||||||
| from . import views | from . import views | ||||||
| 
 | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ 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 seminar.models import base as bm | ||||||
| 
 | 
 | ||||||
| from seminar.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet | from odevzdavatko.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet | ||||||
| from personalni.models import Resitel | from personalni.models import Resitel | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -23,7 +23,7 @@ from taggit.managers import TaggableManager | ||||||
| 
 | 
 | ||||||
| from reversion import revisions as reversion | from reversion import revisions as reversion | ||||||
| 
 | 
 | ||||||
| from seminar.utils import roman | from tvorba.utils import roman, aktivniResitele | ||||||
| from treenode import treelib | from treenode import treelib | ||||||
| 
 | 
 | ||||||
| from unidecode import unidecode # Používám pro získání ID odkazu (ještě je to někde po někom zakomentované) | from unidecode import unidecode # Používám pro získání ID odkazu (ještě je to někde po někom zakomentované) | ||||||
|  | @ -31,7 +31,6 @@ from unidecode import unidecode # Používám pro získání ID odkazu (ještě | ||||||
| from polymorphic.models import PolymorphicModel | from polymorphic.models import PolymorphicModel | ||||||
| 
 | 
 | ||||||
| from django.core.mail import EmailMessage | from django.core.mail import EmailMessage | ||||||
| from seminar.utils import aktivniResitele |  | ||||||
| 
 | 
 | ||||||
| from personalni.models import Prijemce, Organizator | from personalni.models import Prijemce, Organizator | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,16 +0,0 @@ | ||||||
| {% extends 'base.html' %} |  | ||||||
| 
 |  | ||||||
| {% load humanize %} |  | ||||||
| {% load static %} |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| {% block content %} |  | ||||||
| 
 |  | ||||||
| <div class=jakresit> |  | ||||||
| 
 |  | ||||||
| {% include 'seminar/jakresit/jakresit_1.svg' %} |  | ||||||
| {% include 'seminar/jakresit/jakresit_2.svg' %} |  | ||||||
| {% include 'seminar/jakresit/jakresit_3.svg' %} |  | ||||||
| 
 |  | ||||||
| </div> |  | ||||||
| {% endblock %} |  | ||||||
|  | @ -1,19 +0,0 @@ | ||||||
| from django import template |  | ||||||
| from django.utils.safestring import mark_safe |  | ||||||
| from datetime import datetime, timedelta |  | ||||||
| from mamweb.settings import TIME_ZONE |  | ||||||
| import logging |  | ||||||
| register = template.Library() |  | ||||||
| 
 |  | ||||||
| logger = logging.getLogger(__name__) |  | ||||||
| 
 |  | ||||||
| @register.filter(name='kratke_datum', expects_localtime=True) |  | ||||||
| def kratke_datum(dt): |  | ||||||
| 	# None dává None, ne-datum dává False, aby se daly použít filtry typu "default". |  | ||||||
| 	if dt is None: |  | ||||||
| 		return None |  | ||||||
| 	if not isinstance(dt, datetime): |  | ||||||
| 		logger.warning(f"Špatné volání filtru {__name__}: {dt}") |  | ||||||
| 		return False |  | ||||||
| 	out = f'<time datetime="{dt.isoformat()}" title="{dt.strftime("%d. %m. %Y %H:%M")}">{dt.day}.{dt.month}.<span style="text-decoration:overline">{dt.year%100}</time>' |  | ||||||
| 	return mark_safe(out) |  | ||||||
|  | @ -1,910 +0,0 @@ | ||||||
| import datetime |  | ||||||
| 
 |  | ||||||
| from django.contrib.auth.models import Permission |  | ||||||
| from django.contrib.auth.models import Group |  | ||||||
| import random |  | ||||||
| import lorem |  | ||||||
| import django.contrib.auth |  | ||||||
| from django.db import transaction |  | ||||||
| import unidecode |  | ||||||
| import logging |  | ||||||
| 
 |  | ||||||
| from korektury.testutils import create_test_pdf |  | ||||||
| from seminar.models import Skola, Resitel, Rocnik, Cislo, Deadline, Problem, Reseni, PrilohaReseni, Nastaveni, Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Osoba, Organizator, Prijemce, Tema, Uloha, Konfera, TextNode, UlohaVzorakNode, RocnikNode, CisloNode, TemaVCisleNode, Text, Hodnoceni, UlohaZadaniNode, Novinky, TreeNode |  | ||||||
| import seminar.models as m |  | ||||||
| 
 |  | ||||||
| from django.contrib.flatpages.models import FlatPage |  | ||||||
| from django.contrib.sites.models import Site |  | ||||||
| from treenode.treelib import all_children, insert_last_child, all_children_of_type, create_node_after |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| User = django.contrib.auth.get_user_model() |  | ||||||
| zlinska = None # tohle bude speciální škola, které později dodáme kontaktní osobu |  | ||||||
| 
 |  | ||||||
| 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): |  | ||||||
| 	logger.info('Generuji osoby (size={})...'.format(size)) |  | ||||||
| 
 |  | ||||||
| 	jmena_m = ['Aleš', 'Tomáš', 'Martin', 'Jakub', 'Petr', 'Lukáš', 'Cyril', 'Pavel Karel'] |  | ||||||
| 	jmena_f = ['Eva', 'Karolína', 'Zuzana', 'Sylvie', 'Iva', 'Jana', 'Marie', |  | ||||||
| 		'Marta Iva', 'Shu Shan'] |  | ||||||
| 	prijmeni_m = ['Novotný', 'Svoboda', 'Pecha', 'Kořen', 'Holan', 'Uhlíř', 'Chytráček', |  | ||||||
| 			'Pokora', 'Koch', 'Szegedy', 'Rudý', "von Neumann", "d'Este"] |  | ||||||
| 	prijmeni_f = ['Novotná', 'Svobodová', 'Machová', 'Zelená', 'Yu-Xin', 'Mlsná', 'Dubná', |  | ||||||
| 			'Mrkvová', 'Suchá', 'Lovelace', 'Holcová', 'Rui', "Nováčková Tydlitátová"] |  | ||||||
| 	prezdivky = ['Kaki', 'Hurdur', 'Maracuja', 'Bobbo', "", "", "", "", "", |  | ||||||
| 			"", "", 'Riki', 'Sapa', "", '', '---', 'Koko'] |  | ||||||
| 	domain = ['example.com', 'dolujeme.eu', 'mff.cuni.cz', 'strcprstskrzkrk.cz', |  | ||||||
| 			'british.co.uk', 'splachni.to', 'haha.org'] |  | ||||||
| 	seznam_ulic = ['Krátká', 'Vlhká', 'Jungmanova', '17. listopadu', '4. října', 'Roztocká', |  | ||||||
| 			'Forstova', 'Generála Františka Janouška', 'Náměstí Války', |  | ||||||
| 			'Svratecké náměstí', 'Zelená lhota', 'Z Plynu', 'K Jezeru', 'U Kocourkova', |  | ||||||
| 			'Uštěpačná', 'Ostrorepská', 'Zubří'] |  | ||||||
| 	seznam_mest = ['Praha', 'Brno', 'Ostrava', 'Horní Jelení', 'Dolní Zábrdovice', 'Prdelkov', |  | ||||||
| 			'Stará myslivna', 'Kocourkov', 'Šalingrad', 'Medvědí hora', 'Basilej', |  | ||||||
| 			'Unterschiedlich', 'Old York', 'Lancastershire', 'Vóloďháza'] |  | ||||||
| 
 |  | ||||||
| 	osoby = [] |  | ||||||
| 	# 30 je náhodná konstanta, size je použité na víc místech a |  | ||||||
| 	# říká, jak velká asi chceme testovací data |  | ||||||
| 	for i in range(30 * size): |  | ||||||
| 		pohlavi_idx = rnd.randint(0,2) # 2 = nebinární |  | ||||||
| 		osloveni = [Osoba.OSLOVENI_MUZSKE, Osoba.OSLOVENI_ZENSKE, Osoba.OSLOVENI_ZADNE][pohlavi_idx] |  | ||||||
| 		jmeno = rnd.choice([jmena_m, jmena_f, jmena_m + jmena_f][pohlavi_idx]) |  | ||||||
| 		prijmeni = rnd.choice([prijmeni_m, prijmeni_f, prijmeni_m + prijmeni_f][pohlavi_idx]) |  | ||||||
| 		if pohlavi_idx == 2: logger.debug(f'Testdata: nebinární osoba: {jmeno} {prijmeni}.') |  | ||||||
| 		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, jmena_m + jmena_f][pohlavi_idx]) |  | ||||||
| 			prijmeni = rnd.choice([prijmeni_m, prijmeni_f, prijmeni_m + prijmeni_f][pohlavi_idx]) |  | ||||||
| 			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) |  | ||||||
| 		email = "@".join([unidecode.unidecode(jmeno), rnd.choice(domain)]) |  | ||||||
| 		telefon = "".join([str(rnd.choice([k for k in range(10)])) for i in range(9)]) |  | ||||||
| 		narozeni = datetime.date(rnd.randint(1980, datetime.datetime.now().year),  |  | ||||||
| 				rnd.randint(1, 12), rnd.randint(1, 28)) |  | ||||||
| 		ulic = rnd.choice(seznam_ulic) |  | ||||||
| 		cp = rnd.randint(1, 99) |  | ||||||
| 		ulice = " ".join([ulic, str(cp)]) |  | ||||||
| 		mesto = rnd.choice(seznam_mest) |  | ||||||
| 		psc = "".join([str(rnd.choice([k for k in range(10)])) for i in range(5)]) |  | ||||||
| 
 |  | ||||||
| 		osoby.append(Osoba.objects.create(jmeno = jmeno, prijmeni = prijmeni, |  | ||||||
| 				prezdivka = prezdivka, osloveni = osloveni, email = email, |  | ||||||
| 				telefon = telefon, datum_narozeni = narozeni, ulice = ulice, |  | ||||||
| 				mesto = mesto, psc = psc, |  | ||||||
| 				datum_registrace = datetime.date(rnd.randint(2019, 2029), |  | ||||||
| 					rnd.randint(1, 12), rnd.randint(1, 28)))) |  | ||||||
| 		#TODO pridat foto male a velke. Jak? |  | ||||||
| 				# Pavel tvrdí, že to necháme a přidáme až do adminu |  | ||||||
| 
 |  | ||||||
| 	return osoby |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def gen_skoly(): #TODO někdy to přepsat, aby jich bylo více |  | ||||||
| 	logger.info('Generuji školy...') |  | ||||||
| 
 |  | ||||||
| 	skoly = [] |  | ||||||
| 	prvnizs = Skola.objects.create(mesto='Praha', stat='CZ', psc='101 00', |  | ||||||
| 		ulice='Krátká 5', nazev='První ZŠ', je_zs=True, je_ss=False) |  | ||||||
| 	skoly.append(prvnizs) |  | ||||||
| 	skoly.append(Skola.objects.create(mesto='Praha', stat='CZ', psc='101 00', |  | ||||||
| 		ulice='Krátká 5', nazev='První SŠ', je_zs=False, je_ss=True)) |  | ||||||
| 	skoly.append(Skola.objects.create(mesto='Praha', stat='CZ', psc='102 00', |  | ||||||
| 		ulice='Dlouhá 5', nazev='Druhá SŠ', je_zs=False, je_ss=True)) |  | ||||||
| 	skoly.append(Skola.objects.create(mesto='Praha', stat='CZ', psc='103 00', |  | ||||||
| 		ulice='Široká 3', nazev='Třetí SŠ a ZŠ', je_zs=True, je_ss=True)) |  | ||||||
| 	skoly.append(Skola.objects.create(mesto='Ostrava', stat='CZ', psc='700 00', |  | ||||||
| 		ulice='Hluboká 42', nazev='Hutní gympl', je_zs=False, je_ss=True)) |  | ||||||
| 	skoly.append(Skola.objects.create(mesto='Humenné', stat='SK', psc='012 34', |  | ||||||
| 		ulice='Pltká 1', nazev='Sredná škuola', je_zs=False, je_ss=True)) |  | ||||||
| 	global zlinska |  | ||||||
| 	zlinska = Skola.objects.create(mesto = 'Zlín', stat='CZ', psc='76001', |  | ||||||
| 		ulice='náměstí T.G. Masaryka 2734-9', |  | ||||||
| 		nazev='Gymnázium a Střední jazyková škola s právem SJZ', |  | ||||||
| 		kratky_nazev="GaSJŠspSJZ", je_zs=True, je_ss=True) |  | ||||||
| 	skoly.append(zlinska) |  | ||||||
| 	return skoly |  | ||||||
| 
 |  | ||||||
| def gen_resitele(rnd, osoby, skoly): |  | ||||||
| 	logger.info('Generuji řešitele...') |  | ||||||
| 
 |  | ||||||
| 	resitele = [] |  | ||||||
| 	x = 0 |  | ||||||
| 	resitel_perm = Permission.objects.filter(codename__exact='resitel').first() |  | ||||||
| 	resitel_group = Group.objects.filter(name__exact='resitel').first() |  | ||||||
| 	for os in osoby: |  | ||||||
| 		rand = rnd.randint(0, 8) |  | ||||||
| 		if not (rand % 8 == 0): |  | ||||||
| 			if not os.user: |  | ||||||
| 				if x: |  | ||||||
| 					user = User.objects.create_user(username='r'+str(x), email=os.email, password='r') |  | ||||||
| 				else: |  | ||||||
| 					user = User.objects.create_user(username='r', email=os.email, password='r') |  | ||||||
| 				x += 1 |  | ||||||
| 				os.user = user |  | ||||||
| 				os.save() |  | ||||||
| 				os.user.user_permissions.add(resitel_perm) |  | ||||||
| 				os.user.groups.add(resitel_group) |  | ||||||
| 			resitele.append(Resitel.objects.create(osoba=os, skola=rnd.choice(skoly), |  | ||||||
| 				rok_maturity=os.datum_narozeni.year + rnd.randint(18, 21), |  | ||||||
| 				zasilat=rnd.choice(Resitel.ZASILAT_CHOICES)[0])) |  | ||||||
| 	return resitele |  | ||||||
| 
 |  | ||||||
| def gen_prijemci(rnd, osoby, kolik=10): |  | ||||||
| 	logger.info('Generuji příjemce (kolik={})...'.format(kolik)) |  | ||||||
| 	prijemci = [] |  | ||||||
| 	for i in rnd.sample(osoby, kolik): |  | ||||||
| 		prijemci.append(Prijemce.objects.create(osoba=i)) |  | ||||||
| 	return prijemci |  | ||||||
| 
 |  | ||||||
| def gen_organizatori(rnd, osoby, last_rocnik): |  | ||||||
| 	logger.info('Generuji organizátory...') |  | ||||||
| 	organizatori = [] |  | ||||||
| 
 |  | ||||||
| 	 |  | ||||||
| 	seznam_konicku = ["vařím", "jezdím na kole", "řeším diferenciální rovnice", "koukám z okna", |  | ||||||
| 			"tancuji", "programuji", "jezdím vlakem", "nedělám nic"] |  | ||||||
| 	seznam_oboru = ["matematiku", "matematiku", "matematiku", "fyziku", "literaturu", |  | ||||||
| 			"informatiku", "informatiku", "běhání dokolečka"] |  | ||||||
| 
 |  | ||||||
| 	x = 0 |  | ||||||
| 	org_perm = Permission.objects.filter(codename__exact='org').first() |  | ||||||
| 	org_group = Group.objects.filter(name__exact='org').first() |  | ||||||
| 	for os in osoby: |  | ||||||
| 		rand = rnd.randint(0, 8) |  | ||||||
| 		if (rand % 8 == 0): |  | ||||||
| 			pusobnost = rnd.randint(1, last_rocnik) |  | ||||||
| 			od = datetime.datetime( |  | ||||||
| 				year=1993 + pusobnost, |  | ||||||
| 				month=rnd.randint(1, 12), |  | ||||||
| 				day=rnd.randint(1, 28), |  | ||||||
| 				tzinfo=datetime.timezone.utc, |  | ||||||
| 				) |  | ||||||
| 			do = datetime.datetime( |  | ||||||
| 				year=od.year + rnd.randint(1, 6), |  | ||||||
| 				month=rnd.randint(1, 12), |  | ||||||
| 				day=rnd.randint(1, 28), |  | ||||||
| 				tzinfo=datetime.timezone.utc, |  | ||||||
| 				) |  | ||||||
| 			#aktualni organizatori jeste nemaji vyplnene organizuje_do |  | ||||||
| 
 |  | ||||||
| 			#popis orga |  | ||||||
| 			konicek1 = rnd.choice(seznam_konicku) |  | ||||||
| 			konicek2 = rnd.choice(seznam_konicku) |  | ||||||
| 			obor = rnd.choice(seznam_oboru) |  | ||||||
| 			popis_orga = "Ve volném čase " + konicek1 + " a také " + konicek2 + ". Studuji " + obor + " a moc mě to baví." |  | ||||||
| 
 |  | ||||||
| 			if do.year > datetime.datetime.now().year: |  | ||||||
| 				do = None |  | ||||||
| 			if not os.user: |  | ||||||
| 				if x: |  | ||||||
| 					user = User.objects.create_user(username='o'+str(x), email=os.email, password='o') |  | ||||||
| 				else: |  | ||||||
| 					user = User.objects.create_user(username='o', email=os.email, password='o') |  | ||||||
| 				x += 1 |  | ||||||
| 				os.user = user |  | ||||||
| 				os.save() |  | ||||||
| 				os.user.user_permissions.add(org_perm) |  | ||||||
| 				os.user.groups.add(org_group) |  | ||||||
| 				os.user.is_staff = True |  | ||||||
| 				os.user.save() |  | ||||||
| 			organizatori.append(Organizator.objects.create(osoba=os, |  | ||||||
| 				organizuje_od=od, organizuje_do=do, strucny_popis_organizatora = popis_orga)) |  | ||||||
| 	return organizatori |  | ||||||
| 
 |  | ||||||
| def gen_zadani_ulohy(rnd, cisla, organizatori, pocet_oboru, poradi_cisla, poradi_problemu): |  | ||||||
| 	 |  | ||||||
| 	# Proměnné pro náhodné generování názvů a zadání. |  | ||||||
| 	jaka = ["Šachová", "Černá", "Větrná", "Dlouhá", "Křehká", "Rychlá", |  | ||||||
| 		"Zákeřná", "Fyzikální"] |  | ||||||
| 	co = ["kostka", "smršť", "díra", "zrada", "toulka", "tyč", |  | ||||||
| 		"úloha", "blecha"] |  | ||||||
| 	sloveso = ["Najděte", "Spočítejte", "Zapište", "Změřte", "Odhadněte"] |  | ||||||
| 	koho = ["délku", "počet", "množství", "dílky"] |  | ||||||
| 	ceho = ["všech", "správných", "konstatních", "zelených"] |  | ||||||
| 	jmeno = ["řešení", "tahů", "čísel", "kalhot", "koulí", "hadů"] |  | ||||||
| 	kde = ["na zemi", "ve vesmíru", "ve vzduchu", "na šňůře", "v letadle"] |  | ||||||
| 	obory = ["M", "F", "I", "O", "B"] |  | ||||||
| 
 |  | ||||||
| 	p = Uloha.objects.create( |  | ||||||
| 		# atributy třídy Problem |  | ||||||
| 		nazev=" ".join([rnd.choice(jaka), rnd.choice(co)]), |  | ||||||
| 		stav=Problem.STAV_ZADANY, |  | ||||||
| 		zamereni=rnd.sample(obory, pocet_oboru), |  | ||||||
| 		autor=rnd.choice(organizatori), |  | ||||||
| 		garant=rnd.choice(organizatori), |  | ||||||
| 		kod=str(poradi_problemu), |  | ||||||
| 		# atributy třídy Uloha |  | ||||||
| 		cislo_zadani=cisla[poradi_cisla-2-1], |  | ||||||
| 		cislo_reseni=cisla[poradi_cisla-1], |  | ||||||
| 		cislo_deadline=cisla[poradi_cisla-1], |  | ||||||
| 		max_body = rnd.randint(1, 8) |  | ||||||
| 	) |  | ||||||
| 
 |  | ||||||
| 	text = " ".join( |  | ||||||
| 		[rnd.choice(sloveso), |  | ||||||
| 		rnd.choice(koho), |  | ||||||
| 		rnd.choice(ceho), |  | ||||||
| 		rnd.choice(jmeno), |  | ||||||
| 		rnd.choice(kde)] |  | ||||||
| 		) |  | ||||||
| 	text_zadani = Text.objects.create( |  | ||||||
| 		na_web = text, |  | ||||||
| 		do_cisla = text, |  | ||||||
| 	) |  | ||||||
| 	zad = TextNode.objects.create(text = text_zadani, root = p.cislo_zadani.rocnik.rocniknode) |  | ||||||
| 	uloha_zadani = UlohaZadaniNode.objects.create(uloha = p, first_child = zad, root = p.cislo_zadani.rocnik.rocniknode) |  | ||||||
| 	p.ulohazadaninode = uloha_zadani |  | ||||||
| 	otec_syn(cisla[poradi_cisla-2-1].cislonode, uloha_zadani) |  | ||||||
| 
 |  | ||||||
| 	return p |  | ||||||
| 
 |  | ||||||
| def gen_vzoroveho_reseni_ulohy(rnd, organizatori, uloha, pocet_opravovatelu): |  | ||||||
| 	reseni = ["to je přece jasné", "triviální", "omlouváme se," |  | ||||||
| 		"otevřený problém", "neřešitelné", "triviálně triviální", |  | ||||||
| 		"použitím věty z prvního semestru na matfyzu", |  | ||||||
| 		"jednoduše pomocí látky z druhého semestru na matfyzu", |  | ||||||
| 		"netriviální aplikace diferenciálních rovnic", "zadání je vnitřně" |  | ||||||
| 		"sporné", "nepopsatelně jednoduché", "pokud jste na to nepřišli," |  | ||||||
| 		"tak jste fakt hloupí"] |  | ||||||
| 
 |  | ||||||
| 	# Generování vzorového řešení. |  | ||||||
| 	obsah = rnd.choice(reseni) |  | ||||||
| 	text_vzoraku = Text.objects.create( |  | ||||||
| 		na_web = obsah, |  | ||||||
| 		do_cisla = obsah |  | ||||||
| 	) |  | ||||||
| 	vzorak = TextNode.objects.create(text = text_vzoraku, root = uloha.cislo_zadani.rocnik.rocniknode) |  | ||||||
| 	uloha_vzorak = UlohaVzorakNode.objects.create(uloha = uloha, first_child = vzorak, root = uloha.cislo_zadani.rocnik.rocniknode) |  | ||||||
| 	uloha.ulohavzoraknode = uloha_vzorak |  | ||||||
| 
 |  | ||||||
| 	uloha.opravovatele.set(rnd.sample(organizatori, pocet_opravovatelu)) |  | ||||||
| 	uloha.save() |  | ||||||
| 	return uloha_vzorak |  | ||||||
| 
 |  | ||||||
| def gen_reseni_ulohy(rnd, cisla, uloha, pocet_resitelu, poradi_cisla, resitele_cisla, resitele): |  | ||||||
| 	 |  | ||||||
| 	pocet_reseni = rnd.randint(pocet_resitelu//4, pocet_resitelu * 4) |  | ||||||
| 	# generujeme náhodný počet řešení vzhledem k počtu řešitelů čísla |  | ||||||
| 	for _ in range(pocet_reseni): |  | ||||||
| 		#print("Generuji {}-té řešení".format(reseni)) |  | ||||||
| 		if rnd.randint(1, 10) == 1: |  | ||||||
| 		# cca desetina řešení od více řešitelů |  | ||||||
| 			res_vyber = rnd.sample(resitele_cisla,  |  | ||||||
| 				rnd.randint(2, 5)) |  | ||||||
| 		else: |  | ||||||
| 			res_vyber = rnd.sample(resitele_cisla, 1) |  | ||||||
| 		if resitele[0] in res_vyber: # speciální řešitel, který nemá žádné body |  | ||||||
| 			res_vyber.remove(resitele[0]) |  | ||||||
| 
 |  | ||||||
| 		# Vytvoření řešení. |  | ||||||
| 		if uloha.cislo_zadani.zlomovy_deadline_pro_papirove_cislo() is not None: |  | ||||||
| 			# combine, abychom dostali plný čas a ne jen datum |  | ||||||
| 			cas_doruceni = uloha.cislo_zadani.deadline_v_cisle.first().deadline - datetime.timedelta(days=random.randint(0, 40)) - datetime.timedelta(minutes=random.randint(0, 60*24)) |  | ||||||
| 			# astimezone, protože jinak vyhazuje warning o nenastavené TZ |  | ||||||
| 			res = Reseni.objects.create(forma=rnd.choice(Reseni.FORMA_CHOICES)[0], cas_doruceni=cas_doruceni.astimezone(datetime.timezone.utc)) |  | ||||||
| 		else: |  | ||||||
| 			res = Reseni.objects.create(forma=rnd.choice(Reseni.FORMA_CHOICES)[0]) |  | ||||||
| 		# Problém a řešitele přiřadíme později, ManyToManyField |  | ||||||
| 		# se nedá vyplnit v create(). |  | ||||||
| 		res.resitele.set(res_vyber) |  | ||||||
| 		res.save() |  | ||||||
| 		 |  | ||||||
| 		# Vytvoření hodnocení. |  | ||||||
| 		hod = Hodnoceni.objects.create( |  | ||||||
| 			body=rnd.randint(0, uloha.max_body), |  | ||||||
| 			cislo_body=cisla[poradi_cisla - 1], |  | ||||||
| 			reseni=res, |  | ||||||
| 			problem=uloha |  | ||||||
| 		) |  | ||||||
| 	return |  | ||||||
| 
 |  | ||||||
| def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size): |  | ||||||
| 	logger.info('Generuji úlohy do čísla (size={})...'.format(size)) |  | ||||||
| 
 |  | ||||||
| 	k = 0 |  | ||||||
| 	for rocnik in rocniky: |  | ||||||
| 		k += 1 |  | ||||||
| 		print("Generuji {}. číslo.".format(k)) |  | ||||||
| 		cisla = rocnik_cisla[k - 1] |  | ||||||
| 		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ů |  | ||||||
| 			poc_res = rnd.randint(resitele_size//8, resitele_size//4) |  | ||||||
|  			# dané číslo řeší něco mezi osminou a čtvrtinou všech řešitelů |  | ||||||
| 			# (náhodná hausnumera, možno změnit) |  | ||||||
| 			# úč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 |  | ||||||
| 			resitele_cisla = rnd.sample(resitele, poc_res) |  | ||||||
| 			for pi in range(1, ((size + 1) // 2) + 1): # počet problémů |  | ||||||
| 
 |  | ||||||
| 				poc_op = rnd.randint(1, 4) # počet opravovatelů |  | ||||||
| 				poc_oboru = rnd.randint(1, 2) |  | ||||||
| 				 |  | ||||||
| 				# Generování zadání úlohy a UlohaZadaniNode,  |  | ||||||
| 				# přivěšení pod dané číslo |  | ||||||
| 				p = gen_zadani_ulohy(rnd, cisla, organizatori, poc_oboru, ci, pi)	 |  | ||||||
| 				# Generování vzorového řešení |  | ||||||
| 				uloha_vzorak = gen_vzoroveho_reseni_ulohy(rnd, organizatori,  |  | ||||||
| 					p, poc_op) |  | ||||||
| 				insert_last_child(cisla[ci-1].cislonode, uloha_vzorak) |  | ||||||
| 
 |  | ||||||
| 				# Generování řešení a hodnocení k úloze |  | ||||||
| 				gen_reseni_ulohy(rnd, cisla, p, poc_res, ci,  |  | ||||||
| 					resitele_cisla, resitele)  |  | ||||||
| 
 |  | ||||||
| 	return |  | ||||||
| 
 |  | ||||||
| def gen_soustredeni(rnd, resitele, organizatori): |  | ||||||
| 	logger.info('Generuji soustředění...') |  | ||||||
| 
 |  | ||||||
| 	soustredeni = [] |  | ||||||
| 	for _ in range(1, 10): #FIXME Tu range si změňte jak chcete, nevím, co přesně znamená size (asi Anet?) |  | ||||||
| 		datum_zacatku=datetime.date(rnd.randint(2000, 2020), rnd.randint(1, 12), rnd.randint(1, 28)) |  | ||||||
| 		working_sous = Soustredeni.objects.create( |  | ||||||
| 			rocnik=Rocnik.objects.order_by('?').first(), |  | ||||||
| 			verejne_db=rnd.choice([True, False]), |  | ||||||
| 			misto=rnd.choice(['Kremrolovice', 'Indiánov', 'U zmzliny', 'Vafláreň', 'Větrník', 'Horní Rakvička', 'Dolní cheesecake']), |  | ||||||
| 			typ=rnd.choice(['jarni', 'podzimni', 'vikend']), |  | ||||||
| 			datum_zacatku=datum_zacatku, |  | ||||||
| 			datum_konce=datum_zacatku + datetime.timedelta(days=7)) |  | ||||||
| 		ucastnici = rnd.sample(resitele, min(len(resitele), 20)) |  | ||||||
| 		working_sous.ucastnici.set(ucastnici) |  | ||||||
| 		#for res in rnd.sample(resitele, min(len(resitele), 20)): |  | ||||||
| 		#	Soustredeni_Ucastnici.objects.create(resitel=res, soutredeni=working_sous) |  | ||||||
| 		orgove_vyber = rnd.sample(organizatori, min(len(organizatori), 20)) |  | ||||||
| 		working_sous.organizatori.set(orgove_vyber) |  | ||||||
| 		#for org in rnd.sample(organizatori, min(len(organizatori), 20)): |  | ||||||
| 		#	Soustredeni_Organizatori.objects.create(organizator=org, soutredeni=working_sous) |  | ||||||
| 		working_sous.save() |  | ||||||
| 		soustredeni.append(working_sous) |  | ||||||
| 	return soustredeni |  | ||||||
| 
 |  | ||||||
| def gen_rocniky(last_rocnik, size): |  | ||||||
| 	logger.info('Generuji ročníky (size={})...'.format(size)) |  | ||||||
| 
 |  | ||||||
| 	rocniky = [] |  | ||||||
| 	node = None |  | ||||||
| 	for ri in range(min(last_rocnik - size, 1), last_rocnik + 1): |  | ||||||
| 		rocnik = Rocnik.objects.create(prvni_rok = 1993 + ri, rocnik = ri) |  | ||||||
| 		node2 = RocnikNode.objects.create(rocnik = rocnik, succ = node) |  | ||||||
| 		rocnik.save() |  | ||||||
| 		node = node2 |  | ||||||
| 		rocniky.append(rocnik) |  | ||||||
| 	return rocniky |  | ||||||
| 
 |  | ||||||
| def gen_konfery(size, rnd, organizatori, resitele, soustredeni): |  | ||||||
| 	logger.info('Generuji konfery (size={})...'.format(size)) |  | ||||||
| 
 |  | ||||||
| 	konfery = [] |  | ||||||
| 	for _ in range(1, size): #FIXME Tu range si změňte jak chcete, nevím, co přesně znamená size (asi Anet?) |  | ||||||
| 		# Anet: size je parametr udávající velikost testovacích dat a dá se pomocí ní škálovat, |  | ||||||
| 		# kolik dat se nageneruje |  | ||||||
| 		konfera = Konfera.objects.create( |  | ||||||
| 			nazev=rnd.choice(['Pozorování', 'Zkoumání', 'Modelování', 'Počítání', 'Zkoušení']) + rnd.choice([' vlastností', ' jevů', ' charakteristik']) + rnd.choice([' vektorových prostorů', ' kinetické terorie látek', ' molekulární biologie', ' syntentických stromů']), |  | ||||||
| 			anotace=lorem.paragraph(), |  | ||||||
| 			abstrakt=lorem.paragraph(), |  | ||||||
| 			garant=rnd.choice(organizatori), |  | ||||||
| 			soustredeni=rnd.choice(soustredeni), |  | ||||||
| 			typ_prezentace=rnd.choice(['veletrh', 'prezentace'])) |  | ||||||
| 		ucastnici_sous = list(konfera.soustredeni.ucastnici.all()) |  | ||||||
| 		ucastnici = rnd.sample(ucastnici_sous, min(len(ucastnici_sous), rnd.randint(3, 6))) |  | ||||||
| 		konfera.ucastnici.set(ucastnici) |  | ||||||
| 		#for res in rnd.sample(ucastnici, min(len(ucastnici), rnd.randint(3, 6))): |  | ||||||
| 		#	Konfery_Ucastnici.objects.create(resitel=res, konfera=konfera) |  | ||||||
| 		konfera.save() |  | ||||||
| 		konfery.append(konfera) |  | ||||||
| 	return konfery |  | ||||||
| 
 |  | ||||||
| def gen_cisla(rnd, rocniky): |  | ||||||
| 	logger.info('Generuji čísla...') |  | ||||||
| 
 |  | ||||||
| 	rocnik_cisla = [] |  | ||||||
| 	for rocnik in rocniky: |  | ||||||
| 		otec = True |  | ||||||
| 		cisla = [] |  | ||||||
| 		cisel = rnd.randint(4, 8) |  | ||||||
| 		node = None |  | ||||||
| 		for ci in range(1, cisel + 1): |  | ||||||
| 			# první číslo vydáváme typicky okolo prázdnin |  | ||||||
| 			# (ci - 1)*2 zaručuje první číslo v červnu a všechna |  | ||||||
| 			# další po dvou měsících (což je rozumná aproximace) |  | ||||||
| 			mesic_vydani = (ci - 1)*2 + 6 |  | ||||||
| 			# celociselné dělení mi řekne, jestli to je první nebo druhý rok ročníku |  | ||||||
| 			vydano = datetime.date(rocnik.prvni_rok + mesic_vydani // 12, |  | ||||||
| 				(mesic_vydani - 1) % 12 + 1, |  | ||||||
| 				rnd.randint(1, 28)) |  | ||||||
| 			deadline = datetime.date(rocnik.prvni_rok + (mesic_vydani + 2) // 12, |  | ||||||
| 				(mesic_vydani + 1) % 12 + 1, |  | ||||||
| 				rnd.randint(1, 28)) |  | ||||||
| 
 |  | ||||||
| 			cislo = Cislo.objects.create( |  | ||||||
| 				rocnik = rocnik, |  | ||||||
| 				poradi = str(ci),  |  | ||||||
| 				datum_vydani=vydano, |  | ||||||
| 				verejne_db=True, |  | ||||||
| 			) |  | ||||||
| 			node2 = CisloNode.objects.get(cislo = cislo) |  | ||||||
| 			node2.succ = node |  | ||||||
| 			node2.root = rocnik.rocniknode |  | ||||||
| 			cislo.save() |  | ||||||
| 			deadline = Deadline.objects.create( |  | ||||||
| 				cislo=cislo, |  | ||||||
| 				deadline=deadline, |  | ||||||
| 				typ=Deadline.TYP_CISLA, |  | ||||||
| 				verejna_vysledkovka=True, |  | ||||||
| 			) |  | ||||||
| 			deadline.save() |  | ||||||
| 			node = node2 |  | ||||||
| 			if otec: |  | ||||||
| 				otec = False |  | ||||||
| 				rocnik.rocniknode.first_child = node |  | ||||||
| 				rocnik.save() |  | ||||||
| 
 |  | ||||||
| 			cisla.append(cislo) |  | ||||||
| 		rocnik_cisla.append(cisla) |  | ||||||
| 	return rocnik_cisla |  | ||||||
| 
 |  | ||||||
| def add_first_child(node, child): |  | ||||||
| 	node.first_child = child |  | ||||||
| 	node.save() |  | ||||||
| 	return |  | ||||||
| 
 |  | ||||||
| def get_text(): |  | ||||||
| 	odstavec = lorem.paragraph() |  | ||||||
| 	return Text.objects.create(na_web = odstavec, do_cisla = odstavec)	 |  | ||||||
| 
 |  | ||||||
| def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod): |  | ||||||
| 	tema = Tema.objects.create( |  | ||||||
| 				nazev=nazev, |  | ||||||
| 				stav=Problem.STAV_ZADANY, |  | ||||||
| 				zamereni="M", |  | ||||||
| 				autor=rnd.choice(organizatori), |  | ||||||
| 				garant=rnd.choice(organizatori), |  | ||||||
| 				kod=str(kod), |  | ||||||
| 				tema_typ=rnd.choice(Tema.TEMA_CHOICES)[0], |  | ||||||
| 				rocnik=rocnik, |  | ||||||
| 				abstrakt = lorem.paragraph()  |  | ||||||
| 		) |  | ||||||
| 	 |  | ||||||
| 	# Generování struktury k tématu |  | ||||||
| 	cisla = sorted(rocnik.cisla.all(), key=lambda cislo: cislo.poradi) |  | ||||||
| 	for cislo in cisla: |  | ||||||
| 		# Přidáme TemaVCisleNode do daného čísla |  | ||||||
| 		cislo_node = cislo.cislonode	 |  | ||||||
| 		tema_cislo_node = TemaVCisleNode.objects.create(tema = tema, root = cislo_node.root) |  | ||||||
| 		insert_last_child(cislo_node, tema_cislo_node) |  | ||||||
| 		 |  | ||||||
| 		# Přidávání obsahu do čísla |  | ||||||
| 		cast_node = m.CastNode.objects.create(nadpis = "Příspěvek k číslu {}".format(cislo.kod), root=cislo_node.root) |  | ||||||
| 		add_first_child(tema_cislo_node, cast_node) |  | ||||||
| 	 |  | ||||||
| 		text_node = TextNode.objects.create(text = get_text(), root=cislo_node.root) |  | ||||||
| 		add_first_child(cast_node, text_node) |  | ||||||
| 
 |  | ||||||
| 		cast_node2 = m.CastNode.objects.create(nadpis = "První podproblém", root=cislo_node.root) |  | ||||||
| 		add_first_child(text_node, cast_node2) |  | ||||||
| 		 |  | ||||||
| 		text_node2 = TextNode.objects.create(text = get_text(), root=cislo_node.root) |  | ||||||
| 		add_first_child(cast_node2, text_node2) |  | ||||||
| 		 |  | ||||||
| 		cast_node3 = m.CastNode.objects.create(nadpis = "Druhý podproblém", root=cislo_node.root) |  | ||||||
| 		add_first_child(text_node2, cast_node3) |  | ||||||
| 
 |  | ||||||
| 		text_node3 = TextNode.objects.create(text = get_text(), root=cislo_node.root) |  | ||||||
| 		add_first_child(cast_node3, text_node3) |  | ||||||
| 
 |  | ||||||
| 		cast_node4 = m.CastNode.objects.create(nadpis = "Třetí podproblém", root=cislo_node.root) |  | ||||||
| 		add_first_child(text_node3, cast_node4)	 |  | ||||||
| 
 |  | ||||||
| 		text_node4 = TextNode.objects.create(text = get_text(), root=cislo_node.root) |  | ||||||
| 		add_first_child(cast_node3, text_node4) |  | ||||||
| 		 |  | ||||||
| 		cast_node3a = m.CastNode.objects.create(nadpis = "Podproblém paralelní s " |  | ||||||
| 					"druhým podproblémem", root=cislo_node.root) |  | ||||||
| 		cast_node3.succ = cast_node3a |  | ||||||
| 		cast_node3.save() |  | ||||||
| 
 |  | ||||||
| 		text_node3a = TextNode.objects.create(text = get_text(), root=cislo_node.root) |  | ||||||
| 		add_first_child(cast_node3a, text_node3a) |  | ||||||
| 
 |  | ||||||
| 		# Občas přidáme mezičíslo |  | ||||||
| 		if rnd.randint(1, 3) == 1: |  | ||||||
| 			create_node_after(cislo_node, m.MezicisloNode, root=cislo_node.root) |  | ||||||
| 			mezicislo_node = cislo_node.succ |  | ||||||
| 	 |  | ||||||
| 			cast_node_mezicislo = m.CastNode.objects.create( |  | ||||||
| 					nadpis = "Příspěvek k mezičíslu".format(cislo.kod), root=cislo_node.root) |  | ||||||
| 			add_first_child(mezicislo_node, cast_node_mezicislo) |  | ||||||
| 
 |  | ||||||
| 			odstavec = lorem.paragraph() |  | ||||||
| 			text_mezicislo = Text.objects.create(na_web = odstavec, do_cisla = odstavec) |  | ||||||
| 			text_node_mezicislo = TextNode.objects.create(text = text_mezicislo, root=cislo_node.root) |  | ||||||
| 			add_first_child(cast_node_mezicislo, text_node_mezicislo) |  | ||||||
| 
 |  | ||||||
| 	return tema |  | ||||||
| 
 |  | ||||||
| def gen_temata(rnd, rocniky, rocnik_cisla, organizatori): |  | ||||||
| 	logger.info('Generuji témata...') |  | ||||||
| 
 |  | ||||||
| 	jake = ["Hravé", "Fyzikální", "Nejlepší", "Totálně masakrální", |  | ||||||
| 					"Šokující", "Magnetické", "Modré", "Překvapivé", |  | ||||||
| 					"Plasmatické", "Novoroční"] |  | ||||||
| 	co = ["téma", "záření", "stavení", "jiskření", "jelito", |  | ||||||
| 					"drama", "kuře", "moře", "klání", "proudění", "čekání"] |  | ||||||
| 	poc_oboru = rnd.randint(1, 2) |  | ||||||
| 
 |  | ||||||
| 	rocnik_temata = [] |  | ||||||
| 	# 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, cisla in zip(rocniky, rocnik_cisla): |  | ||||||
| 		kod = 1 |  | ||||||
| 		letosni_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 |  | ||||||
| 		for zacatek_tematu in range(1, 3): |  | ||||||
| 			# Vygenerujeme téma |  | ||||||
| 			t = Tema.objects.create( |  | ||||||
| 				# atributy třídy Problem |  | ||||||
| 				nazev=" ".join([rnd.choice(jake), rnd.choice(co)]), |  | ||||||
| 				stav=Problem.STAV_ZADANY, |  | ||||||
| 				zamereni=rnd.sample(["M", "F", "I", "O", "B"], poc_oboru), |  | ||||||
| 				autor=rnd.choice(organizatori), |  | ||||||
| 				garant=rnd.choice(organizatori), |  | ||||||
| 				kod=str(kod), |  | ||||||
| 				# atributy třídy Téma |  | ||||||
| 				tema_typ=rnd.choice(Tema.TEMA_CHOICES)[0], |  | ||||||
| 				rocnik=rocnik, |  | ||||||
| 				abstrakt = "Abstrakt tematka {}".format(kod) |  | ||||||
| 			) |  | ||||||
| 			kod += 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,root=rocnik.rocniknode) |  | ||||||
| 				# FIXME: Není to off-by-one? |  | ||||||
| 				otec = cisla[i-1].cislonode |  | ||||||
| 				otec_syn(otec, node) |  | ||||||
| 
 |  | ||||||
| 			# 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() |  | ||||||
| 			letosni_temata.append((zacatek_tematu, konec_tematu, t)) |  | ||||||
| 		rocnik_temata.append(letosni_temata) |  | ||||||
| 	return rocnik_temata |  | ||||||
| 
 |  | ||||||
| def gen_ulohy_tematu(rnd, organizatori, resitele, tema, kod, cislo, cislo_se_vzorakem): |  | ||||||
| 	""" Generování úlohy k danému tématu. """ |  | ||||||
| 	 |  | ||||||
| 	# Proměnné pro náhodné generování názvů a zadání. |  | ||||||
| 	jaka = ["Šachová", "Černá", "Větrná", "Dlouhá", "Křehká", "Rychlá", |  | ||||||
| 		"Zákeřná", "Fyzikální"] |  | ||||||
| 	co = ["kostka", "smršť", "díra", "zrada", "toulka", "tyč", |  | ||||||
| 		"úloha", "blecha"] |  | ||||||
| 	sloveso = ["Najděte", "Spočítejte", "Zapište", "Změřte", "Odhadněte"] |  | ||||||
| 	koho = ["délku", "počet", "množství", "dílky"] |  | ||||||
| 	ceho = ["všech", "správných", "konstatních", "zelených"] |  | ||||||
| 	jmeno = ["řešení", "tahů", "čísel", "kalhot", "koulí", "hadů"] |  | ||||||
| 	kde = ["na zemi", "ve vesmíru", "ve vzduchu", "na šňůře", "v letadle"] |  | ||||||
| 	obory = ["M", "F", "I", "O", "B"] |  | ||||||
| 	 |  | ||||||
| 	uloha = Uloha.objects.create( |  | ||||||
| 		nazev=": ".join([tema.nazev,  |  | ||||||
| 			"úloha {}.".format(kod)]), |  | ||||||
| 		nadproblem=tema, |  | ||||||
| 		stav=Problem.STAV_ZADANY, |  | ||||||
| 		zamereni=tema.zamereni, |  | ||||||
| 		autor=tema.autor, |  | ||||||
| 		garant=tema.garant, |  | ||||||
| 		kod=str(kod), |  | ||||||
| 		cislo_zadani=cislo, |  | ||||||
| 		cislo_reseni=cislo_se_vzorakem, |  | ||||||
| 		cislo_deadline=cislo_se_vzorakem, |  | ||||||
| 		max_body = rnd.randint(1, 8) |  | ||||||
| 	) |  | ||||||
| 	 |  | ||||||
| 	# Samotný obsah následně vzniklého Textu zadání |  | ||||||
| 	obsah = " ".join( |  | ||||||
| 		[rnd.choice(sloveso),  |  | ||||||
| 		rnd.choice(koho),  |  | ||||||
| 		rnd.choice(ceho),  |  | ||||||
| 		rnd.choice(jmeno),  |  | ||||||
| 		rnd.choice(kde)] |  | ||||||
| 		) |  | ||||||
| 	text_zadani = Text.objects.create( |  | ||||||
| 		na_web = obsah, |  | ||||||
| 		do_cisla = obsah, |  | ||||||
| 		) |  | ||||||
| 	zad = TextNode.objects.create(text = text_zadani, root=tema.temavcislenode_set.first().root) |  | ||||||
| 	uloha_zadani = UlohaZadaniNode.objects.create(uloha=uloha, first_child = zad, root=tema.temavcislenode_set.first().root) |  | ||||||
| 	uloha.ulohazadaninode = uloha_zadani |  | ||||||
| 
 |  | ||||||
| 	# Generování řešení a hodnocení k úloze |  | ||||||
| 	gen_reseni_ulohy(rnd, [cislo], uloha, len(resitele)//4, 1, |  | ||||||
| 					 resitele, resitele) |  | ||||||
| 
 |  | ||||||
| 	return uloha, uloha_zadani |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori, resitele): |  | ||||||
| 	logger.info('Generuji úlohy k tématům...') |  | ||||||
| 
 |  | ||||||
| 	# Ke každému ročníku si vezmeme příslušná čísla a témata |  | ||||||
| 	for rocnik, cisla, temata in zip(rocniky, rocnik_cisla, rocnik_temata): |  | ||||||
| 		# Do každého čísla nagenerujeme ke každému témátku pár úložek |  | ||||||
| 		for cislo in cisla: |  | ||||||
| 			print("Generuji úložky do {}-tého čísla".format(cislo.poradi)) |  | ||||||
| 			# Vzorák bude o dvě čísla dál |  | ||||||
| 			cislo_se_vzorakem = Cislo.objects.filter( |  | ||||||
| 					rocnik=rocnik, |  | ||||||
| 					poradi=str(int(cislo.poradi) + 2), |  | ||||||
| 					) |  | ||||||
| 			# 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í...) |  | ||||||
| 			# Tohle sice umožňuje vygenerovat vzorák do čísla dávno po konci témátka,  |  | ||||||
| 			# ale to nám pro jednoduchost nevadí. |  | ||||||
| 			if len(cislo_se_vzorakem) == 0: |  | ||||||
| 				cislo_se_vzorakem = cisla[-1] |  | ||||||
| 			else: |  | ||||||
| 				cislo_se_vzorakem = cislo_se_vzorakem.first() |  | ||||||
| 
 |  | ||||||
| 			for tema_node in all_children_of_type(cislo.cislonode, TemaVCisleNode): |  | ||||||
| 				tema = tema_node.tema |  | ||||||
| 					 |  | ||||||
| 				# 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, uz = gen_ulohy_tematu(rnd, organizatori, resitele, tema, kod, |  | ||||||
| 						cislo, cislo_se_vzorakem) |  | ||||||
| 
 |  | ||||||
| 					insert_last_child(tema_node, uz) |  | ||||||
| 				 |  | ||||||
| 					poc_op = rnd.randint(1, 4) |  | ||||||
| 					uvz = gen_vzoroveho_reseni_ulohy(rnd, organizatori,  |  | ||||||
| 						u, poc_op)  |  | ||||||
| 					 |  | ||||||
| 					# Najdeme správný TemaVCisleNode pro vložení vzoráku |  | ||||||
| 					res_tema_node = None; |  | ||||||
| 					for node in all_children(cislo_se_vzorakem.cislonode): |  | ||||||
| 						if isinstance(node, TemaVCisleNode):  |  | ||||||
| 							if node.tema == tema: |  | ||||||
| 								res_tema_node = node |  | ||||||
| 					if res_tema_node is None: |  | ||||||
| 						raise LookupError("Nenalezen Node pro vložení vzoráku") |  | ||||||
| 					insert_last_child(res_tema_node, uvz) |  | ||||||
| 					u.save() |  | ||||||
| 	return |  | ||||||
| 
 |  | ||||||
| def gen_novinky(rnd, organizatori): |  | ||||||
| 	logger.info('Generuji novinky...') |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 	jake = ["zábavné", "veselé", "dobrodružné", "skvělé"] |  | ||||||
| 	co = ["soustředění", "Fyziklání", "víkendové setkání"] |  | ||||||
| 	kde = ["na Šumavě", "v Praze", "u Plzně", "na Marsu"] |  | ||||||
| 	kdy = ["Zítra bude", "10. 10. 2020 bude", "V prosinci bude", "V létě bude"] |  | ||||||
| 
 |  | ||||||
| 	for i in range(5): |  | ||||||
| 		text_novinky = " ".join([rnd.choice(kdy), rnd.choice(kde), rnd.choice(jake), |  | ||||||
| 			rnd.choice(co)]) |  | ||||||
| 		novinka = Novinky.objects.create(id=i,autor=rnd.choice(organizatori), |  | ||||||
| 			text=(text_novinky+", těšíme se na vás!"),zverejneno=rnd.choice([True,False])) |  | ||||||
| 		novinka.save() |  | ||||||
| 	return |  | ||||||
| 
 |  | ||||||
| def otec_syn(otec, syn): |  | ||||||
| 	bratr = otec.first_child |  | ||||||
| 	syn.succ = bratr |  | ||||||
| 	otec.first_child = syn |  | ||||||
| 	syn.save() |  | ||||||
| 	otec.save() |  | ||||||
| 
 |  | ||||||
| def gen_clanek(rnd, organizatori, resitele): |  | ||||||
| 	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(organizatori), |  | ||||||
| 		kod='cl', |  | ||||||
| 		) |  | ||||||
| 	clanek.save() |  | ||||||
| 
 |  | ||||||
| 	reseni = m.Reseni.objects.create( |  | ||||||
| 		zverejneno=True, |  | ||||||
| 		) |  | ||||||
| 	reseni.resitele.add(rnd.choice(resitele)) |  | ||||||
| 	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 treenode.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)) |  | ||||||
| 
 |  | ||||||
| 	assert size >= 1 |  | ||||||
| 	# pevna pseudo-nahodnost |  | ||||||
| 	rnd = rnd or random.Random(x=42) |  | ||||||
| 
 |  | ||||||
| 	# static URL stranky |  | ||||||
| 	# FIXME: nakopirovat sem vsechny z produkcni databaze |  | ||||||
| 	s = Site.objects.filter(name="example.com") |  | ||||||
| 	f = FlatPage.objects.create(url="/", title="Seminář M&M", |  | ||||||
| 		content = "<p>Vítejte na stránce semináře MaM!</p>") |  | ||||||
| 	print(s) |  | ||||||
| 	f.sites.add(s[0]) |  | ||||||
| 	f.save() |  | ||||||
| 
 |  | ||||||
| 	# users |  | ||||||
| 	admin = User.objects.create_superuser(username='admin', email='', password='admin') |  | ||||||
| 	os_admin = Osoba.objects.create( |  | ||||||
| 		user=admin, jmeno='admin', prijmeni='admin', |  | ||||||
| 		prezdivka='admin', osloveni='', email='admin@admin.admin', |  | ||||||
| 		telefon='123 456 789', datum_narozeni=datetime.date(2000, 1, 1), |  | ||||||
| 		ulice='admin', mesto='admin', psc='100 00', |  | ||||||
| 		datum_registrace=datetime.date(2020, 9, 6) |  | ||||||
| 	) |  | ||||||
| 	or_admin = Organizator.objects.create( |  | ||||||
| 		osoba=os_admin, organizuje_od=None, organizuje_do=None, |  | ||||||
| 		strucny_popis_organizatora="Organizátor k uživateli Admin" |  | ||||||
| 	) |  | ||||||
| 
 |  | ||||||
| 	usernames = ['anet', 'bara', 'cyril', 'david', 'eva', 'filip'] |  | ||||||
| 	users = [] |  | ||||||
| 	for usr in usernames[:size]: |  | ||||||
| 		u = User.objects.create_user(username=usr, password=usr) |  | ||||||
| 		u.first_name = usr.capitalize() |  | ||||||
| 		u.save() |  | ||||||
| 		users.append(u) |  | ||||||
| 	print(users) |  | ||||||
| 
 |  | ||||||
| 	# skoly |  | ||||||
| 	skoly = gen_skoly() |  | ||||||
| 
 |  | ||||||
| 	# osoby |  | ||||||
| 	osoby = gen_osoby(rnd, size) |  | ||||||
| 
 |  | ||||||
| 	# resitele a organizatori |  | ||||||
| 	last_rocnik = 25 |  | ||||||
| 	organizatori = gen_organizatori(rnd, osoby, last_rocnik) |  | ||||||
| 	resitele = gen_resitele(rnd, osoby, skoly) |  | ||||||
| 
 |  | ||||||
| 	#generování novinek |  | ||||||
| 	novinky = gen_novinky(rnd, organizatori) |  | ||||||
| 
 |  | ||||||
| 	# prijemci |  | ||||||
| 	prijemci = gen_prijemci(rnd, osoby) |  | ||||||
| 
 |  | ||||||
| 	global zlinska |  | ||||||
| 	zlinska.kontaktni_osoba=rnd.choice(osoby) |  | ||||||
| 	zlinska.save() |  | ||||||
| 
 |  | ||||||
| 	# rocniky |  | ||||||
| 	rocniky = gen_rocniky(last_rocnik, size) |  | ||||||
| 
 |  | ||||||
| 	# 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) |  | ||||||
| 
 |  | ||||||
| 	# generování obyčejných úloh do čísel |  | ||||||
| 	gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size) |  | ||||||
| 
 |  | ||||||
| 	# generování témat, zatím v prvních třech číslech po jednom |  | ||||||
| 	# 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 = Rocnik.objects.filter(rocnik = 23).first() |  | ||||||
| 	dlouhe_tema = gen_dlouhe_tema(rnd, organizatori, rocnik, "Strašně dlouhé téma", |  | ||||||
| 		"MFI", 8) |  | ||||||
| 
 |  | ||||||
| 	# generování úloh k tématům ve všech číslech |  | ||||||
| 	gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori, resitele) |  | ||||||
| 
 |  | ||||||
| 	#generování soustředění |  | ||||||
| 	soustredeni = gen_soustredeni(rnd, resitele, organizatori) |  | ||||||
| 
 |  | ||||||
| 	#generování konfer |  | ||||||
| 	konfery = gen_konfery(size, rnd, organizatori, resitele, soustredeni) |  | ||||||
| 
 |  | ||||||
| 	# vytvoreni pdf ke korekturam |  | ||||||
| 	create_test_pdf(rnd, organizatori) |  | ||||||
| 
 |  | ||||||
| 			# TODO: nastavi správně, kolik se čeho generuje, aby rozsahy přibližně odpovídaly |  | ||||||
| 			# FIXME: misto typu ruzne typy objektu a vnoreni do sebe (Tom nechápe, co je tímto fixme míněno) |  | ||||||
| 			# TODO: vytvorit temata s ruznymi vlakny |  | ||||||
| 			# TODO: nagenerovat starsim rocnikum pohadku |  | ||||||
| 			# TODO: nagenerovat články |  | ||||||
| 			# TODO: vecpat obrázky všude, kde to jde |  | ||||||
| 			# 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, organizatori, resitele) |  | ||||||
| 
 |  | ||||||
| 			# TODO: přidat články včetně zařazení do struktury treenodů, |  | ||||||
| 			#	a následně otestovat konsistency check databáze z utils.py |  | ||||||
| 			# 	pomocí stránky /stav |  | ||||||
| 
 |  | ||||||
| 	# obecné nastavení semináře, musí být už přidané ročníky a čísla, jinak se nastaví divně |  | ||||||
| 	nastaveni = Nastaveni.objects.create( |  | ||||||
| 			aktualni_cislo = Cislo.objects.all()[1]) |  | ||||||
							
								
								
									
										387
									
								
								seminar/utils.py
									
									
									
									
									
								
							
							
						
						|  | @ -1,387 +0,0 @@ | ||||||
| import datetime |  | ||||||
| import decimal |  | ||||||
| 
 |  | ||||||
| from django.contrib.auth import get_user_model |  | ||||||
| from django.contrib.auth.decorators import permission_required, \ |  | ||||||
| 	user_passes_test |  | ||||||
| from django import views as DjangoViews |  | ||||||
| 
 |  | ||||||
| from django.db import transaction |  | ||||||
| 
 |  | ||||||
| from django.contrib.auth.models import AnonymousUser |  | ||||||
| from django.contrib.contenttypes.models import ContentType |  | ||||||
| from django.core.exceptions import ObjectDoesNotExist |  | ||||||
| 
 |  | ||||||
| import logging |  | ||||||
| 
 |  | ||||||
| import seminar.models as m |  | ||||||
| import treenode.treelib as t |  | ||||||
| 
 |  | ||||||
| logger = logging.getLogger(__name__) |  | ||||||
| 
 |  | ||||||
| org_required = permission_required('auth.org') |  | ||||||
| resitel_required = permission_required('auth.resitel') |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # inspirováno django.contrib.auth.decorators permission_required |  | ||||||
| def check_perms(user): |  | ||||||
| 	if user.has_perms(('auth.resitel',)): |  | ||||||
| 		return True |  | ||||||
| 	if user.has_perms(('auth.org',)): |  | ||||||
| 		return True |  | ||||||
| 	return False |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| resitel_or_org_required = user_passes_test(check_perms) |  | ||||||
| 
 |  | ||||||
| User = get_user_model() |  | ||||||
| # Není to úplně hezké, ale budeme doufat, že to je funkční... |  | ||||||
| User.je_org = property(lambda self: self.has_perm('auth.org')) |  | ||||||
| User.je_resitel = property(lambda self: self.has_perm('auth.resitel')) |  | ||||||
| AnonymousUser.je_org = False |  | ||||||
| AnonymousUser.je_resitel = False |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def vzorecek_na_prepocet(body, resitelu): |  | ||||||
| 	""" Vzoreček na přepočet plných bodů na parciálni, když má řešení více řešitelů. """ |  | ||||||
| 	return body * 3 / (resitelu + 2) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def inverze_vzorecku_na_prepocet(body: decimal.Decimal, resitelu) -> decimal.Decimal: |  | ||||||
| 	""" Vzoreček na přepočet parciálních bodů na plné, když má řešení více řešitelů. """ |  | ||||||
| 	return round(body * (resitelu + 2) / 3, 1) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def histogram(seznam): |  | ||||||
| 	d = {} |  | ||||||
| 	for i in seznam: |  | ||||||
| 		if i not in d: |  | ||||||
| 			d[i] = 0 |  | ||||||
| 		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')) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 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(): |  | ||||||
| 	"""Funkce pro hledání nekonzistencí v databázi a dalších nežádoucích stavů webu/databáze. |  | ||||||
| 
 |  | ||||||
| 	Nijak nesouvisí s Problémy zadanými řešitelům.""" |  | ||||||
| 	# FIXME: přejmenovat funkci? |  | ||||||
| 	# FIXME: Tak, jak je napsaná, asi spíš patří někam k views a ne do utils (?) |  | ||||||
| 	problemy = [] |  | ||||||
| 
 |  | ||||||
| 	# Pomocna fce k formatovani problemovych hlasek |  | ||||||
| 	def prb(cls, msg, objs=None): |  | ||||||
| 		s = '<b>%s:</b> %s' % (cls.__name__, msg) |  | ||||||
| 		if objs: |  | ||||||
| 			s += ' [' |  | ||||||
| 			for o in objs: |  | ||||||
| 				try: |  | ||||||
| 					url = o.admin_url() |  | ||||||
| 				except: |  | ||||||
| 					url = None |  | ||||||
| 				if url: |  | ||||||
| 					s += '<a href="%s">%s</a>, ' % (url, o.pk,) |  | ||||||
| 				else: |  | ||||||
| 					s += '%s, ' % (o.pk,) |  | ||||||
| 			s = s[:-2] + ']' |  | ||||||
| 		problemy.append(s) |  | ||||||
| 
 |  | ||||||
| 	# Duplicita jmen |  | ||||||
| 	jmena = {} |  | ||||||
| 	for r in m.Resitel.objects.all(): |  | ||||||
| 		j = r.osoba.plne_jmeno() |  | ||||||
| 		if j not in jmena: |  | ||||||
| 			jmena[j] = [] |  | ||||||
| 		jmena[j].append(r) |  | ||||||
| 	for j in jmena: |  | ||||||
| 		if len(jmena[j]) > 1: |  | ||||||
| 			prb(m.Resitel, 'Duplicitní jméno "%s"' % (j,), jmena[j]) |  | ||||||
| 
 |  | ||||||
| 	# Data maturity a narození |  | ||||||
| 	for r in m.Resitel.objects.all(): |  | ||||||
| 		if not r.rok_maturity: |  | ||||||
| 			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): |  | ||||||
| 			prb(m.Resitel, 'Podezřelé datum narození', [r]) |  | ||||||
| #		if not r.email: |  | ||||||
| #			prb(Resitel, u'Neznámý email', [r]) |  | ||||||
| 
 |  | ||||||
| 	## Kontroly konzistence databáze a TreeNodů |  | ||||||
| 
 |  | ||||||
| 	# Články |  | ||||||
| 	for clanek in m.Clanek.objects.all(): |  | ||||||
| 		# 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 |  | ||||||
| 		return m.Resitel.objects.filter(rok_maturity__gte=rocnik.druhy_rok(), |  | ||||||
| 										reseni__hodnoceni__deadline_body__cislo__rocnik=rocnik).distinct() |  | ||||||
| 	else:  # filtrujeme podle ročníku i čísla |  | ||||||
| 		return m.Resitel.objects.filter(rok_maturity__gte=rocnik.druhy_rok(), |  | ||||||
| 										reseni__hodnoceni__deadline_body__cislo__rocnik=rocnik, |  | ||||||
| 										reseni__hodnoceni__deadline_body__cislo__poradi__lte=cislo.poradi).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).filter(rok_maturity__gte=letos.druhy_rok()) |  | ||||||
| 	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().filter(rok_maturity__gte=letos.druhy_rok()) |  | ||||||
| 
 |  | ||||||
| def viewMethodSwitch(get, post): |  | ||||||
| 	""" |  | ||||||
| 	Vrátí view, který zavolá různé jiné views podle toho, kterou metodou je zavolán. |  | ||||||
| 
 |  | ||||||
| 	Inspirováno https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#an-alternative-better-solution, jen jsem to udělal genericky. |  | ||||||
| 
 |  | ||||||
| 	Parametry: |  | ||||||
| 		post	view pro metodu POST |  | ||||||
| 		get	view pro metodu GET |  | ||||||
| 	 |  | ||||||
| 	V obou případech se míní už view jakožto funkce, takže u class-based views se už má použít .as_view() |  | ||||||
| 
 |  | ||||||
| 	TODO: Podpora i pro metodu HEAD? A možná i pro FILES? |  | ||||||
| 	""" |  | ||||||
| 
 |  | ||||||
| 	theGetView = get |  | ||||||
| 	thePostView = post |  | ||||||
| 
 |  | ||||||
| 	class NewView(DjangoViews.View): |  | ||||||
| 		def get(self, request, *args, **kwargs): |  | ||||||
| 			return theGetView(request, *args, **kwargs) |  | ||||||
| 		def post(self, request, *args, **kwargs): |  | ||||||
| 			return thePostView(request, *args, **kwargs) |  | ||||||
| 	 |  | ||||||
| 	return NewView.as_view() |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def sync_skoly(base_url): |  | ||||||
| 	"""Stáhne všechny školy z mamwebu na adrese <base_url> a uloží je do databáze""" |  | ||||||
| 	from django.urls import reverse |  | ||||||
| 	full_url = base_url.rstrip('/') + reverse('export_skoly') |  | ||||||
| 	import requests |  | ||||||
| 	from django.core import serializers |  | ||||||
| 	json =  requests.get(full_url, stream=True).content |  | ||||||
| 	for skola in serializers.deserialize('json', json): |  | ||||||
| 		skola.save() |  | ||||||
| 
 |  | ||||||
| @transaction.atomic |  | ||||||
| def merge_resitele(cilovy, zdrojovy): |  | ||||||
| 	"""Spojí dva řešitelské objekty do cílového. |  | ||||||
| 
 |  | ||||||
| 	Pojmenování "zdrojový" je silně nepřiléhající, ale co už…""" |  | ||||||
| 
 |  | ||||||
| 	# Postup: |  | ||||||
| 	# Sjednotit / upravit informace cílového řešitele |  | ||||||
| 	print('Upravuji data modelu') |  | ||||||
| 	fieldy_shoda = ['skola', 'rok_maturity', 'zasilat', 'zasilat_cislo_emailem', 'zasilat_cislo_papirove'] |  | ||||||
| 	 |  | ||||||
| 	for f in fieldy_shoda: |  | ||||||
| 		zf = getattr(zdrojovy, f) |  | ||||||
| 		cf = getattr(cilovy, f) |  | ||||||
| 		if cf == zf: |  | ||||||
| 			print(f' Údaj {f} je shodný ({zf})') |  | ||||||
| 		else: |  | ||||||
| 			if zf is None: |  | ||||||
| 				print(f' Údaj {f} je pouze v cílovém, používám') |  | ||||||
| 				continue |  | ||||||
| 			if cf is None: |  | ||||||
| 				setattr(cilovy, f, zf) |  | ||||||
| 				cilovy.poznamka += f'\nDEBUG: Merge: doplnéný údaj {f} ze zdrojového: {zf}' |  | ||||||
| 				print(f" Přiřazuji {f} ze zdrojového: {zf}") |  | ||||||
| 				continue |  | ||||||
| 			# Jsou fakt různé… |  | ||||||
| 			# FIXME: chybí možnost na vlastní úpravu… |  | ||||||
| 			verdikt = input(f"\n\n Údaj {f} se u řešitele {cilovy} ({cilovy.id}) liší:\n  Zdrojový: {zf}\n  Cílový: {cf}\n Který použít, [z]drojový, [c]ílový? ") |  | ||||||
| 			verdikt = verdikt[0].casefold() |  | ||||||
| 			if verdikt == 'z': |  | ||||||
| 				setattr(cilovy, f, zf) |  | ||||||
| 				cilovy.poznamka += f'\nDEBUG: Merge: pro {f} použit údaj {zf} (zdrojový), nepoužit {cf} (cílový)' |  | ||||||
| 			elif verdikt == 'c': |  | ||||||
| 				cilovy.poznamka += f'\nDEBUG: Merge: pro {f} použit údaj {cf} (cílový), nepoužit {zf} (zdrojový)' |  | ||||||
| 			else: raise ValueError('Špatná odpověď, řešitel pravděpodobně neuložen') |  | ||||||
| 	# poznámku chceme nezahodit… |  | ||||||
| 	cilovy.poznamka += f'\nDEBUG: Merge: Původní poznámka: {zdrojovy.poznamka}' |  | ||||||
| 	print(f' Výsledný řešitel: {cilovy.__dict__}, ukládám') |  | ||||||
| 	cilovy.save() |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 	# Přepojit všechny vazby ze zdrojového na cílového |  | ||||||
| 	print('Přepojuji vazby') |  | ||||||
| 	# Vazby: Škola (hotovo), Řešení_Řešitelé, Konfery_Účastníci, Soustředění_Účastníci, Osoba (vyřeší se později, nejde přepojit) |  | ||||||
| 	ct = m.Reseni_Resitele.objects.filter(resitele=zdrojovy).update(resitele=cilovy) |  | ||||||
| 	print(f' Přepojeno {ct} řešení') |  | ||||||
| 	ct = m.Konfery_Ucastnici.objects.filter(resitel=zdrojovy).update(resitel=cilovy) |  | ||||||
| 	print(f' Přepojeno {ct} konfer') |  | ||||||
| 	ct = m.Soustredeni_Ucastnici.objects.filter(resitel=zdrojovy).update(resitel=cilovy) |  | ||||||
| 	print(f' Přepojeno {ct} sousů') |  | ||||||
| 
 |  | ||||||
| 	# Teď by na zdrojovém řešiteli nemělo nic viset, smazat ho, pamatujíce si jeho Osobu |  | ||||||
| 	zdrosoba = zdrojovy.osoba |  | ||||||
| 	print(f'Mažu zdrojového řešitele {zdrojovy.__dict__}') |  | ||||||
| 	zdrojovy.delete() |  | ||||||
| 	# Spojit osoby (separátní funkce). |  | ||||||
| 	merge_osoby(cilovy.osoba, zdrosoba) |  | ||||||
| 
 |  | ||||||
| 	input("Potvrdit transakci řešitelů (^C pro zrušení) ") |  | ||||||
| 
 |  | ||||||
| @transaction.atomic |  | ||||||
| def merge_osoby(cilova, zdrojova): |  | ||||||
| 	""" Spojí dvě osoby do cílové |  | ||||||
| 
 |  | ||||||
| 	Nehlídá omezení typu "max 1 řešitel na osobu", to by měla hlídat databáze (OneToOneField).""" |  | ||||||
| 	# Sjednocení dat |  | ||||||
| 	print('Sjednocuji data osob') |  | ||||||
| 	# ID, User neřešíme, poznámku vyřešíme separátně. |  | ||||||
| 	fieldy = ['datum_narozeni', 'datum_registrace', 'datum_souhlasu_udaje', |  | ||||||
| 			'datum_souhlasu_zasilani', 'email', 'foto', 'jmeno', 'mesto', |  | ||||||
| 			'osloveni', 'prezdivka', 'prijmeni', 'psc', 'stat', 'telefon', 'ulice'] |  | ||||||
| 	for f in fieldy: |  | ||||||
| 		zf = getattr(zdrojova, f) |  | ||||||
| 		cf = getattr(cilova, f) |  | ||||||
| 		if cf == zf: |  | ||||||
| 			print(f' Údaj {f} je shodný ({zf})') |  | ||||||
| 		else: |  | ||||||
| 			if zf is None: |  | ||||||
| 				print(f' Údaj {f} je pouze v cílové, používám') |  | ||||||
| 				continue |  | ||||||
| 			if cf is None: |  | ||||||
| 				setattr(cilova, f, zf) |  | ||||||
| 				cilova.poznamka += f'\nDEBUG: Merge: doplnéný údaj {f} ze zdrojové: {zf}' |  | ||||||
| 				print(f" Přiřazuji {f} ze zdrojové: {zf}") |  | ||||||
| 				continue |  | ||||||
| 			# Jsou fakt různé… |  | ||||||
| 			# FIXME: chybí možnost na vlastní úpravu… |  | ||||||
| 			verdikt = input(f"\n\n Údaj {f} se u osoby {cilova} ({cilova.id}) liší:\n  Zdrojový: {zf}\n  Cílový: {cf}\n Který použít, [z]drojový, [c]ílový? ") |  | ||||||
| 			verdikt = verdikt[0].casefold() |  | ||||||
| 			if verdikt == 'z': |  | ||||||
| 				setattr(cilova, f, zf) |  | ||||||
| 				cilova.poznamka += f'\nDEBUG: Merge: pro {f} použit údaj {zf} (zdrojová), nepoužit {cf} (cílová)' |  | ||||||
| 			elif verdikt == 'c': |  | ||||||
| 				cilova.poznamka += f'\nDEBUG: Merge: pro {f} použit údaj {cf} (cílová), nepoužit {zf} (zdrojová)' |  | ||||||
| 			else: raise ValueError('Špatná odpověď, řešitel pravděpodobně neuložen') |  | ||||||
| 	# poznámku chceme nezahodit… |  | ||||||
| 	cilova.poznamka += f'\nDEBUG: Merge: Původní poznámka: {zdrojova.poznamka}' |  | ||||||
| 	print(f' Výsledná osoba: {cilova.__dict__}, ukládám') |  | ||||||
| 	cilova.save() |  | ||||||
| 
 |  | ||||||
| 	# Vazby: Řešitel, User, Příjemce, Organizátor, Škola.kontaktní_osoba |  | ||||||
| 	print('Přepojuji vazby') |  | ||||||
| 	ct = m.Skola.objects.filter(kontaktni_osoba=zdrojova).update(kontaktni_osoba=cilova) |  | ||||||
| 	print(f' Přepojeno {ct} kontaktních osob') |  | ||||||
| 	# Ostatní vazby vyřeší OneToOneFieldy, ale někdy nemusí existovat… |  | ||||||
| 	ct = m.Resitel.objects.filter(osoba=zdrojova).update(osoba=cilova) |  | ||||||
| 	print(f' Přepojeno {ct} řešitelů') |  | ||||||
| 	ct = m.Prijemce.objects.filter(osoba=zdrojova).update(osoba=cilova) |  | ||||||
| 	print(f' Přepojeno {ct} příjemců') |  | ||||||
| 	ct = m.Organizator.objects.filter(osoba=zdrojova).update(osoba=cilova) |  | ||||||
| 	print(f' Přepojeno {ct} organizátorů') |  | ||||||
| 	# Uživatelé vedou opačným směrem, radši chceme zkontrolovat, že jsou různí ručně: |  | ||||||
| 	if zdrojova.user != cilova.user: |  | ||||||
| 		# Jeden z nich může být nenastavený… |  | ||||||
| 		if zdrojova.user is None: |  | ||||||
| 			print('Uživatel je již v cílové osobě') |  | ||||||
| 		elif cilova.user is None: |  | ||||||
| 			print('Používám uživatele zdrojové osoby') |  | ||||||
| 			cilova.user = zdrojova.user |  | ||||||
| 			# Teď nemůžeme uložit, protože kolize uživatelů. Ukládat cílovou budeme až po smazání zdrojové. |  | ||||||
| 		else: raise ValueError('Osoby mají obě uživatele, radši padám') |  | ||||||
| 	 |  | ||||||
| 	# Uložení a mazání |  | ||||||
| 	print(f'Mažu zdrojovou osobu {zdrojova.__dict__}') |  | ||||||
| 	zdrojova.delete() |  | ||||||
| 	print(f'Ukládám cílovou osobu {cilova.__dict__}') |  | ||||||
| 	cilova.save() |  | ||||||
| 
 |  | ||||||
| 	input("Potvrdit transakci osob (^C pro zrušení) ") |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|  | @ -1,4 +0,0 @@ | ||||||
| from .views_all import * |  | ||||||
| 
 |  | ||||||
| # Dočsasné views |  | ||||||
| from .docasne import * |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| from django.urls import path | from django.urls import path | ||||||
| 
 | 
 | ||||||
| from seminar.utils import org_required, resitel_or_org_required | from personalni.utils import org_required, resitel_or_org_required | ||||||
| from .views import SifrovackaView, SifrovackaListView, NapovedaView, NapovedaListView, PreskoceniView | from .views import SifrovackaView, SifrovackaListView, NapovedaView, NapovedaListView, PreskoceniView | ||||||
| 
 | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| from django.urls import reverse | from django.urls import reverse | ||||||
| from django.views.generic import FormView, ListView | from django.views.generic import FormView, ListView | ||||||
| 
 | 
 | ||||||
| from seminar.views import formularOKView | from various.views.pomocne import formularOKView | ||||||
| from .forms import SifrovackaForm, NapovedaForm | from .forms import SifrovackaForm, NapovedaForm | ||||||
| from .models import OdpovedUcastnika, SpravnaOdpoved, Napoveda, NapovezenoUcastnikovi | from .models import OdpovedUcastnika, SpravnaOdpoved, Napoveda, NapovezenoUcastnikovi | ||||||
| from personalni.models import Resitel | from personalni.models import Resitel | ||||||
|  |  | ||||||
							
								
								
									
										69
									
								
								soustredeni/testutils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,69 @@ | ||||||
|  | import logging | ||||||
|  | import datetime | ||||||
|  | import random | ||||||
|  | from typing import Sequence | ||||||
|  | 
 | ||||||
|  | import lorem | ||||||
|  | 
 | ||||||
|  | from .models import Soustredeni, Konfera | ||||||
|  | import seminar.models.tvorba as am | ||||||
|  | import personalni.models as pm | ||||||
|  | 
 | ||||||
|  | logger = logging.getLogger(__name__) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def gen_soustredeni( | ||||||
|  | 		size: int, | ||||||
|  | 		resitele: Sequence[pm.Resitel], | ||||||
|  | 		organizatori: Sequence[pm.Organizator], | ||||||
|  | 		rnd: random.Random = None, | ||||||
|  | ) -> Sequence[Soustredeni]: | ||||||
|  | 	logger.info('Generuji soustředění (size={})...') | ||||||
|  | 	rnd = rnd or random.Random(x=42) | ||||||
|  | 
 | ||||||
|  | 	soustredeni = [] | ||||||
|  | 	for _ in range(1, 10):  # FIXME Tu range si změňte jak chcete, nevím, co přesně znamená size (asi Anet?) | ||||||
|  | 		datum_zacatku = datetime.date(rnd.randint(2000, 2020), rnd.randint(1, 12), rnd.randint(1, 28)) | ||||||
|  | 		working_sous = Soustredeni.objects.create( | ||||||
|  | 			rocnik=am.Rocnik.objects.order_by('?').first(), | ||||||
|  | 			verejne_db=rnd.choice([True, False]), | ||||||
|  | 			misto=rnd.choice(['Kremrolovice', 'Indiánov', 'U zmzliny', 'Vafláreň', 'Větrník', 'Horní Rakvička', 'Dolní cheesecake']), | ||||||
|  | 			typ=rnd.choice(['jarni', 'podzimni', 'vikend']), | ||||||
|  | 			datum_zacatku=datum_zacatku, | ||||||
|  | 			datum_konce=datum_zacatku + datetime.timedelta(days=7)) | ||||||
|  | 		ucastnici = rnd.sample(resitele, min(len(resitele), 20)) | ||||||
|  | 		working_sous.ucastnici.set(ucastnici) | ||||||
|  | 		orgove_vyber = rnd.sample(organizatori, min(len(organizatori), 20)) | ||||||
|  | 		working_sous.organizatori.set(orgove_vyber) | ||||||
|  | 		working_sous.save() | ||||||
|  | 		soustredeni.append(working_sous) | ||||||
|  | 	return soustredeni | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def gen_konfery( | ||||||
|  | 		size: int, | ||||||
|  | 		organizatori: Sequence[pm.Organizator], | ||||||
|  | 		soustredeni: Sequence[Soustredeni], | ||||||
|  | 		resitele: Sequence[pm.Resitel] = None, | ||||||
|  | 		rnd: random.Random = None, | ||||||
|  | ) -> Sequence[Konfera]: | ||||||
|  | 	logger.info('Generuji konfery (size={})...'.format(size)) | ||||||
|  | 	rnd = rnd or random.Random(x=42) | ||||||
|  | 
 | ||||||
|  | 	konfery = [] | ||||||
|  | 	for _ in range(1, size):  # FIXME Tu range si změňte jak chcete, nevím, co přesně znamená size (asi Anet?) | ||||||
|  | 		# Anet: size je parametr udávající velikost testovacích dat a dá se pomocí ní škálovat, | ||||||
|  | 		# kolik dat se nageneruje | ||||||
|  | 		konfera = Konfera.objects.create( | ||||||
|  | 			nazev=rnd.choice(['Pozorování', 'Zkoumání', 'Modelování', 'Počítání', 'Zkoušení']) + rnd.choice([' vlastností', ' jevů', ' charakteristik']) + rnd.choice([' vektorových prostorů', ' kinetické terorie látek', ' molekulární biologie', ' syntentických stromů']), | ||||||
|  | 			anotace=lorem.paragraph(), | ||||||
|  | 			abstrakt=lorem.paragraph(), | ||||||
|  | 			garant=rnd.choice(organizatori), | ||||||
|  | 			soustredeni=rnd.choice(soustredeni), | ||||||
|  | 			typ_prezentace=rnd.choice(['veletrh', 'prezentace'])) | ||||||
|  | 		ucastnici_sous = resitele if resitele else list(konfera.soustredeni.ucastnici.all()) | ||||||
|  | 		ucastnici = rnd.sample(ucastnici_sous, min(len(ucastnici_sous), rnd.randint(3, 6))) | ||||||
|  | 		konfera.ucastnici.set(ucastnici) | ||||||
|  | 		konfera.save() | ||||||
|  | 		konfery.append(konfera) | ||||||
|  | 	return konfery | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| from django.urls import path, include | from django.urls import path, include | ||||||
| from . import views | from . import views | ||||||
| from seminar.utils import org_required | from personalni.utils import org_required | ||||||
| 
 | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
| 	path( | 	path( | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ import subprocess | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
| import http | import http | ||||||
| 
 | 
 | ||||||
| from seminar.views import obalkyView | import personalni.views | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class SoustredeniListView(generic.ListView): | class SoustredeniListView(generic.ListView): | ||||||
|  | @ -34,7 +34,7 @@ class SoustredeniListView(generic.ListView): | ||||||
| 
 | 
 | ||||||
| def soustredeniObalkyView(request, soustredeni): | def soustredeniObalkyView(request, soustredeni): | ||||||
| 	soustredeni = get_object_or_404(Soustredeni, id=soustredeni) | 	soustredeni = get_object_or_404(Soustredeni, id=soustredeni) | ||||||
| 	return obalkyView(request, soustredeni.ucastnici.all()) | 	return personalni.views.obalkyView(request, soustredeni.ucastnici.all()) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class SoustredeniUcastniciBaseView(generic.ListView): | class SoustredeniUcastniciBaseView(generic.ListView): | ||||||
|  | @ -93,7 +93,7 @@ def soustredeniStvrzenkyView(request, soustredeni): | ||||||
| 		with open(tempdir / "stvrzenky.tex", "w") as texfile: | 		with open(tempdir / "stvrzenky.tex", "w") as texfile: | ||||||
| 			texfile.write(tex.decode()) | 			texfile.write(tex.decode()) | ||||||
| 
 | 
 | ||||||
| 		shutil.copy(find('images/logomm.pdf'), tempdir) | 		shutil.copy(find('soustredeni/logomm.pdf'), tempdir) | ||||||
| 		subprocess.call(["pdflatex", "stvrzenky.tex"], cwd = tempdir, stdout=subprocess.DEVNULL) | 		subprocess.call(["pdflatex", "stvrzenky.tex"], cwd = tempdir, stdout=subprocess.DEVNULL) | ||||||
| 
 | 
 | ||||||
| 		with open(tempdir / "stvrzenky.pdf", "rb") as pdffile: | 		with open(tempdir / "stvrzenky.pdf", "rb") as pdffile: | ||||||
|  |  | ||||||
|  | @ -1,4 +1,6 @@ | ||||||
| from django.contrib import admin | from django.contrib import admin | ||||||
|  | from django.db import models | ||||||
|  | from django.forms import widgets | ||||||
| 
 | 
 | ||||||
| from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter | from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter | ||||||
| 
 | 
 | ||||||
|  | @ -86,3 +88,12 @@ class TextNodeAdmin(PolymorphicChildModelAdmin): | ||||||
| 	show_in_index = True | 	show_in_index = True | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class TextAdminInline(admin.TabularInline): | ||||||
|  | 	model = m.Text | ||||||
|  | 	formfield_overrides = { | ||||||
|  | 		models.TextField: {'widget': widgets.TextInput} | ||||||
|  | 	} | ||||||
|  | 	exclude = ['text_zkraceny_set', 'text_zkraceny'] | ||||||
|  | 
 | ||||||
|  | admin.site.register(m.Text) | ||||||
|  | admin.site.register(m.Obrazek) | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| {% extends "seminar/archiv/base.html" %} | {% extends "tvorba/archiv/base.html" %} | ||||||
| {% load static %} | {% load static %} | ||||||
| 
 | 
 | ||||||
| {% block custom_css %} | {% block custom_css %} | ||||||
|  |  | ||||||
|  | @ -1,20 +1,20 @@ | ||||||
| from django.contrib import admin | from django.contrib import admin | ||||||
| from django.db import models | from django.forms import ModelForm | ||||||
| from django.forms import widgets, ModelForm |  | ||||||
| from django.core.exceptions import ValidationError | from django.core.exceptions import ValidationError | ||||||
| 
 | 
 | ||||||
| from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter | from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter | ||||||
| from solo.admin import SingletonModelAdmin |  | ||||||
| from django.utils.safestring import mark_safe | from django.utils.safestring import mark_safe | ||||||
| 
 | 
 | ||||||
| # Todo: reversion | # Todo: reversion | ||||||
| 
 | 
 | ||||||
| import seminar.models as m | import soustredeni.models | ||||||
| 
 | 
 | ||||||
| admin.site.register(m.Rocnik) | from seminar.models.tvorba import Rocnik, ZmrazenaVysledkovka, Deadline, Uloha, Problem, Tema, Clanek, Cislo | ||||||
| admin.site.register(m.ZmrazenaVysledkovka) |  | ||||||
| 
 | 
 | ||||||
| @admin.register(m.Deadline) | admin.site.register(Rocnik) | ||||||
|  | admin.site.register(ZmrazenaVysledkovka) | ||||||
|  | 
 | ||||||
|  | @admin.register(Deadline) | ||||||
| class DeadlineAdmin(admin.ModelAdmin): | class DeadlineAdmin(admin.ModelAdmin): | ||||||
| 	actions = ['pregeneruj_vysledkovku'] | 	actions = ['pregeneruj_vysledkovku'] | ||||||
| 
 | 
 | ||||||
|  | @ -28,42 +28,44 @@ class DeadlineAdmin(admin.ModelAdmin): | ||||||
| 		# Boilerplate: potřebujeme nějakou permission, protože nějaká haluz v Djangu… | 		# Boilerplate: potřebujeme nějakou permission, protože nějaká haluz v Djangu… | ||||||
| 		return request.user.is_superuser | 		return request.user.is_superuser | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| class DeadlineAdminInline(admin.TabularInline): | class DeadlineAdminInline(admin.TabularInline): | ||||||
| 	model = m.Deadline | 	model = Deadline | ||||||
| 	extra = 0 | 	extra = 0 | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| class CisloForm(ModelForm): | class CisloForm(ModelForm): | ||||||
| 	class Meta: | 	class Meta: | ||||||
| 		model = m.Cislo | 		model = Cislo | ||||||
| 		fields = '__all__' | 		fields = '__all__' | ||||||
| 
 | 
 | ||||||
| 	def clean(self): | 	def clean(self): | ||||||
| 		if self.cleaned_data.get('verejne_db') == False: | 		if self.cleaned_data.get('verejne_db') == False: | ||||||
| 			return self.cleaned_data | 			return self.cleaned_data | ||||||
| 		# cn = m.CisloNode.objects.get(cislo=self.instance) | 		# cn = CisloNode.objects.get(cislo=self.instance) | ||||||
| 		# errors = [] | 		# errors = [] | ||||||
| 		# for ch in tl.all_children(cn): | 		# for ch in tl.all_children(cn): | ||||||
| 		# 	if isinstance(ch, m.TemaVCisleNode): | 		# 	if isinstance(ch, TemaVCisleNode): | ||||||
| 		# 		if ch.tema.stav not in \ | 		# 		if ch.tema.stav not in \ | ||||||
| 		# 			(m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): | 		# 			(Problem.STAV_ZADANY, Problem.STAV_VYRESENY): | ||||||
| 		# 			errors.append(ValidationError('Téma %(tema)s není zadané ani vyřešené', params={'tema':ch.tema})) | 		# 			errors.append(ValidationError('Téma %(tema)s není zadané ani vyřešené', params={'tema':ch.tema})) | ||||||
| 		# | 		# | ||||||
| 		# 	if isinstance(ch, m.UlohaZadaniNode) or isinstance(ch, m.UlohaVzorakNode): | 		# 	if isinstance(ch, UlohaZadaniNode) or isinstance(ch, UlohaVzorakNode): | ||||||
| 		# 		if ch.uloha.stav not in \ | 		# 		if ch.uloha.stav not in \ | ||||||
| 		# 			(m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): | 		# 			(Problem.STAV_ZADANY, Problem.STAV_VYRESENY): | ||||||
| 		# 			errors.append(ValidationError('Úloha %(uloha)s není zadaná ani vyřešená', params={'uloha':ch.uloha})) | 		# 			errors.append(ValidationError('Úloha %(uloha)s není zadaná ani vyřešená', params={'uloha':ch.uloha})) | ||||||
| 		# 	if isinstance(ch, m.ReseniNode): | 		# 	if isinstance(ch, ReseniNode): | ||||||
| 		# 		for problem in ch.reseni.problem_set: | 		# 		for problem in ch.reseni.problem_set: | ||||||
| 		# 			if problem not in \ | 		# 			if problem not in \ | ||||||
| 		# 				(m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): | 		# 				(Problem.STAV_ZADANY, Problem.STAV_VYRESENY): | ||||||
| 		# 				errors.append(ValidationError('Problém %s není zadaný ani vyřešený', code=problem)) | 		# 				errors.append(ValidationError('Problém %s není zadaný ani vyřešený', code=problem)) | ||||||
| 		# if errors: | 		# if errors: | ||||||
| 		# 	errors.append(ValidationError(mark_safe('<b>Pokud chceš učinit všechny problémy, co nejsou zadané ani vyřešené, zadanými a číslo zveřejnit, můžeš to udělat pomocí akce v <a href="/admin/seminar/cislo">seznamu čísel</a></b>'))) | 		# 	errors.append(ValidationError(mark_safe('<b>Pokud chceš učinit všechny problémy, co nejsou zadané ani vyřešené, zadanými a číslo zveřejnit, můžeš to udělat pomocí akce v <a href="/admin/seminar/cislo">seznamu čísel</a></b>'))) | ||||||
| 		# 	raise ValidationError(errors) | 		# 	raise ValidationError(errors) | ||||||
| 
 | 
 | ||||||
| 		errors = [] | 		errors = [] | ||||||
| 		for ch in m.Uloha.objects.filter(cislo_zadani=self.instance): | 		for ch in Uloha.objects.filter(cislo_zadani=self.instance): | ||||||
| 			if ch.stav not in (m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): | 			if ch.stav not in (Problem.STAV_ZADANY, Problem.STAV_VYRESENY): | ||||||
| 				errors.append( | 				errors.append( | ||||||
| 					ValidationError('Úloha %(uloha)s není zadaná ani vyřešená', params={'uloha': ch})) | 					ValidationError('Úloha %(uloha)s není zadaná ani vyřešená', params={'uloha': ch})) | ||||||
| 		if errors: | 		if errors: | ||||||
|  | @ -78,7 +80,7 @@ class CisloForm(ModelForm): | ||||||
| 		return self.cleaned_data | 		return self.cleaned_data | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @admin.register(m.Cislo) | @admin.register(Cislo) | ||||||
| class CisloAdmin(admin.ModelAdmin): | class CisloAdmin(admin.ModelAdmin): | ||||||
| 	form = CisloForm | 	form = CisloForm | ||||||
| 	actions = ['force_publish', 'pregeneruj_vysledkovky'] | 	actions = ['force_publish', 'pregeneruj_vysledkovky'] | ||||||
|  | @ -86,31 +88,31 @@ class CisloAdmin(admin.ModelAdmin): | ||||||
| 
 | 
 | ||||||
| 	def force_publish(self,request,queryset): | 	def force_publish(self,request,queryset): | ||||||
| 		for cislo in queryset: | 		for cislo in queryset: | ||||||
| 			# cn = m.CisloNode.objects.get(cislo=cislo) | 			# cn = CisloNode.objects.get(cislo=cislo) | ||||||
| 			# for ch in tl.all_children(cn): | 			# for ch in tl.all_children(cn): | ||||||
| 			# 	if isinstance(ch, m.TemaVCisleNode): | 			# 	if isinstance(ch, TemaVCisleNode): | ||||||
| 			# 		if ch.tema.stav not in (m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): | 			# 		if ch.tema.stav not in (Problem.STAV_ZADANY, Problem.STAV_VYRESENY): | ||||||
| 			# 			ch.tema.stav = m.Problem.STAV_ZADANY | 			# 			ch.tema.stav = Problem.STAV_ZADANY | ||||||
| 			# 			ch.tema.save() | 			# 			ch.tema.save() | ||||||
| 			# | 			# | ||||||
| 			# 	if isinstance(ch, m.UlohaZadaniNode) or isinstance(ch, m.UlohaVzorakNode): | 			# 	if isinstance(ch, UlohaZadaniNode) or isinstance(ch, UlohaVzorakNode): | ||||||
| 			# 		if ch.uloha.stav not in (m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): | 			# 		if ch.uloha.stav not in (Problem.STAV_ZADANY, Problem.STAV_VYRESENY): | ||||||
| 			# 			ch.uloha.stav = m.Problem.STAV_ZADANY | 			# 			ch.uloha.stav = Problem.STAV_ZADANY | ||||||
| 			# 			ch.uloha.save() | 			# 			ch.uloha.save() | ||||||
| 			# 	if isinstance(ch, m.ReseniNode): | 			# 	if isinstance(ch, ReseniNode): | ||||||
| 			# 		for problem in ch.reseni.problem_set: | 			# 		for problem in ch.reseni.problem_set: | ||||||
| 			# 			if problem not in (m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): | 			# 			if problem not in (Problem.STAV_ZADANY, Problem.STAV_VYRESENY): | ||||||
| 			# 				problem.stav = m.Problem.STAV_ZADANY | 			# 				problem.stav = Problem.STAV_ZADANY | ||||||
| 			# 				problem.save() | 			# 				problem.save() | ||||||
| 
 | 
 | ||||||
| 			for ch in m.Uloha.objects.filter(cislo_zadani=cislo): | 			for ch in Uloha.objects.filter(cislo_zadani=cislo): | ||||||
| 				if ch.stav not in (m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): | 				if ch.stav not in (Problem.STAV_ZADANY, Problem.STAV_VYRESENY): | ||||||
| 					ch.stav = m.Problem.STAV_ZADANY | 					ch.stav = Problem.STAV_ZADANY | ||||||
| 					ch.save() | 					ch.save() | ||||||
| 
 | 
 | ||||||
| 					hp = ch.hlavni_problem | 					hp = ch.hlavni_problem | ||||||
| 					if hp.stav not in (m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY): | 					if hp.stav not in (Problem.STAV_ZADANY, Problem.STAV_VYRESENY): | ||||||
| 						hp.stav = m.Problem.STAV_ZADANY | 						hp.stav = Problem.STAV_ZADANY | ||||||
| 						hp.save() | 						hp.save() | ||||||
| 
 | 
 | ||||||
| 			# TODO Řešení, vzoráky? | 			# TODO Řešení, vzoráky? | ||||||
|  | @ -133,18 +135,19 @@ class CisloAdmin(admin.ModelAdmin): | ||||||
| 		return request.user.is_superuser | 		return request.user.is_superuser | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @admin.register(m.Problem) | @admin.register(Problem) | ||||||
| class ProblemAdmin(PolymorphicParentModelAdmin): | class ProblemAdmin(PolymorphicParentModelAdmin): | ||||||
| 	base_model = m.Problem | 	base_model = Problem | ||||||
| 	child_models = [ | 	child_models = [ | ||||||
| 		m.Tema, | 		Tema, | ||||||
| 		m.Clanek, | 		Clanek, | ||||||
| 		m.Uloha, | 		Uloha, | ||||||
| 		m.Konfera, | 		soustredeni.models.Konfera, | ||||||
| 		] | 	] | ||||||
| 	# Pokud chceme orezavat na aktualni rocnik, musime do modelu pridat odkaz na rocnik. Zatim bere vse. | 	# Pokud chceme orezavat na aktualni rocnik, musime do modelu pridat odkaz na rocnik. Zatim bere vse. | ||||||
| 	search_fields = ['nazev'] | 	search_fields = ['nazev'] | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| # V ProblemAdmin to nejde, protoze se to nepropise do deti | # V ProblemAdmin to nejde, protoze se to nepropise do deti | ||||||
| class ProblemAdminMixin(object): | class ProblemAdminMixin(object): | ||||||
| 	show_in_index = True | 	show_in_index = True | ||||||
|  | @ -152,32 +155,23 @@ class ProblemAdminMixin(object): | ||||||
| 	filter_horizontal = ['opravovatele'] | 	filter_horizontal = ['opravovatele'] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @admin.register(m.Tema) | @admin.register(Tema) | ||||||
| class TemaAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin): | class TemaAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin): | ||||||
| 	base_model = m.Tema | 	base_model = Tema | ||||||
| 
 | 
 | ||||||
| @admin.register(m.Clanek) | 
 | ||||||
|  | @admin.register(Clanek) | ||||||
| class ClanekAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin): | class ClanekAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin): | ||||||
| 	base_model = m.Clanek | 	base_model = Clanek | ||||||
| 
 | 
 | ||||||
| @admin.register(m.Uloha) | 
 | ||||||
|  | @admin.register(Uloha) | ||||||
| class UlohaAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin): | class UlohaAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin): | ||||||
| 	base_model = m.Uloha | 	base_model = Uloha | ||||||
| 
 | 
 | ||||||
| @admin.register(m.Konfera) | 
 | ||||||
|  | @admin.register(soustredeni.models.Konfera) | ||||||
| class KonferaAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin): | class KonferaAdmin(ProblemAdminMixin,PolymorphicChildModelAdmin): | ||||||
| 	base_model = m.Konfera | 	base_model = soustredeni.models.Konfera | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class TextAdminInline(admin.TabularInline): |  | ||||||
| 	model = m.Text |  | ||||||
| 	formfield_overrides = { |  | ||||||
| 		models.TextField: {'widget': widgets.TextInput} |  | ||||||
| 	} |  | ||||||
| 	exclude = ['text_zkraceny_set','text_zkraceny'] |  | ||||||
| 
 |  | ||||||
| admin.site.register(m.Text) |  | ||||||
| 
 | 
 | ||||||
| # admin.site.register(m.Pohadka) | # admin.site.register(m.Pohadka) | ||||||
| admin.site.register(m.Obrazek) |  | ||||||
| admin.site.register(m.Nastaveni, SingletonModelAdmin) |  | ||||||
							
								
								
									
										6
									
								
								tvorba/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,6 @@ | ||||||
|  | from django.apps import AppConfig | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class TvorbaConfig(AppConfig): | ||||||
|  | 	name = 'tvorba' | ||||||
|  | 	verbose_name = 'Tvorba' | ||||||
| Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB | 
| Before Width: | Height: | Size: 300 KiB After Width: | Height: | Size: 300 KiB | 
|  | @ -33,7 +33,7 @@ | ||||||
|         {% if c.titulka_nahled %} |         {% if c.titulka_nahled %} | ||||||
|         <img src="{{ c.titulka_nahled.url }}" alt="{{ c.kod }}" height=180px> |         <img src="{{ c.titulka_nahled.url }}" alt="{{ c.kod }}" height=180px> | ||||||
|         {% else %} |         {% else %} | ||||||
|         {% load static %} <img src="{% static 'images/no-picture.png' %}" height=180px alt="no-picture"> |         {% load static %} <img src="{% static 'tvorba/no-picture.png' %}" height=180px alt="no-picture"> | ||||||
|         {% endif %} |         {% endif %} | ||||||
|       </div> |       </div> | ||||||
| 
 | 
 | ||||||
|  | @ -79,7 +79,7 @@ | ||||||
|                                 {% if c.titulka_nahled %} |                                 {% if c.titulka_nahled %} | ||||||
|                                     <img src="{{ c.titulka_nahled.url }}" alt="{{ c.kod }}" height=180px> |                                     <img src="{{ c.titulka_nahled.url }}" alt="{{ c.kod }}" height=180px> | ||||||
|                                 {% else %} |                                 {% else %} | ||||||
|                                     {% load static %} <img src="{% static 'images/no-picture.png' %}" height=180px alt="no-picture"> |                                     {% load static %} <img src="{% static 'tvorba/no-picture.png' %}" height=180px alt="no-picture"> | ||||||
|                                 {% endif %} |                                 {% endif %} | ||||||
|                             </div> |                             </div> | ||||||
| 
 | 
 | ||||||
|  | @ -34,7 +34,7 @@ | ||||||
|           {% if tematko.obrazek %} |           {% if tematko.obrazek %} | ||||||
|             <img src="{{ tematko.obrazek.url }}" alt="{{ tematko.nazev }}"> |             <img src="{{ tematko.obrazek.url }}" alt="{{ tematko.nazev }}"> | ||||||
|           {% else %} {# pokud témátko nemá fotku, zobrazuje se defaultní obrázek #} |           {% else %} {# pokud témátko nemá fotku, zobrazuje se defaultní obrázek #} | ||||||
|             {% load static %} <img src="{% static 'images/tema-bez-obrazku.png' %}" alt="{{ tematko.nazev }}"> |             {% load static %} <img src="{% static 'tvorba/tema-bez-obrazku.png' %}" alt="{{ tematko.nazev }}"> | ||||||
|           {% endif %} |           {% endif %} | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
							
								
								
									
										506
									
								
								tvorba/testutils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,506 @@ | ||||||
|  | # FIXME vypreparovat treenode | ||||||
|  | 
 | ||||||
|  | import datetime | ||||||
|  | 
 | ||||||
|  | import lorem | ||||||
|  | import django.contrib.auth | ||||||
|  | import logging | ||||||
|  | 
 | ||||||
|  | from seminar.models import Rocnik, Cislo, Deadline, Problem, Tema, Uloha, TextNode, UlohaVzorakNode, RocnikNode, CisloNode, TemaVCisleNode, Text, UlohaZadaniNode | ||||||
|  | import seminar.models as m | ||||||
|  | 
 | ||||||
|  | from treenode.treelib import all_children, insert_last_child, all_children_of_type, create_node_after | ||||||
|  | 
 | ||||||
|  | from odevzdavatko.testutils import gen_reseni_ulohy | ||||||
|  | 
 | ||||||
|  | logger = logging.getLogger(__name__) | ||||||
|  | 
 | ||||||
|  | User = django.contrib.auth.get_user_model() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def gen_zadani_ulohy(rnd, cisla, organizatori, pocet_oboru, poradi_cisla, poradi_problemu): | ||||||
|  | 	 | ||||||
|  | 	# Proměnné pro náhodné generování názvů a zadání. | ||||||
|  | 	jaka = ["Šachová", "Černá", "Větrná", "Dlouhá", "Křehká", "Rychlá", | ||||||
|  | 		"Zákeřná", "Fyzikální"] | ||||||
|  | 	co = ["kostka", "smršť", "díra", "zrada", "toulka", "tyč", | ||||||
|  | 		"úloha", "blecha"] | ||||||
|  | 	sloveso = ["Najděte", "Spočítejte", "Zapište", "Změřte", "Odhadněte"] | ||||||
|  | 	koho = ["délku", "počet", "množství", "dílky"] | ||||||
|  | 	ceho = ["všech", "správných", "konstatních", "zelených"] | ||||||
|  | 	jmeno = ["řešení", "tahů", "čísel", "kalhot", "koulí", "hadů"] | ||||||
|  | 	kde = ["na zemi", "ve vesmíru", "ve vzduchu", "na šňůře", "v letadle"] | ||||||
|  | 	obory = ["M", "F", "I", "O", "B"] | ||||||
|  | 
 | ||||||
|  | 	p = Uloha.objects.create( | ||||||
|  | 		# atributy třídy Problem | ||||||
|  | 		nazev=" ".join([rnd.choice(jaka), rnd.choice(co)]), | ||||||
|  | 		stav=Problem.STAV_ZADANY, | ||||||
|  | 		zamereni=rnd.sample(obory, pocet_oboru), | ||||||
|  | 		autor=rnd.choice(organizatori), | ||||||
|  | 		garant=rnd.choice(organizatori), | ||||||
|  | 		kod=str(poradi_problemu), | ||||||
|  | 		# atributy třídy Uloha | ||||||
|  | 		cislo_zadani=cisla[poradi_cisla-2-1], | ||||||
|  | 		cislo_reseni=cisla[poradi_cisla-1], | ||||||
|  | 		cislo_deadline=cisla[poradi_cisla-1], | ||||||
|  | 		max_body = rnd.randint(1, 8) | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 	text = " ".join( | ||||||
|  | 		[rnd.choice(sloveso), | ||||||
|  | 		rnd.choice(koho), | ||||||
|  | 		rnd.choice(ceho), | ||||||
|  | 		rnd.choice(jmeno), | ||||||
|  | 		rnd.choice(kde)] | ||||||
|  | 		) | ||||||
|  | 	text_zadani = Text.objects.create( | ||||||
|  | 		na_web = text, | ||||||
|  | 		do_cisla = text, | ||||||
|  | 	) | ||||||
|  | 	zad = TextNode.objects.create(text = text_zadani, root = p.cislo_zadani.rocnik.rocniknode) | ||||||
|  | 	uloha_zadani = UlohaZadaniNode.objects.create(uloha = p, first_child = zad, root = p.cislo_zadani.rocnik.rocniknode) | ||||||
|  | 	p.ulohazadaninode = uloha_zadani | ||||||
|  | 	otec_syn(cisla[poradi_cisla-2-1].cislonode, uloha_zadani) | ||||||
|  | 
 | ||||||
|  | 	return p | ||||||
|  | 
 | ||||||
|  | def gen_vzoroveho_reseni_ulohy(rnd, organizatori, uloha, pocet_opravovatelu): | ||||||
|  | 	reseni = ["to je přece jasné", "triviální", "omlouváme se," | ||||||
|  | 		"otevřený problém", "neřešitelné", "triviálně triviální", | ||||||
|  | 		"použitím věty z prvního semestru na matfyzu", | ||||||
|  | 		"jednoduše pomocí látky z druhého semestru na matfyzu", | ||||||
|  | 		"netriviální aplikace diferenciálních rovnic", "zadání je vnitřně" | ||||||
|  | 		"sporné", "nepopsatelně jednoduché", "pokud jste na to nepřišli," | ||||||
|  | 		"tak jste fakt hloupí"] | ||||||
|  | 
 | ||||||
|  | 	# Generování vzorového řešení. | ||||||
|  | 	obsah = rnd.choice(reseni) | ||||||
|  | 	text_vzoraku = Text.objects.create( | ||||||
|  | 		na_web = obsah, | ||||||
|  | 		do_cisla = obsah | ||||||
|  | 	) | ||||||
|  | 	vzorak = TextNode.objects.create(text = text_vzoraku, root = uloha.cislo_zadani.rocnik.rocniknode) | ||||||
|  | 	uloha_vzorak = UlohaVzorakNode.objects.create(uloha = uloha, first_child = vzorak, root = uloha.cislo_zadani.rocnik.rocniknode) | ||||||
|  | 	uloha.ulohavzoraknode = uloha_vzorak | ||||||
|  | 
 | ||||||
|  | 	uloha.opravovatele.set(rnd.sample(organizatori, pocet_opravovatelu)) | ||||||
|  | 	uloha.save() | ||||||
|  | 	return uloha_vzorak | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size): | ||||||
|  | 	logger.info('Generuji úlohy do čísla (size={})...'.format(size)) | ||||||
|  | 
 | ||||||
|  | 	k = 0 | ||||||
|  | 	for rocnik in rocniky: | ||||||
|  | 		k += 1 | ||||||
|  | 		print("Generuji {}. číslo.".format(k)) | ||||||
|  | 		cisla = rocnik_cisla[k - 1] | ||||||
|  | 		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ů | ||||||
|  | 			poc_res = rnd.randint(resitele_size//8, resitele_size//4) | ||||||
|  |  			# dané číslo řeší něco mezi osminou a čtvrtinou všech řešitelů | ||||||
|  | 			# (náhodná hausnumera, možno změnit) | ||||||
|  | 			# úč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 | ||||||
|  | 			resitele_cisla = rnd.sample(resitele, poc_res) | ||||||
|  | 			for pi in range(1, ((size + 1) // 2) + 1): # počet problémů | ||||||
|  | 
 | ||||||
|  | 				poc_op = rnd.randint(1, 4) # počet opravovatelů | ||||||
|  | 				poc_oboru = rnd.randint(1, 2) | ||||||
|  | 				 | ||||||
|  | 				# Generování zadání úlohy a UlohaZadaniNode,  | ||||||
|  | 				# přivěšení pod dané číslo | ||||||
|  | 				p = gen_zadani_ulohy(rnd, cisla, organizatori, poc_oboru, ci, pi)	 | ||||||
|  | 				# Generování vzorového řešení | ||||||
|  | 				uloha_vzorak = gen_vzoroveho_reseni_ulohy(rnd, organizatori,  | ||||||
|  | 					p, poc_op) | ||||||
|  | 				insert_last_child(cisla[ci-1].cislonode, uloha_vzorak) | ||||||
|  | 
 | ||||||
|  | 				# Generování řešení a hodnocení k úloze | ||||||
|  | 				gen_reseni_ulohy(rnd, cisla, p, poc_res, ci,  | ||||||
|  | 					resitele_cisla, resitele)  | ||||||
|  | 
 | ||||||
|  | 	return | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def gen_rocniky(last_rocnik, size): | ||||||
|  | 	logger.info('Generuji ročníky (size={})...'.format(size)) | ||||||
|  | 
 | ||||||
|  | 	rocniky = [] | ||||||
|  | 	node = None | ||||||
|  | 	for ri in range(min(last_rocnik - size, 1), last_rocnik + 1): | ||||||
|  | 		rocnik = Rocnik.objects.create(prvni_rok = 1993 + ri, rocnik = ri) | ||||||
|  | 		node2 = RocnikNode.objects.create(rocnik = rocnik, succ = node) | ||||||
|  | 		rocnik.save() | ||||||
|  | 		node = node2 | ||||||
|  | 		rocniky.append(rocnik) | ||||||
|  | 	return rocniky | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def gen_cisla(rnd, rocniky): | ||||||
|  | 	logger.info('Generuji čísla...') | ||||||
|  | 
 | ||||||
|  | 	rocnik_cisla = [] | ||||||
|  | 	for rocnik in rocniky: | ||||||
|  | 		otec = True | ||||||
|  | 		cisla = [] | ||||||
|  | 		cisel = rnd.randint(4, 8) | ||||||
|  | 		node = None | ||||||
|  | 		for ci in range(1, cisel + 1): | ||||||
|  | 			# první číslo vydáváme typicky okolo prázdnin | ||||||
|  | 			# (ci - 1)*2 zaručuje první číslo v červnu a všechna | ||||||
|  | 			# další po dvou měsících (což je rozumná aproximace) | ||||||
|  | 			mesic_vydani = (ci - 1)*2 + 6 | ||||||
|  | 			# celociselné dělení mi řekne, jestli to je první nebo druhý rok ročníku | ||||||
|  | 			vydano = datetime.date(rocnik.prvni_rok + mesic_vydani // 12, | ||||||
|  | 				(mesic_vydani - 1) % 12 + 1, | ||||||
|  | 				rnd.randint(1, 28)) | ||||||
|  | 			deadline = datetime.date(rocnik.prvni_rok + (mesic_vydani + 2) // 12, | ||||||
|  | 				(mesic_vydani + 1) % 12 + 1, | ||||||
|  | 				rnd.randint(1, 28)) | ||||||
|  | 
 | ||||||
|  | 			cislo = Cislo.objects.create( | ||||||
|  | 				rocnik = rocnik, | ||||||
|  | 				poradi = str(ci),  | ||||||
|  | 				datum_vydani=vydano, | ||||||
|  | 				verejne_db=True, | ||||||
|  | 			) | ||||||
|  | 			node2 = CisloNode.objects.get(cislo = cislo) | ||||||
|  | 			node2.succ = node | ||||||
|  | 			node2.root = rocnik.rocniknode | ||||||
|  | 			cislo.save() | ||||||
|  | 			deadline = Deadline.objects.create( | ||||||
|  | 				cislo=cislo, | ||||||
|  | 				deadline=deadline, | ||||||
|  | 				typ=Deadline.TYP_CISLA, | ||||||
|  | 				verejna_vysledkovka=True, | ||||||
|  | 			) | ||||||
|  | 			deadline.save() | ||||||
|  | 			node = node2 | ||||||
|  | 			if otec: | ||||||
|  | 				otec = False | ||||||
|  | 				rocnik.rocniknode.first_child = node | ||||||
|  | 				rocnik.save() | ||||||
|  | 
 | ||||||
|  | 			cisla.append(cislo) | ||||||
|  | 		rocnik_cisla.append(cisla) | ||||||
|  | 	return rocnik_cisla | ||||||
|  | 
 | ||||||
|  | def add_first_child(node, child): | ||||||
|  | 	node.first_child = child | ||||||
|  | 	node.save() | ||||||
|  | 	return | ||||||
|  | 
 | ||||||
|  | def get_text(): | ||||||
|  | 	odstavec = lorem.paragraph() | ||||||
|  | 	return Text.objects.create(na_web = odstavec, do_cisla = odstavec)	 | ||||||
|  | 
 | ||||||
|  | def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod): | ||||||
|  | 	tema = Tema.objects.create( | ||||||
|  | 				nazev=nazev, | ||||||
|  | 				stav=Problem.STAV_ZADANY, | ||||||
|  | 				zamereni="M", | ||||||
|  | 				autor=rnd.choice(organizatori), | ||||||
|  | 				garant=rnd.choice(organizatori), | ||||||
|  | 				kod=str(kod), | ||||||
|  | 				tema_typ=rnd.choice(Tema.TEMA_CHOICES)[0], | ||||||
|  | 				rocnik=rocnik, | ||||||
|  | 				abstrakt = lorem.paragraph()  | ||||||
|  | 		) | ||||||
|  | 	 | ||||||
|  | 	# Generování struktury k tématu | ||||||
|  | 	cisla = sorted(rocnik.cisla.all(), key=lambda cislo: cislo.poradi) | ||||||
|  | 	for cislo in cisla: | ||||||
|  | 		# Přidáme TemaVCisleNode do daného čísla | ||||||
|  | 		cislo_node = cislo.cislonode	 | ||||||
|  | 		tema_cislo_node = TemaVCisleNode.objects.create(tema = tema, root = cislo_node.root) | ||||||
|  | 		insert_last_child(cislo_node, tema_cislo_node) | ||||||
|  | 		 | ||||||
|  | 		# Přidávání obsahu do čísla | ||||||
|  | 		cast_node = m.CastNode.objects.create(nadpis = "Příspěvek k číslu {}".format(cislo.kod), root=cislo_node.root) | ||||||
|  | 		add_first_child(tema_cislo_node, cast_node) | ||||||
|  | 	 | ||||||
|  | 		text_node = TextNode.objects.create(text = get_text(), root=cislo_node.root) | ||||||
|  | 		add_first_child(cast_node, text_node) | ||||||
|  | 
 | ||||||
|  | 		cast_node2 = m.CastNode.objects.create(nadpis = "První podproblém", root=cislo_node.root) | ||||||
|  | 		add_first_child(text_node, cast_node2) | ||||||
|  | 		 | ||||||
|  | 		text_node2 = TextNode.objects.create(text = get_text(), root=cislo_node.root) | ||||||
|  | 		add_first_child(cast_node2, text_node2) | ||||||
|  | 		 | ||||||
|  | 		cast_node3 = m.CastNode.objects.create(nadpis = "Druhý podproblém", root=cislo_node.root) | ||||||
|  | 		add_first_child(text_node2, cast_node3) | ||||||
|  | 
 | ||||||
|  | 		text_node3 = TextNode.objects.create(text = get_text(), root=cislo_node.root) | ||||||
|  | 		add_first_child(cast_node3, text_node3) | ||||||
|  | 
 | ||||||
|  | 		cast_node4 = m.CastNode.objects.create(nadpis = "Třetí podproblém", root=cislo_node.root) | ||||||
|  | 		add_first_child(text_node3, cast_node4)	 | ||||||
|  | 
 | ||||||
|  | 		text_node4 = TextNode.objects.create(text = get_text(), root=cislo_node.root) | ||||||
|  | 		add_first_child(cast_node3, text_node4) | ||||||
|  | 		 | ||||||
|  | 		cast_node3a = m.CastNode.objects.create(nadpis = "Podproblém paralelní s " | ||||||
|  | 					"druhým podproblémem", root=cislo_node.root) | ||||||
|  | 		cast_node3.succ = cast_node3a | ||||||
|  | 		cast_node3.save() | ||||||
|  | 
 | ||||||
|  | 		text_node3a = TextNode.objects.create(text = get_text(), root=cislo_node.root) | ||||||
|  | 		add_first_child(cast_node3a, text_node3a) | ||||||
|  | 
 | ||||||
|  | 		# Občas přidáme mezičíslo | ||||||
|  | 		if rnd.randint(1, 3) == 1: | ||||||
|  | 			create_node_after(cislo_node, m.MezicisloNode, root=cislo_node.root) | ||||||
|  | 			mezicislo_node = cislo_node.succ | ||||||
|  | 	 | ||||||
|  | 			cast_node_mezicislo = m.CastNode.objects.create( | ||||||
|  | 					nadpis = "Příspěvek k mezičíslu".format(cislo.kod), root=cislo_node.root) | ||||||
|  | 			add_first_child(mezicislo_node, cast_node_mezicislo) | ||||||
|  | 
 | ||||||
|  | 			odstavec = lorem.paragraph() | ||||||
|  | 			text_mezicislo = Text.objects.create(na_web = odstavec, do_cisla = odstavec) | ||||||
|  | 			text_node_mezicislo = TextNode.objects.create(text = text_mezicislo, root=cislo_node.root) | ||||||
|  | 			add_first_child(cast_node_mezicislo, text_node_mezicislo) | ||||||
|  | 
 | ||||||
|  | 	return tema | ||||||
|  | 
 | ||||||
|  | def gen_temata(rnd, rocniky, rocnik_cisla, organizatori): | ||||||
|  | 	logger.info('Generuji témata...') | ||||||
|  | 
 | ||||||
|  | 	jake = ["Hravé", "Fyzikální", "Nejlepší", "Totálně masakrální", | ||||||
|  | 					"Šokující", "Magnetické", "Modré", "Překvapivé", | ||||||
|  | 					"Plasmatické", "Novoroční"] | ||||||
|  | 	co = ["téma", "záření", "stavení", "jiskření", "jelito", | ||||||
|  | 					"drama", "kuře", "moře", "klání", "proudění", "čekání"] | ||||||
|  | 	poc_oboru = rnd.randint(1, 2) | ||||||
|  | 
 | ||||||
|  | 	rocnik_temata = [] | ||||||
|  | 	# 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, cisla in zip(rocniky, rocnik_cisla): | ||||||
|  | 		kod = 1 | ||||||
|  | 		letosni_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 | ||||||
|  | 		for zacatek_tematu in range(1, 3): | ||||||
|  | 			# Vygenerujeme téma | ||||||
|  | 			t = Tema.objects.create( | ||||||
|  | 				# atributy třídy Problem | ||||||
|  | 				nazev=" ".join([rnd.choice(jake), rnd.choice(co)]), | ||||||
|  | 				stav=Problem.STAV_ZADANY, | ||||||
|  | 				zamereni=rnd.sample(["M", "F", "I", "O", "B"], poc_oboru), | ||||||
|  | 				autor=rnd.choice(organizatori), | ||||||
|  | 				garant=rnd.choice(organizatori), | ||||||
|  | 				kod=str(kod), | ||||||
|  | 				# atributy třídy Téma | ||||||
|  | 				tema_typ=rnd.choice(Tema.TEMA_CHOICES)[0], | ||||||
|  | 				rocnik=rocnik, | ||||||
|  | 				abstrakt = "Abstrakt tematka {}".format(kod) | ||||||
|  | 			) | ||||||
|  | 			kod += 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,root=rocnik.rocniknode) | ||||||
|  | 				# FIXME: Není to off-by-one? | ||||||
|  | 				otec = cisla[i-1].cislonode | ||||||
|  | 				otec_syn(otec, node) | ||||||
|  | 
 | ||||||
|  | 			# 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() | ||||||
|  | 			letosni_temata.append((zacatek_tematu, konec_tematu, t)) | ||||||
|  | 		rocnik_temata.append(letosni_temata) | ||||||
|  | 	return rocnik_temata | ||||||
|  | 
 | ||||||
|  | def gen_ulohy_tematu(rnd, organizatori, resitele, tema, kod, cislo, cislo_se_vzorakem): | ||||||
|  | 	""" Generování úlohy k danému tématu. """ | ||||||
|  | 	 | ||||||
|  | 	# Proměnné pro náhodné generování názvů a zadání. | ||||||
|  | 	jaka = ["Šachová", "Černá", "Větrná", "Dlouhá", "Křehká", "Rychlá", | ||||||
|  | 		"Zákeřná", "Fyzikální"] | ||||||
|  | 	co = ["kostka", "smršť", "díra", "zrada", "toulka", "tyč", | ||||||
|  | 		"úloha", "blecha"] | ||||||
|  | 	sloveso = ["Najděte", "Spočítejte", "Zapište", "Změřte", "Odhadněte"] | ||||||
|  | 	koho = ["délku", "počet", "množství", "dílky"] | ||||||
|  | 	ceho = ["všech", "správných", "konstatních", "zelených"] | ||||||
|  | 	jmeno = ["řešení", "tahů", "čísel", "kalhot", "koulí", "hadů"] | ||||||
|  | 	kde = ["na zemi", "ve vesmíru", "ve vzduchu", "na šňůře", "v letadle"] | ||||||
|  | 	obory = ["M", "F", "I", "O", "B"] | ||||||
|  | 	 | ||||||
|  | 	uloha = Uloha.objects.create( | ||||||
|  | 		nazev=": ".join([tema.nazev,  | ||||||
|  | 			"úloha {}.".format(kod)]), | ||||||
|  | 		nadproblem=tema, | ||||||
|  | 		stav=Problem.STAV_ZADANY, | ||||||
|  | 		zamereni=tema.zamereni, | ||||||
|  | 		autor=tema.autor, | ||||||
|  | 		garant=tema.garant, | ||||||
|  | 		kod=str(kod), | ||||||
|  | 		cislo_zadani=cislo, | ||||||
|  | 		cislo_reseni=cislo_se_vzorakem, | ||||||
|  | 		cislo_deadline=cislo_se_vzorakem, | ||||||
|  | 		max_body = rnd.randint(1, 8) | ||||||
|  | 	) | ||||||
|  | 	 | ||||||
|  | 	# Samotný obsah následně vzniklého Textu zadání | ||||||
|  | 	obsah = " ".join( | ||||||
|  | 		[rnd.choice(sloveso),  | ||||||
|  | 		rnd.choice(koho),  | ||||||
|  | 		rnd.choice(ceho),  | ||||||
|  | 		rnd.choice(jmeno),  | ||||||
|  | 		rnd.choice(kde)] | ||||||
|  | 		) | ||||||
|  | 	text_zadani = Text.objects.create( | ||||||
|  | 		na_web = obsah, | ||||||
|  | 		do_cisla = obsah, | ||||||
|  | 		) | ||||||
|  | 	zad = TextNode.objects.create(text = text_zadani, root=tema.temavcislenode_set.first().root) | ||||||
|  | 	uloha_zadani = UlohaZadaniNode.objects.create(uloha=uloha, first_child = zad, root=tema.temavcislenode_set.first().root) | ||||||
|  | 	uloha.ulohazadaninode = uloha_zadani | ||||||
|  | 
 | ||||||
|  | 	# Generování řešení a hodnocení k úloze | ||||||
|  | 	gen_reseni_ulohy(rnd, [cislo], uloha, len(resitele)//4, 1, | ||||||
|  | 					 resitele, resitele) | ||||||
|  | 
 | ||||||
|  | 	return uloha, uloha_zadani | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori, resitele): | ||||||
|  | 	logger.info('Generuji úlohy k tématům...') | ||||||
|  | 
 | ||||||
|  | 	# Ke každému ročníku si vezmeme příslušná čísla a témata | ||||||
|  | 	for rocnik, cisla, temata in zip(rocniky, rocnik_cisla, rocnik_temata): | ||||||
|  | 		# Do každého čísla nagenerujeme ke každému témátku pár úložek | ||||||
|  | 		for cislo in cisla: | ||||||
|  | 			print("Generuji úložky do {}-tého čísla".format(cislo.poradi)) | ||||||
|  | 			# Vzorák bude o dvě čísla dál | ||||||
|  | 			cislo_se_vzorakem = Cislo.objects.filter( | ||||||
|  | 					rocnik=rocnik, | ||||||
|  | 					poradi=str(int(cislo.poradi) + 2), | ||||||
|  | 					) | ||||||
|  | 			# 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í...) | ||||||
|  | 			# Tohle sice umožňuje vygenerovat vzorák do čísla dávno po konci témátka,  | ||||||
|  | 			# ale to nám pro jednoduchost nevadí. | ||||||
|  | 			if len(cislo_se_vzorakem) == 0: | ||||||
|  | 				cislo_se_vzorakem = cisla[-1] | ||||||
|  | 			else: | ||||||
|  | 				cislo_se_vzorakem = cislo_se_vzorakem.first() | ||||||
|  | 
 | ||||||
|  | 			for tema_node in all_children_of_type(cislo.cislonode, TemaVCisleNode): | ||||||
|  | 				tema = tema_node.tema | ||||||
|  | 					 | ||||||
|  | 				# 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, uz = gen_ulohy_tematu(rnd, organizatori, resitele, tema, kod, | ||||||
|  | 						cislo, cislo_se_vzorakem) | ||||||
|  | 
 | ||||||
|  | 					insert_last_child(tema_node, uz) | ||||||
|  | 				 | ||||||
|  | 					poc_op = rnd.randint(1, 4) | ||||||
|  | 					uvz = gen_vzoroveho_reseni_ulohy(rnd, organizatori,  | ||||||
|  | 						u, poc_op)  | ||||||
|  | 					 | ||||||
|  | 					# Najdeme správný TemaVCisleNode pro vložení vzoráku | ||||||
|  | 					res_tema_node = None; | ||||||
|  | 					for node in all_children(cislo_se_vzorakem.cislonode): | ||||||
|  | 						if isinstance(node, TemaVCisleNode):  | ||||||
|  | 							if node.tema == tema: | ||||||
|  | 								res_tema_node = node | ||||||
|  | 					if res_tema_node is None: | ||||||
|  | 						raise LookupError("Nenalezen Node pro vložení vzoráku") | ||||||
|  | 					insert_last_child(res_tema_node, uvz) | ||||||
|  | 					u.save() | ||||||
|  | 	return | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def otec_syn(otec, syn): | ||||||
|  | 	bratr = otec.first_child | ||||||
|  | 	syn.succ = bratr | ||||||
|  | 	otec.first_child = syn | ||||||
|  | 	syn.save() | ||||||
|  | 	otec.save() | ||||||
|  | 
 | ||||||
|  | def gen_clanek(rnd, organizatori, resitele): | ||||||
|  | 	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(organizatori), | ||||||
|  | 		kod='cl', | ||||||
|  | 		) | ||||||
|  | 	clanek.save() | ||||||
|  | 
 | ||||||
|  | 	reseni = m.Reseni.objects.create( | ||||||
|  | 		zverejneno=True, | ||||||
|  | 		) | ||||||
|  | 	reseni.resitele.add(rnd.choice(resitele)) | ||||||
|  | 	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 treenode.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})") | ||||||
|  | @ -1,15 +1,11 @@ | ||||||
| from django.urls import path, include, re_path | from django.urls import path, include, re_path | ||||||
| from . import views | from . import views | ||||||
| from .utils import org_required | from personalni.utils import org_required | ||||||
| 
 | 
 | ||||||
| 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), | ||||||
| 
 | 
 | ||||||
| 	# Organizatori |  | ||||||
| 	path('o-nas/organizatori/', views.CojemamOrganizatoriView.as_view(), name='organizatori'), |  | ||||||
| 	path('o-nas/organizatori/organizovali/', views.CojemamOrganizatoriStariView.as_view(), name='stari_organizatori'), |  | ||||||
| 
 |  | ||||||
| 	# Archiv | 	# Archiv | ||||||
| 	path('archiv/rocniky/', views.ArchivView.as_view(), name="seminar_archiv_rocniky"), | 	path('archiv/rocniky/', views.ArchivView.as_view(), name="seminar_archiv_rocniky"), | ||||||
| 	path('archiv/temata/', views.ArchivTemataView.as_view(), name="seminar_archiv_temata"), | 	path('archiv/temata/', views.ArchivTemataView.as_view(), name="seminar_archiv_temata"), | ||||||
|  | @ -65,20 +61,11 @@ urlpatterns = [ | ||||||
| 		org_required(views.TitulyView), | 		org_required(views.TitulyView), | ||||||
| 		name='seminar_cislo_titul' | 		name='seminar_cislo_titul' | ||||||
| 	), | 	), | ||||||
| 	path( |  | ||||||
| 		'stav', |  | ||||||
| 		org_required(views.StavDatabazeView), |  | ||||||
| 		name='stav_databaze' |  | ||||||
| 	), |  | ||||||
| 	path( | 	path( | ||||||
| 		'cislo/<int:trocnik>.<str:tcislo>/odmeny/<int:frocnik>.<str:fcislo>/', | 		'cislo/<int:trocnik>.<str:tcislo>/odmeny/<int:frocnik>.<str:fcislo>/', | ||||||
| 		org_required(views.OdmenyView.as_view()), | 		org_required(views.OdmenyView.as_view()), | ||||||
| 		name="seminar_archiv_odmeny"), | 		name="seminar_archiv_odmeny"), | ||||||
| 
 | 
 | ||||||
| 	path('', views.TitulniStranaView.as_view(), name='titulni_strana'), |  | ||||||
| 	path('jak-resit/', views.JakResitView.as_view(), name='jak_resit'), |  | ||||||
| 	path('stare-novinky/', views.StareNovinkyView.as_view(), name='stare_novinky'), |  | ||||||
| 
 |  | ||||||
| 	# Dočasné & neodladěné: | 	# Dočasné & neodladěné: | ||||||
| 	path( | 	path( | ||||||
| 		'hidden/hromadne_pridani', | 		'hidden/hromadne_pridani', | ||||||
							
								
								
									
										89
									
								
								tvorba/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,89 @@ | ||||||
|  | from django.core.exceptions import ObjectDoesNotExist | ||||||
|  | 
 | ||||||
|  | import personalni.models | ||||||
|  | 
 | ||||||
|  | import seminar.models as m | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 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 | ||||||
|  | 		return personalni.models.Resitel.objects.filter( | ||||||
|  | 			rok_maturity__gte=rocnik.druhy_rok(), | ||||||
|  | 			reseni__hodnoceni__deadline_body__cislo__rocnik=rocnik | ||||||
|  | 		).distinct() | ||||||
|  | 	else:  # filtrujeme podle ročníku i čísla | ||||||
|  | 		return personalni.models.Resitel.objects.filter( | ||||||
|  | 			rok_maturity__gte=rocnik.druhy_rok(), | ||||||
|  | 			reseni__hodnoceni__deadline_body__cislo__rocnik=rocnik, | ||||||
|  | 			reseni__hodnoceni__deadline_body__cislo__poradi__lte=cislo.poradi | ||||||
|  | 		).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).filter(rok_maturity__gte=letos.druhy_rok()) | ||||||
|  | 	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().filter(rok_maturity__gte=letos.druhy_rok()) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # 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),  # noqa | ||||||
|  | 					 ('M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'))  # noqa | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 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) | ||||||
							
								
								
									
										584
									
								
								tvorba/views/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,584 @@ | ||||||
|  | # Dočsasné views | ||||||
|  | from .docasne import * | ||||||
|  | 
 | ||||||
|  | # Zbytek | ||||||
|  | 
 | ||||||
|  | from django.shortcuts import get_object_or_404, render | ||||||
|  | from django.http import HttpResponse | ||||||
|  | from django.urls import reverse | ||||||
|  | from django.core.exceptions import ObjectDoesNotExist | ||||||
|  | from django.views import generic | ||||||
|  | from django.utils.translation import gettext as _ | ||||||
|  | from django.http import Http404 | ||||||
|  | from django.db.models import Q, Sum, Count | ||||||
|  | from django.views.generic.base import RedirectView | ||||||
|  | from django.core.exceptions import PermissionDenied | ||||||
|  | 
 | ||||||
|  | import seminar.models as s | ||||||
|  | import seminar.models as m | ||||||
|  | from seminar.models import Problem, Cislo, Reseni, Nastaveni, Rocnik, \ | ||||||
|  | 	Resitel, Novinky, Tema, Clanek, \ | ||||||
|  | 	Deadline  # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci | ||||||
|  | #from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva | ||||||
|  | from treenode import treelib | ||||||
|  | import treenode.templatetags as tnltt | ||||||
|  | import treenode.serializers as vr | ||||||
|  | from vysledkovky.utils import body_resitelu, VysledkovkaCisla, \ | ||||||
|  | 	VysledkovkaRocniku, VysledkovkaDoTeXu | ||||||
|  | 
 | ||||||
|  | from datetime import date, datetime | ||||||
|  | from itertools import groupby | ||||||
|  | from collections import OrderedDict | ||||||
|  | import os | ||||||
|  | import os.path as op | ||||||
|  | from django.conf import settings | ||||||
|  | import unicodedata | ||||||
|  | import logging | ||||||
|  | import time | ||||||
|  | 
 | ||||||
|  | import personalni.views | ||||||
|  | 
 | ||||||
|  | from .. import utils | ||||||
|  | 
 | ||||||
|  | # ze starého modelu | ||||||
|  | #def verejna_temata(rocnik): | ||||||
|  | #	""" | ||||||
|  | #	Vrací queryset zveřejněných témat v daném ročníku. | ||||||
|  | #	""" | ||||||
|  | #	return Problem.objects.filter(typ=Problem.TYP_TEMA, cislo_zadani__rocnik=rocnik, cislo_zadani__verejne_db=True).order_by('kod') | ||||||
|  | # | ||||||
|  | #def temata_v_rocniku(rocnik): | ||||||
|  | #	return Problem.objects.filter(typ=Problem.TYP_TEMA, rocnik=rocnik) | ||||||
|  | 
 | ||||||
|  | logger = logging.getLogger(__name__) | ||||||
|  | 
 | ||||||
|  | def get_problemy_k_tematu(tema): | ||||||
|  | 	return Problem.objects.filter(nadproblem = tema) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # FIXME: Pozor, níž je ještě jeden ProblemView! | ||||||
|  | #class ProblemView(generic.DetailView): | ||||||
|  | #	model = s.Problem | ||||||
|  | #	# Zkopírujeme template_name od TreeNodeView, protože jsme prakticky jen trošku upravený TreeNodeView | ||||||
|  | #	template_name = TreeNodeView.template_name | ||||||
|  | # | ||||||
|  | #	def get_context_data(self, **kwargs): | ||||||
|  | #		context = super().get_context_data(**kwargs) | ||||||
|  | #		user = self.request.user | ||||||
|  | #		# Teď potřebujeme doplnit tnldata do kontextu. | ||||||
|  | #		# Ošklivý type switch, hezčí by bylo udělat to polymorfni. FIXME. | ||||||
|  | #		if False: | ||||||
|  | #			# Hezčí formátování zbytku :-P | ||||||
|  | #			pass | ||||||
|  | #		elif isinstance(self.object, s.Clanek) or  isinstance(self.object, s.Konfera): | ||||||
|  | #			# Tyhle Problémy mají ŘešeníNode | ||||||
|  | #			context['tnldata'] = TNLData.from_treenode(self.object.reseninode,user) | ||||||
|  | #		elif isinstance(self.object, s.Uloha): | ||||||
|  | #			# FIXME: Teď vždycky zobrazujeme i vzorák! Možná by bylo hezčí/lepší mít to stejně jako pro Téma: procházet jen dosažitelné z Ročníku / čísla / whatever | ||||||
|  | #			tnl_zadani = TNLData.from_treenode(self.object.ulohazadaninode,user) | ||||||
|  | #			tnl_vzorak = TNLData.from_treenode(self.object.ulohavzoraknode,user) | ||||||
|  | #			context['tnldata'] = TNLData.from_tnldata_list([tnl_zadani, tnl_vzorak]) | ||||||
|  | #		elif isinstance(self.object, s.Tema): | ||||||
|  | #			rocniknode = self.object.rocnik.rocniknode | ||||||
|  | #			context['tnldata'] = TNLData.filter_treenode(rocniknode, lambda x: isinstance(x, s.TemaVCisleNode)) | ||||||
|  | #		else: | ||||||
|  | #			raise ValueError("Obecný problém nejde zobrazit.") | ||||||
|  | #		return context | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #class AktualniZadaniView(generic.TemplateView): | ||||||
|  | #	template_name = 'treenode/treenode.html' | ||||||
|  | 
 | ||||||
|  | # TODO Co chceme vlastně zobrazovat na této stránce? Zatím je zde aktuální číslo, ale může tu být cokoli jiného... | ||||||
|  | #class AktualniZadaniView(TreeNodeView): | ||||||
|  | #	def get_object(self): | ||||||
|  | #		nastaveni = get_object_or_404(Nastaveni) | ||||||
|  | #		return nastaveni.aktualni_cislo.cislonode | ||||||
|  | # | ||||||
|  | #	def get_context_data(self,**kwargs): | ||||||
|  | #		nastaveni = get_object_or_404(Nastaveni) | ||||||
|  | #		context = super().get_context_data(**kwargs) | ||||||
|  | #		verejne = nastaveni.aktualni_cislo.verejne() | ||||||
|  | #		context['verejne'] = verejne | ||||||
|  | #		return context | ||||||
|  | 
 | ||||||
|  | def AktualniZadaniView(request): | ||||||
|  | 	nastaveni = get_object_or_404(Nastaveni) | ||||||
|  | 	verejne = nastaveni.aktualni_cislo.verejne() | ||||||
|  | 	return render(request, 'tvorba/zadani/AktualniZadani.html', | ||||||
|  | 				  {'nastaveni': nastaveni, | ||||||
|  | 				   'verejne': verejne, | ||||||
|  | 				   }, | ||||||
|  | 				  ) | ||||||
|  | 
 | ||||||
|  | def ZadaniTemataView(request): | ||||||
|  | 	nastaveni = get_object_or_404(Nastaveni) | ||||||
|  | 	verejne = nastaveni.aktualni_cislo.verejne() | ||||||
|  | 	akt_rocnik = nastaveni.aktualni_cislo.rocnik | ||||||
|  | 	temata = s.Tema.objects.filter(rocnik=akt_rocnik, stav='zadany') | ||||||
|  | 	return render(request, 'tvorba/tematka/rozcestnik.html', | ||||||
|  | 				  { | ||||||
|  | 					  'tematka': temata, | ||||||
|  | 					  'verejne': verejne, | ||||||
|  | 				  }, | ||||||
|  | 				  ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #	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, 'tvorba/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, 'tvorba/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, 'tvorba/tematka/rozcestnik.html', {"tematka": tematka, "rocnik" : nastaveni.aktualni_rocnik().rocnik}) | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | def ZadaniAktualniVysledkovkaView(request): | ||||||
|  | 	nastaveni = get_object_or_404(Nastaveni) | ||||||
|  | 	# Aktualni verejna vysledkovka | ||||||
|  | 	rocnik = nastaveni.aktualni_rocnik | ||||||
|  | 	context = {'vysledkovka': VysledkovkaRocniku(rocnik, True)} | ||||||
|  | 
 | ||||||
|  | 	# kdyz neni verejna vysledkovka, tak zobraz starou | ||||||
|  | 	if len(context['vysledkovka'].cisla_rocniku) == 0: | ||||||
|  | 		try: | ||||||
|  | 			minuly_rocnik = Rocnik.objects.get( | ||||||
|  | 				rocnik=(rocnik.rocnik-1)) | ||||||
|  | 			rocnik = minuly_rocnik | ||||||
|  | 
 | ||||||
|  | 			# Přepíšeme prázdnou výsledkovku výsledkovkou z minulého ročníku | ||||||
|  | 			context['vysledkovka'] = VysledkovkaRocniku(rocnik, True) | ||||||
|  | 		except ObjectDoesNotExist: | ||||||
|  | 			pass | ||||||
|  | 
 | ||||||
|  | 	context['rocnik'] = rocnik | ||||||
|  | 	return render( | ||||||
|  | 		request, | ||||||
|  | 		'tvorba/zadani/AktualniVysledkovka.html', | ||||||
|  | 		context | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### Titulni strana | ||||||
|  | 
 | ||||||
|  | def aktualni_temata(rocnik): | ||||||
|  | 	""" | ||||||
|  | 	Vrací PolymorphicQuerySet témat v daném ročníku, ke kterým se aktuálně dá něco odevzdat. | ||||||
|  | 	""" | ||||||
|  | 	return Tema.objects.filter(rocnik=rocnik, stav='zadany').order_by('kod') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### Archiv | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArchivView(generic.ListView): | ||||||
|  | 	model = Rocnik | ||||||
|  | 	template_name = 'tvorba/archiv/cisla.html' | ||||||
|  | 
 | ||||||
|  | 	def get_context_data(self, **kwargs): | ||||||
|  | 		context = super(ArchivView, self).get_context_data(**kwargs) | ||||||
|  | 
 | ||||||
|  | 		cisla = Cislo.objects.filter(poradi=1) | ||||||
|  | 		if not self.request.user.je_org: | ||||||
|  | 			cisla = cisla.filter(verejne_db=True) | ||||||
|  | 		urls ={} | ||||||
|  | 
 | ||||||
|  | 		for i, c in enumerate(cisla): | ||||||
|  | 			# Výchozí nastavení | ||||||
|  | 			if c.rocnik not in urls: | ||||||
|  | 				urls[c.rocnik] = op.join(settings.STATIC_URL, "tvorba", "no-picture.png") | ||||||
|  | 			# NOTE: tohle možná nastavuje poslední titulku | ||||||
|  | 			if c.titulka_nahled: | ||||||
|  | 				urls[c.rocnik] = c.titulka_nahled.url | ||||||
|  | 
 | ||||||
|  | 		context["object_list"] = urls | ||||||
|  | 
 | ||||||
|  | 		return context | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class RocnikView(generic.DetailView): | ||||||
|  | 	model = Rocnik | ||||||
|  | 	template_name = 'tvorba/archiv/rocnik.html' | ||||||
|  | 
 | ||||||
|  | 	# Vlastni ziskavani objektu z databaze podle (Rocnik.rocnik) | ||||||
|  | 	def get_object(self, queryset=None): | ||||||
|  | 		if queryset is None: | ||||||
|  | 			queryset = self.get_queryset() | ||||||
|  | 
 | ||||||
|  | 		return get_object_or_404(queryset,rocnik=self.kwargs.get('rocnik')) | ||||||
|  | 
 | ||||||
|  | 	def get_context_data(self, **kwargs): | ||||||
|  | 		context = super(RocnikView, self).get_context_data(**kwargs) | ||||||
|  | 		context["vysledkovka"] = VysledkovkaRocniku(context["rocnik"], True) | ||||||
|  | 		context["neprazdna_vysledkovka"] = len(context['vysledkovka'].cisla_rocniku) != 0 | ||||||
|  | 		context["vysledkovka_neverejna"] = VysledkovkaRocniku(context["rocnik"], False) | ||||||
|  | 		return context | ||||||
|  | 
 | ||||||
|  | def resiteleRocnikuCsvExportView(request, rocnik): | ||||||
|  | 	from personalni.views import dataResiteluCsvResponse | ||||||
|  | 	assert request.method in ('GET', 'HEAD') | ||||||
|  | 	return dataResiteluCsvResponse( | ||||||
|  | 		utils.resi_v_rocniku( | ||||||
|  | 			get_object_or_404(m.Rocnik, rocnik=rocnik) | ||||||
|  | 		) | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # FIXME: Pozor, výš je ještě jeden ProblemView! | ||||||
|  | #class ProblemView(generic.DetailView): | ||||||
|  | #	model = Problem | ||||||
|  | # | ||||||
|  | #	# Používáme funkci, protože přímo template_name neumí mít v přiřazení dost logiky. Ledaže by se to udělalo polymorfně... | ||||||
|  | #	def get_template_names(self, **kwargs): | ||||||
|  | #		# FIXME: Switch podle typu není hezký, ale nechtělo se mi to přepisovat celé. Správně by se tohle mělo řešit polymorfismem. | ||||||
|  | #		spravne_templaty = { | ||||||
|  | #				s.Uloha: "uloha", | ||||||
|  | #				s.Tema: "tema", | ||||||
|  | #				s.Konfera: "konfera", | ||||||
|  | #				s.Clanek: "clanek", | ||||||
|  | #				} | ||||||
|  | #		context = super().get_context_data(**kwargs) | ||||||
|  | #		return ['tvorba/archiv/problem_' + spravne_templaty[context['object'].__class__]  + '.html'] | ||||||
|  | # | ||||||
|  | #	def get_context_data(self, **kwargs): | ||||||
|  | #		context = super().get_context_data(**kwargs) | ||||||
|  | #		# Musí se používat context['object'], protože nevíme, jestli dostaneme úložku, téma, článek, .... a tyhle věci vyrábějí různé klíče. | ||||||
|  | #		if not context['object'].verejne() and not self.request.user.je_org: | ||||||
|  | #			raise PermissionDenied() | ||||||
|  | #		if isinstance(context['object'], Clanek): | ||||||
|  | #			context['reseni'] = Reseni.objects.filter(problem=context['object']).select_related('resitel').order_by('resitel__prijmeni') | ||||||
|  | #		return context | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class CisloView(generic.DetailView): | ||||||
|  | 	# FIXME zobrazování témátek a vůbec, teď je tam jen odkaz na číslo v pdf | ||||||
|  | 	model = Cislo | ||||||
|  | 	template_name = 'tvorba/archiv/cislo.html' | ||||||
|  | 
 | ||||||
|  | 	# Vlastni ziskavani objektu z databaze podle (Rocnik.rocnik) | ||||||
|  | 	def get_object(self, queryset=None): | ||||||
|  | 		if queryset is None: | ||||||
|  | 			queryset = self.get_queryset() | ||||||
|  | 		rocnik_arg = self.kwargs.get('rocnik') | ||||||
|  | 		poradi_arg = self.kwargs.get('cislo') | ||||||
|  | 		queryset = queryset.filter(rocnik__rocnik=rocnik_arg, poradi=poradi_arg) | ||||||
|  | 
 | ||||||
|  | 		try: | ||||||
|  | 			obj = queryset.get() | ||||||
|  | 		except queryset.model.DoesNotExist: | ||||||
|  | 			raise Http404(_("No %(verbose_name)s found matching the query") % | ||||||
|  | 						  {'verbose_name': queryset.model._meta.verbose_name}) | ||||||
|  | 		return obj | ||||||
|  | 
 | ||||||
|  | 	def get_context_data(self, **kwargs): | ||||||
|  | 		context = super(CisloView, self).get_context_data(**kwargs) | ||||||
|  | 
 | ||||||
|  | 		cislo = context['cislo'] | ||||||
|  | 		context['prevcislo'] = Cislo.objects.filter((Q(rocnik__lt=self.object.rocnik) | Q(poradi__lt=self.object.poradi))&Q(rocnik__lte=self.object.rocnik)).first() | ||||||
|  | 
 | ||||||
|  | 		deadliny = Deadline.objects.filter(cislo=cislo).reverse() | ||||||
|  | 		deadliny_s_vysledkovkami = [] | ||||||
|  | 
 | ||||||
|  | 		nadpisy = { | ||||||
|  | 			m.Deadline.TYP_CISLA: "Výsledkovka", | ||||||
|  | 			m.Deadline.TYP_PRVNI: "Výsledkovka do prvního deadlinu", | ||||||
|  | 			m.Deadline.TYP_PRVNI_A_SOUS: "Výsledkovka do prvního deadlinu a deadlinu pro účast na soustředění", | ||||||
|  | 			m.Deadline.TYP_SOUS: "Výsledkovka do deadlinu pro účast na soustředění", | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for deadline in deadliny: | ||||||
|  | 			if self.request.user.je_org | deadline.verejna_vysledkovka: | ||||||
|  | 				deadliny_s_vysledkovkami.append((deadline, nadpisy[deadline.typ], VysledkovkaCisla(cislo, not self.request.user.je_org, deadline))) | ||||||
|  | 
 | ||||||
|  | 		context['deadliny_s_vysledkovkami'] = deadliny_s_vysledkovkami | ||||||
|  | 		return context | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArchivTemataView(generic.ListView): | ||||||
|  | 	model = Problem | ||||||
|  | 	template_name = 'tvorba/archiv/temata.html' | ||||||
|  | 	queryset = Tema.objects.filter(stav=Problem.STAV_ZADANY).select_related('rocnik').order_by('rocnik', 'kod') | ||||||
|  | 
 | ||||||
|  | 	def get_context_data(self, *args, **kwargs): | ||||||
|  | 		ctx = super().get_context_data(*args, **kwargs) | ||||||
|  | 		ctx['rocniky'] = OrderedDict() | ||||||
|  | 		for rocnik, temata in groupby(ctx['object_list'], lambda tema: tema.rocnik): | ||||||
|  | 			ctx['rocniky'][rocnik] = list(temata) | ||||||
|  | 		return ctx | ||||||
|  | 
 | ||||||
|  | class OdmenyView(generic.TemplateView): | ||||||
|  | 	template_name = 'tvorba/archiv/odmeny.html' | ||||||
|  | 
 | ||||||
|  | 	def get_context_data(self, **kwargs): | ||||||
|  | 		context = super().get_context_data(**kwargs) | ||||||
|  | 		fromcislo = get_object_or_404(Cislo, rocnik=self.kwargs.get('frocnik'), poradi=self.kwargs.get('fcislo')) | ||||||
|  | 		tocislo = get_object_or_404(Cislo, rocnik=self.kwargs.get('trocnik'), poradi=self.kwargs.get('tcislo')) | ||||||
|  | 		resitele = utils.aktivniResitele(tocislo) | ||||||
|  | 
 | ||||||
|  | 		def get_diff(from_deadline: Deadline, to_deadline: Deadline): | ||||||
|  | 			frombody = body_resitelu(resitele=resitele, jen_verejne=False, do=from_deadline) | ||||||
|  | 			tobody = body_resitelu(resitele=resitele, jen_verejne=False, do=to_deadline) | ||||||
|  | 			outlist = [] | ||||||
|  | 			for resitel in resitele: | ||||||
|  | 				fbody = frombody.get(resitel.id, 0) | ||||||
|  | 				tbody = tobody.get(resitel.id, 0) | ||||||
|  | 				ftitul = resitel.get_titul(fbody) | ||||||
|  | 				ttitul = resitel.get_titul(tbody) | ||||||
|  | 				if ftitul != ttitul: | ||||||
|  | 					outlist.append({'jmeno': resitel.osoba.plne_jmeno(), 'ftitul': ftitul, 'ttitul': ttitul}) | ||||||
|  | 			return outlist | ||||||
|  | 
 | ||||||
|  | 		def posledni_deadline_oprava(cislo: Cislo) -> Deadline: | ||||||
|  | 			posledni_deadline = cislo.posledni_deadline | ||||||
|  | 			if posledni_deadline is None: | ||||||
|  | 				return Deadline.objects.filter(Q(cislo__poradi__lt=cislo.poradi, cislo__rocnik=cislo.rocnik) | Q(cislo__rocnik__rocnik__lt=cislo.rocnik.rocnik)).order_by("deadline").last() | ||||||
|  | 			return posledni_deadline | ||||||
|  | 
 | ||||||
|  | 		context["from_cislo"] = fromcislo | ||||||
|  | 		context["to_cislo"] = tocislo | ||||||
|  | 		from_deadline = posledni_deadline_oprava(fromcislo) | ||||||
|  | 		to_deadline = posledni_deadline_oprava(tocislo) | ||||||
|  | 		context["from_deadline"] = from_deadline | ||||||
|  | 		context["to_deadline"] = to_deadline | ||||||
|  | 		context["zmeny"] = get_diff(from_deadline, to_deadline) | ||||||
|  | 
 | ||||||
|  | 		return context | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### Generovani vysledkovky | ||||||
|  | 
 | ||||||
|  | class CisloVysledkovkaView(CisloView): | ||||||
|  | 	"""View vytvořené pro stránku zobrazující výsledkovku čísla v TeXu.""" | ||||||
|  | 
 | ||||||
|  | 	model = Cislo | ||||||
|  | 	template_name = 'tvorba/archiv/cislo_vysledkovka.tex' | ||||||
|  | 	#content_type = 'application/x-tex; charset=UTF8' | ||||||
|  | 	#umozni rovnou stahnout TeXovsky dokument | ||||||
|  | 	content_type = 'text/plain; charset=UTF8' | ||||||
|  | 	#vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani | ||||||
|  | 
 | ||||||
|  | 	def get_context_data(self, **kwargs): | ||||||
|  | 		context = super(CisloVysledkovkaView, self).get_context_data() | ||||||
|  | 		cislo = context['cislo'] | ||||||
|  | 
 | ||||||
|  | 		cislopred = cislo.predchozi() | ||||||
|  | 		if cislopred is not None: | ||||||
|  | 			context['vysledkovka'] = VysledkovkaDoTeXu( | ||||||
|  | 				cislo, | ||||||
|  | 				od_vyjma=cislopred.zlomovy_deadline_pro_papirove_cislo(), | ||||||
|  | 				do_vcetne=cislo.zlomovy_deadline_pro_papirove_cislo(), | ||||||
|  | 			) | ||||||
|  | 		else: | ||||||
|  | 			context['vysledkovka'] = VysledkovkaCisla( | ||||||
|  | 				cislo, | ||||||
|  | 				jen_verejne=False, | ||||||
|  | 				do_deadlinu=cislo.zlomovy_deadline_pro_papirove_cislo(), | ||||||
|  | 			) | ||||||
|  | 		return context | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Podle předchozího | ||||||
|  | class PosledniCisloVysledkovkaView(generic.DetailView): | ||||||
|  | 	"""View vytvořené pro zobrazení výsledkovky posledního čísla v TeXu.""" | ||||||
|  | 
 | ||||||
|  | 	model = Rocnik | ||||||
|  | 	template_name = 'tvorba/archiv/cislo_vysledkovka.tex' | ||||||
|  | 	content_type = 'text/plain; charset=UTF8' | ||||||
|  | 
 | ||||||
|  | 	def get_object(self, queryset=None): | ||||||
|  | 		if queryset is None: | ||||||
|  | 			queryset = self.get_queryset() | ||||||
|  | 		rocnik_arg = self.kwargs.get('rocnik') | ||||||
|  | 		queryset = queryset.filter(rocnik=rocnik_arg) | ||||||
|  | 
 | ||||||
|  | 		try: | ||||||
|  | 			obj = queryset.get() | ||||||
|  | 		except queryset.model.DoesNotExist: | ||||||
|  | 			raise Http404(_("No %(verbose_name)s found matching the query") % | ||||||
|  | 						  {'verbose_name': queryset.model._meta.verbose_name}) | ||||||
|  | 		return obj | ||||||
|  | 
 | ||||||
|  | 	def get_context_data(self, **kwargs): | ||||||
|  | 		context = super(PosledniCisloVysledkovkaView, self).get_context_data() | ||||||
|  | 		rocnik = context['rocnik'] | ||||||
|  | 		cislo = rocnik.cisla.order_by("poradi").filter(deadline_v_cisle__isnull=False).last() | ||||||
|  | 		if cislo is None: | ||||||
|  | 			raise Http404(f"Ročník {rocnik.rocnik} nemá číslo s deadlinem.") | ||||||
|  | 		cislopred = cislo.predchozi() | ||||||
|  | 		context['vysledkovka'] = VysledkovkaDoTeXu( | ||||||
|  | 			cislo, | ||||||
|  | 			od_vyjma=cislopred.zlomovy_deadline_pro_papirove_cislo(), | ||||||
|  | 			do_vcetne=cislo.deadline_v_cisle.order_by("deadline").last(), | ||||||
|  | 		) | ||||||
|  | 		return context | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class RocnikVysledkovkaView(RocnikView): | ||||||
|  | 	""" View vytvořené pro stránku zobrazující výsledkovku ročníku v TeXu.""" | ||||||
|  | 	model = Rocnik | ||||||
|  | 	template_name = 'tvorba/archiv/rocnik_vysledkovka.tex' | ||||||
|  | 	#content_type = 'application/x-tex; charset=UTF8' | ||||||
|  | 	#umozni rovnou stahnout TeXovsky dokument | ||||||
|  | 	content_type = 'text/plain; charset=UTF8' | ||||||
|  | #vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani | ||||||
|  | 
 | ||||||
|  | def cisloObalkyView(request, rocnik, cislo): | ||||||
|  | 	realne_cislo = get_object_or_404(Cislo, poradi=cislo, rocnik__rocnik=rocnik) | ||||||
|  | 	return personalni.views.obalkyView(request, utils.aktivniResitele(realne_cislo)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### Tituly | ||||||
|  | def TitulyViewRocnik(request, rocnik): | ||||||
|  | 	return TitulyView(request, rocnik, None) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def TitulyView(request, rocnik, cislo): | ||||||
|  | 	""" View pro stažení makra titulů v TeXu.""" | ||||||
|  | 	rocnik_obj = get_object_or_404(Rocnik, rocnik = rocnik) | ||||||
|  | 	resitele = Resitel.objects.filter(rok_maturity__gte = rocnik_obj.prvni_rok) | ||||||
|  | 
 | ||||||
|  | 	asciijmena = [] | ||||||
|  | 	jmenovci = False # detekuje, zda jsou dva řešitelé jmenovci (modulo nabodeníčka), | ||||||
|  | 	# pokud ano, vrátí se jako true | ||||||
|  | 	if cislo is not None: | ||||||
|  | 		cislo_obj = get_object_or_404(Cislo, rocnik=rocnik_obj, poradi=cislo) | ||||||
|  | 		slovnik_s_body = body_resitelu(do=cislo_obj.zlomovy_deadline_pro_papirove_cislo(), jen_verejne=False) | ||||||
|  | 	else: | ||||||
|  | 		slovnik_s_body = body_resitelu(do=Deadline.objects.filter(cislo__rocnik=rocnik_obj).last(), jen_verejne=False) | ||||||
|  | 
 | ||||||
|  | 	for resitel in resitele: | ||||||
|  | 		resitel.titul = resitel.get_titul(slovnik_s_body[resitel.id]) | ||||||
|  | 		jmeno = resitel.osoba.jmeno+resitel.osoba.prijmeni | ||||||
|  | 		# převedeme jména a příjmení řešitelů do ASCII | ||||||
|  | 		ascii_jmeno_bytes = unicodedata.normalize('NFKD', jmeno).encode("ascii","ignore") | ||||||
|  | 		# vrátí se byte string, převedeme na standardní string | ||||||
|  | 		ascii_jmeno_divnoznaky = str(ascii_jmeno_bytes, "utf-8", "ignore").replace(" ","") | ||||||
|  | 		resitel.ascii = ''.join(a for a in ascii_jmeno_divnoznaky if a.isalnum()) | ||||||
|  | 		if resitel.ascii not in asciijmena: | ||||||
|  | 			asciijmena.append(resitel.ascii) | ||||||
|  | 		else: | ||||||
|  | 			jmenovci = True | ||||||
|  | 
 | ||||||
|  | 	return render(request, 'tvorba/archiv/tituly.tex', | ||||||
|  | 				  {'resitele': resitele,'jmenovci':jmenovci},content_type="text/plain") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### Články | ||||||
|  | def group_by_rocnik(clanky): | ||||||
|  | 	''' Vezme zadaný seznam článků a seskupí je podle ročníku. | ||||||
|  | 	Vrátí seznam seznamů článků ze stejného ročníku.''' | ||||||
|  | 	if len(clanky) == 0: | ||||||
|  | 		return clanky | ||||||
|  | 	clanky.order_by('cislo__rocnik__rocnik') | ||||||
|  | 	skupiny_clanku = [] | ||||||
|  | 	skupina = [] | ||||||
|  | 
 | ||||||
|  | 	rocnik = clanky.first().cislo.rocnik.rocnik # první ročník | ||||||
|  | 	for clanek in clanky: | ||||||
|  | 		if clanek.cislo.rocnik.rocnik == rocnik: | ||||||
|  | 			skupina.append(clanek) | ||||||
|  | 		else: | ||||||
|  | 			skupiny_clanku.append(skupina) | ||||||
|  | 			skupina = [] | ||||||
|  | 			skupina.append(clanek) | ||||||
|  | 			rocnik = clanek.cislo.rocnik.rocnik | ||||||
|  | 	skupiny_clanku.append(skupina) | ||||||
|  | 	return skupiny_clanku | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # FIXME: clanky jsou vsechny, pokud budou i neresitelske, tak se take zobrazi | ||||||
|  | # FIXME: Původně tu byl kód přímo v těle třídy, což rozbíjelo migrace. Opravil jsem, ale vůbec nevím, jestli to funguje. | ||||||
|  | class ClankyResitelView(generic.ListView): | ||||||
|  | 	model = Problem | ||||||
|  | 	template_name = 'tvorba/clanky/resitelske_clanky.html' | ||||||
|  | 
 | ||||||
|  | 	# FIXME: QuerySet není pole! | ||||||
|  | 	def get_queryset(self): | ||||||
|  | 		clanky = Clanek.objects.filter(stav=Problem.STAV_VYRESENY).select_related('cislo__rocnik').order_by('-cislo__rocnik__rocnik') | ||||||
|  | 		queryset = [] | ||||||
|  | 		skupiny_clanku = group_by_rocnik(clanky) | ||||||
|  | 		for skupina in skupiny_clanku: | ||||||
|  | 			skupina.sort(key=lambda clanek: clanek.kod_v_rocniku) | ||||||
|  | 			for clanek in skupina: | ||||||
|  | 				queryset.append(clanek) | ||||||
|  | 		return queryset | ||||||
|  | 
 | ||||||
|  | # FIXME: pokud chceme orgoclanky, tak nejak zavest do modelu a podle toho odkomentovat a upravit | ||||||
|  | #class ClankyOrganizatorView(generic.ListView)<F12>: | ||||||
|  | #	model = Problem | ||||||
|  | #	template_name = 'tvorba/clanky/organizatorske_clanky.html' | ||||||
|  | #	queryset = Problem.objects.filter(stav=Problem.STAV_ZADANY).select_related('cislo_zadani__rocnik').order_by('-cislo_zadani__rocnik__rocnik', 'kod') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class AktualniRocnikRedirectView(RedirectView): | ||||||
|  | 	permanent=False | ||||||
|  | 	pattern_name = 'seminar_rocnik' | ||||||
|  | 
 | ||||||
|  | 	def get_redirect_url(self, *args, **kwargs): | ||||||
|  | 		aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik.rocnik | ||||||
|  | 		return super().get_redirect_url(rocnik=aktualni_rocnik, *args, **kwargs) | ||||||
|  | @ -8,15 +8,13 @@ from django.http import Http404 | ||||||
| from django.db.models import Q, Sum, Count | from django.db.models import Q, Sum, Count | ||||||
| from django.views.generic.base import RedirectView | from django.views.generic.base import RedirectView | ||||||
| from django.core.exceptions import PermissionDenied | from django.core.exceptions import PermissionDenied | ||||||
| from django.contrib.staticfiles.finders import find |  | ||||||
| 
 | 
 | ||||||
| import seminar.models as s | import seminar.models as s | ||||||
| import seminar.models as m | import seminar.models as m | ||||||
| from seminar.models import Problem, Cislo, Reseni, Nastaveni, Rocnik, \ | from seminar.models import Problem, Cislo, Reseni, Nastaveni, Rocnik, \ | ||||||
| 	Organizator, Resitel, Novinky, Tema, Clanek, \ | 	Resitel, Novinky, Tema, Clanek, \ | ||||||
| 	Deadline  # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci | 	Deadline  # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci | ||||||
| #from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva | #from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva | ||||||
| from seminar import utils |  | ||||||
| from treenode import treelib | from treenode import treelib | ||||||
| import treenode.templatetags as tnltt | import treenode.templatetags as tnltt | ||||||
| import treenode.serializers as vr | import treenode.serializers as vr | ||||||
|  | @ -24,22 +22,18 @@ from vysledkovky.utils import body_resitelu, VysledkovkaCisla, \ | ||||||
| 	VysledkovkaRocniku, VysledkovkaDoTeXu | 	VysledkovkaRocniku, VysledkovkaDoTeXu | ||||||
| 
 | 
 | ||||||
| from datetime import date, datetime | from datetime import date, datetime | ||||||
| from django.utils import timezone |  | ||||||
| from itertools import groupby | from itertools import groupby | ||||||
| from collections import OrderedDict | from collections import OrderedDict | ||||||
| import tempfile |  | ||||||
| import subprocess |  | ||||||
| import shutil |  | ||||||
| import os | import os | ||||||
| import os.path as op | import os.path as op | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| import unicodedata | import unicodedata | ||||||
| import logging | import logging | ||||||
| import time | import time | ||||||
| from collections.abc import Sequence |  | ||||||
| import http |  | ||||||
| 
 | 
 | ||||||
| from seminar.utils import aktivniResitele | import personalni.views | ||||||
|  | 
 | ||||||
|  | from .. import utils | ||||||
| 
 | 
 | ||||||
| # ze starého modelu | # ze starého modelu | ||||||
| #def verejna_temata(rocnik): | #def verejna_temata(rocnik): | ||||||
|  | @ -88,7 +82,7 @@ def get_problemy_k_tematu(tema): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #class AktualniZadaniView(generic.TemplateView): | #class AktualniZadaniView(generic.TemplateView): | ||||||
| #	template_name = 'seminar/treenode.html' | #	template_name = 'treenode/treenode.html' | ||||||
| 
 | 
 | ||||||
| # TODO Co chceme vlastně zobrazovat na této stránce? Zatím je zde aktuální číslo, ale může tu být cokoli jiného... | # TODO Co chceme vlastně zobrazovat na této stránce? Zatím je zde aktuální číslo, ale může tu být cokoli jiného... | ||||||
| #class AktualniZadaniView(TreeNodeView): | #class AktualniZadaniView(TreeNodeView): | ||||||
|  | @ -106,7 +100,7 @@ def get_problemy_k_tematu(tema): | ||||||
| def AktualniZadaniView(request): | def AktualniZadaniView(request): | ||||||
| 	nastaveni = get_object_or_404(Nastaveni) | 	nastaveni = get_object_or_404(Nastaveni) | ||||||
| 	verejne = nastaveni.aktualni_cislo.verejne() | 	verejne = nastaveni.aktualni_cislo.verejne() | ||||||
| 	return render(request, 'seminar/zadani/AktualniZadani.html', | 	return render(request, 'tvorba/zadani/AktualniZadani.html', | ||||||
| 			{'nastaveni': nastaveni, | 			{'nastaveni': nastaveni, | ||||||
| 			 'verejne': verejne, | 			 'verejne': verejne, | ||||||
| 				}, | 				}, | ||||||
|  | @ -117,7 +111,7 @@ def ZadaniTemataView(request): | ||||||
| 	verejne = nastaveni.aktualni_cislo.verejne() | 	verejne = nastaveni.aktualni_cislo.verejne() | ||||||
| 	akt_rocnik = nastaveni.aktualni_cislo.rocnik | 	akt_rocnik = nastaveni.aktualni_cislo.rocnik | ||||||
| 	temata = s.Tema.objects.filter(rocnik=akt_rocnik, stav='zadany') | 	temata = s.Tema.objects.filter(rocnik=akt_rocnik, stav='zadany') | ||||||
| 	return render(request, 'seminar/tematka/rozcestnik.html', | 	return render(request, 'tvorba/tematka/rozcestnik.html', | ||||||
| 			{ | 			{ | ||||||
| 			 'tematka': temata, | 			 'tematka': temata, | ||||||
| 			 'verejne': verejne, | 			 'verejne': verejne, | ||||||
|  | @ -132,7 +126,7 @@ def ZadaniTemataView(request): | ||||||
| #			t.prispevky = t.prispevek_set.filter(problem=t) | #			t.prispevky = t.prispevek_set.filter(problem=t) | ||||||
| #		else: | #		else: | ||||||
| #			t.prispevky = t.prispevek_set.filter(problem=t, zverejnit=True) | #			t.prispevky = t.prispevek_set.filter(problem=t, zverejnit=True) | ||||||
| #	return render(request, 'seminar/zadani/Temata.html', | #	return render(request, 'tvorba/zadani/Temata.html', | ||||||
| #		{ | #		{ | ||||||
| #			'temata': temata, | #			'temata': temata, | ||||||
| #		} | #		} | ||||||
|  | @ -151,7 +145,7 @@ def ZadaniTemataView(request): | ||||||
| #		if node.isinstance(node, s.PohadkaNode): # Mohu ignorovat, má pod sebou | #		if node.isinstance(node, s.PohadkaNode): # Mohu ignorovat, má pod sebou | ||||||
| #			pass | #			pass | ||||||
| # | # | ||||||
| #	return render(request, 'seminar/tematka/toaletak.html', {}) | #	return render(request, 'tvorba/tematka/toaletak.html', {}) | ||||||
| #	 | #	 | ||||||
| # | # | ||||||
| #def TemataRozcestnikView(request): | #def TemataRozcestnikView(request): | ||||||
|  | @ -187,7 +181,7 @@ def ZadaniTemataView(request): | ||||||
| #			"obrazek": tematko_object.obrazek, | #			"obrazek": tematko_object.obrazek, | ||||||
| #			"cisla" : cisla | #			"cisla" : cisla | ||||||
| #		}) | #		}) | ||||||
| #	return render(request, 'seminar/tematka/rozcestnik.html', {"tematka": tematka, "rocnik" : nastaveni.aktualni_rocnik().rocnik}) | #	return render(request, 'tvorba/tematka/rozcestnik.html', {"tematka": tematka, "rocnik" : nastaveni.aktualni_rocnik().rocnik}) | ||||||
| #	 | #	 | ||||||
| 
 | 
 | ||||||
| def ZadaniAktualniVysledkovkaView(request): | def ZadaniAktualniVysledkovkaView(request): | ||||||
|  | @ -211,25 +205,13 @@ def ZadaniAktualniVysledkovkaView(request): | ||||||
| 	context['rocnik'] = rocnik | 	context['rocnik'] = rocnik | ||||||
| 	return render( | 	return render( | ||||||
| 		request, | 		request, | ||||||
| 		'seminar/zadani/AktualniVysledkovka.html', | 		'tvorba/zadani/AktualniVysledkovka.html', | ||||||
| 		context | 		context | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ### Titulni strana | ### 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() |  | ||||||
| 	if not user.je_org: |  | ||||||
| 		qs = qs.filter(zverejneno=True) |  | ||||||
| 	return qs.order_by('-datum') |  | ||||||
| 
 |  | ||||||
| def aktualni_temata(rocnik): | def aktualni_temata(rocnik): | ||||||
| 	""" | 	""" | ||||||
| 	Vrací PolymorphicQuerySet témat v daném ročníku, ke kterým se aktuálně dá něco odevzdat. | 	Vrací PolymorphicQuerySet témat v daném ročníku, ke kterým se aktuálně dá něco odevzdat. | ||||||
|  | @ -237,73 +219,12 @@ def aktualni_temata(rocnik): | ||||||
| 	return Tema.objects.filter(rocnik=rocnik, stav='zadany').order_by('kod') | 	return Tema.objects.filter(rocnik=rocnik, stav='zadany').order_by('kod') | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class TitulniStranaView(generic.ListView): |  | ||||||
| 	template_name= 'seminar/titulnistrana/titulnistrana.html' |  | ||||||
| 
 |  | ||||||
| 	def get_queryset(self): |  | ||||||
| 		return spravne_novinky(self.request)[:3] |  | ||||||
| 
 |  | ||||||
| 	def get_context_data(self, **kwargs): |  | ||||||
| 		context = super(TitulniStranaView, self).get_context_data(**kwargs) |  | ||||||
| 		nastaveni = get_object_or_404(Nastaveni) |  | ||||||
| 
 |  | ||||||
| 		deadline = m.Deadline.objects.filter(deadline__gte=timezone.now()).order_by("deadline").first() |  | ||||||
| 		context['nejblizsi_deadline'] = deadline |  | ||||||
| 
 |  | ||||||
| 		# Aktuální témata |  | ||||||
| 		nazvy_a_odkazy_na_aktualni_temata = [] |  | ||||||
| 		akt_temata = aktualni_temata(nastaveni.aktualni_rocnik) |  | ||||||
| 
 |  | ||||||
| 		for tema in akt_temata: |  | ||||||
| 			# FIXME: netuším, jestli funguje tema.verejne_url(), nemáme testdata na témátka - je to asi url vzhledem k ročníku |  | ||||||
| 			nazvy_a_odkazy_na_aktualni_temata.append({'nazev':tema.nazev,'url':tema.verejne_url()}) |  | ||||||
| 
 |  | ||||||
| 		context['aktualni_temata'] = nazvy_a_odkazy_na_aktualni_temata |  | ||||||
| 
 |  | ||||||
| 		print(context) |  | ||||||
| 
 |  | ||||||
| 		return context |  | ||||||
| 
 |  | ||||||
| class StareNovinkyView(generic.ListView): |  | ||||||
| 	template_name = 'seminar/stare_novinky.html' |  | ||||||
| 
 |  | ||||||
| 	def get_queryset(self): |  | ||||||
| 		return spravne_novinky(self.request) |  | ||||||
| 
 |  | ||||||
| ### Co je M&M |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Organizatori |  | ||||||
| def aktivniOrganizatori(datum=timezone.now()): |  | ||||||
| 	return Organizator.objects.exclude( |  | ||||||
| 		organizuje_do__isnull=False, |  | ||||||
| 		organizuje_do__lt=datum |  | ||||||
| 	).order_by('osoba__jmeno') |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class CojemamOrganizatoriView(generic.ListView): |  | ||||||
| 	model = Organizator |  | ||||||
| 	template_name = 'seminar/cojemam/organizatori.html' |  | ||||||
| 	queryset = aktivniOrganizatori() |  | ||||||
| 
 |  | ||||||
| 	def get_context_data(self, **kwargs): |  | ||||||
| 		context = super(CojemamOrganizatoriView, self).get_context_data(**kwargs) |  | ||||||
| 		context['aktivni'] = True |  | ||||||
| 		return context |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class CojemamOrganizatoriStariView(generic.ListView): |  | ||||||
| 	model = Organizator |  | ||||||
| 	template_name = 'seminar/cojemam/organizatori.html' |  | ||||||
| 	queryset = Organizator.objects.exclude( |  | ||||||
| 		id__in=aktivniOrganizatori()).order_by('-organizuje_do') |  | ||||||
| 
 |  | ||||||
| ### Archiv | ### Archiv | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class ArchivView(generic.ListView): | class ArchivView(generic.ListView): | ||||||
| 	model = Rocnik | 	model = Rocnik | ||||||
| 	template_name='seminar/archiv/cisla.html' | 	template_name = 'tvorba/archiv/cisla.html' | ||||||
| 
 | 
 | ||||||
| 	def get_context_data(self, **kwargs): | 	def get_context_data(self, **kwargs): | ||||||
| 		context = super(ArchivView, self).get_context_data(**kwargs) | 		context = super(ArchivView, self).get_context_data(**kwargs) | ||||||
|  | @ -316,7 +237,7 @@ class ArchivView(generic.ListView): | ||||||
| 		for i, c in enumerate(cisla): | 		for i, c in enumerate(cisla): | ||||||
| 			# Výchozí nastavení | 			# Výchozí nastavení | ||||||
| 			if c.rocnik not in urls: | 			if c.rocnik not in urls: | ||||||
| 				urls[c.rocnik] = op.join(settings.STATIC_URL, "images", "no-picture.png") | 				urls[c.rocnik] = op.join(settings.STATIC_URL, "tvorba", "no-picture.png") | ||||||
| 			# NOTE: tohle možná nastavuje poslední titulku | 			# NOTE: tohle možná nastavuje poslední titulku | ||||||
| 			if c.titulka_nahled: | 			if c.titulka_nahled: | ||||||
| 				urls[c.rocnik] = c.titulka_nahled.url | 				urls[c.rocnik] = c.titulka_nahled.url | ||||||
|  | @ -331,7 +252,7 @@ class ArchivView(generic.ListView): | ||||||
| 
 | 
 | ||||||
| class RocnikView(generic.DetailView): | class RocnikView(generic.DetailView): | ||||||
| 	model = Rocnik | 	model = Rocnik | ||||||
| 	template_name = 'seminar/archiv/rocnik.html' | 	template_name = 'tvorba/archiv/rocnik.html' | ||||||
| 
 | 
 | ||||||
| 	# Vlastni ziskavani objektu z databaze podle (Rocnik.rocnik) | 	# Vlastni ziskavani objektu z databaze podle (Rocnik.rocnik) | ||||||
| 	def get_object(self, queryset=None): | 	def get_object(self, queryset=None): | ||||||
|  | @ -371,7 +292,7 @@ def resiteleRocnikuCsvExportView(request, rocnik): | ||||||
| #				s.Clanek: "clanek", | #				s.Clanek: "clanek", | ||||||
| #				} | #				} | ||||||
| #		context = super().get_context_data(**kwargs) | #		context = super().get_context_data(**kwargs) | ||||||
| #		return ['seminar/archiv/problem_' + spravne_templaty[context['object'].__class__]  + '.html'] | #		return ['tvorba/archiv/problem_' + spravne_templaty[context['object'].__class__]  + '.html'] | ||||||
| # | # | ||||||
| #	def get_context_data(self, **kwargs): | #	def get_context_data(self, **kwargs): | ||||||
| #		context = super().get_context_data(**kwargs) | #		context = super().get_context_data(**kwargs) | ||||||
|  | @ -387,7 +308,7 @@ def resiteleRocnikuCsvExportView(request, rocnik): | ||||||
| class CisloView(generic.DetailView): | class CisloView(generic.DetailView): | ||||||
| 	# FIXME zobrazování témátek a vůbec, teď je tam jen odkaz na číslo v pdf | 	# FIXME zobrazování témátek a vůbec, teď je tam jen odkaz na číslo v pdf | ||||||
| 	model = Cislo | 	model = Cislo | ||||||
| 	template_name = 'seminar/archiv/cislo.html' | 	template_name = 'tvorba/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): | 	def get_object(self, queryset=None): | ||||||
|  | @ -430,7 +351,7 @@ class CisloView(generic.DetailView): | ||||||
| 
 | 
 | ||||||
| class ArchivTemataView(generic.ListView): | class ArchivTemataView(generic.ListView): | ||||||
| 	model = Problem | 	model = Problem | ||||||
| 	template_name = 'seminar/archiv/temata.html' | 	template_name = 'tvorba/archiv/temata.html' | ||||||
| 	queryset = Tema.objects.filter(stav=Problem.STAV_ZADANY).select_related('rocnik').order_by('rocnik', 'kod') | 	queryset = Tema.objects.filter(stav=Problem.STAV_ZADANY).select_related('rocnik').order_by('rocnik', 'kod') | ||||||
| 
 | 
 | ||||||
| 	def get_context_data(self, *args, **kwargs): | 	def get_context_data(self, *args, **kwargs): | ||||||
|  | @ -441,13 +362,13 @@ class ArchivTemataView(generic.ListView): | ||||||
| 		return ctx | 		return ctx | ||||||
| 
 | 
 | ||||||
| class OdmenyView(generic.TemplateView): | class OdmenyView(generic.TemplateView): | ||||||
| 	template_name = 'seminar/archiv/odmeny.html' | 	template_name = 'tvorba/archiv/odmeny.html' | ||||||
| 
 | 
 | ||||||
| 	def get_context_data(self, **kwargs): | 	def get_context_data(self, **kwargs): | ||||||
| 		context = super().get_context_data(**kwargs) | 		context = super().get_context_data(**kwargs) | ||||||
| 		fromcislo = get_object_or_404(Cislo, rocnik=self.kwargs.get('frocnik'), poradi=self.kwargs.get('fcislo')) | 		fromcislo = get_object_or_404(Cislo, rocnik=self.kwargs.get('frocnik'), poradi=self.kwargs.get('fcislo')) | ||||||
| 		tocislo = get_object_or_404(Cislo, rocnik=self.kwargs.get('trocnik'), poradi=self.kwargs.get('tcislo')) | 		tocislo = get_object_or_404(Cislo, rocnik=self.kwargs.get('trocnik'), poradi=self.kwargs.get('tcislo')) | ||||||
| 		resitele = aktivniResitele(tocislo) | 		resitele = utils.aktivniResitele(tocislo) | ||||||
| 
 | 
 | ||||||
| 		def get_diff(from_deadline: Deadline, to_deadline: Deadline): | 		def get_diff(from_deadline: Deadline, to_deadline: Deadline): | ||||||
| 			frombody = body_resitelu(resitele=resitele, jen_verejne=False, do=from_deadline) | 			frombody = body_resitelu(resitele=resitele, jen_verejne=False, do=from_deadline) | ||||||
|  | @ -487,7 +408,7 @@ class CisloVysledkovkaView(CisloView): | ||||||
| 	"""View vytvořené pro stránku zobrazující výsledkovku čísla v TeXu.""" | 	"""View vytvořené pro stránku zobrazující výsledkovku čísla v TeXu.""" | ||||||
| 
 | 
 | ||||||
| 	model = Cislo | 	model = Cislo | ||||||
| 	template_name = 'seminar/archiv/cislo_vysledkovka.tex' | 	template_name = 'tvorba/archiv/cislo_vysledkovka.tex' | ||||||
| 	#content_type = 'application/x-tex; charset=UTF8' | 	#content_type = 'application/x-tex; charset=UTF8' | ||||||
| 	#umozni rovnou stahnout TeXovsky dokument | 	#umozni rovnou stahnout TeXovsky dokument | ||||||
| 	content_type = 'text/plain; charset=UTF8' | 	content_type = 'text/plain; charset=UTF8' | ||||||
|  | @ -518,7 +439,7 @@ class PosledniCisloVysledkovkaView(generic.DetailView): | ||||||
| 	"""View vytvořené pro zobrazení výsledkovky posledního čísla v TeXu.""" | 	"""View vytvořené pro zobrazení výsledkovky posledního čísla v TeXu.""" | ||||||
| 
 | 
 | ||||||
| 	model = Rocnik | 	model = Rocnik | ||||||
| 	template_name = 'seminar/archiv/cislo_vysledkovka.tex' | 	template_name = 'tvorba/archiv/cislo_vysledkovka.tex' | ||||||
| 	content_type = 'text/plain; charset=UTF8' | 	content_type = 'text/plain; charset=UTF8' | ||||||
| 
 | 
 | ||||||
| 	def get_object(self, queryset=None): | 	def get_object(self, queryset=None): | ||||||
|  | @ -552,7 +473,7 @@ class PosledniCisloVysledkovkaView(generic.DetailView): | ||||||
| class RocnikVysledkovkaView(RocnikView): | class RocnikVysledkovkaView(RocnikView): | ||||||
| 	""" View vytvořené pro stránku zobrazující výsledkovku ročníku v TeXu.""" | 	""" View vytvořené pro stránku zobrazující výsledkovku ročníku v TeXu.""" | ||||||
| 	model = Rocnik | 	model = Rocnik | ||||||
| 	template_name = 'seminar/archiv/rocnik_vysledkovka.tex' | 	template_name = 'tvorba/archiv/rocnik_vysledkovka.tex' | ||||||
| 	#content_type = 'application/x-tex; charset=UTF8' | 	#content_type = 'application/x-tex; charset=UTF8' | ||||||
| 	#umozni rovnou stahnout TeXovsky dokument | 	#umozni rovnou stahnout TeXovsky dokument | ||||||
| 	content_type = 'text/plain; charset=UTF8' | 	content_type = 'text/plain; charset=UTF8' | ||||||
|  | @ -560,31 +481,9 @@ class RocnikVysledkovkaView(RocnikView): | ||||||
| 
 | 
 | ||||||
| def cisloObalkyView(request, rocnik, cislo): | def cisloObalkyView(request, rocnik, cislo): | ||||||
| 	realne_cislo = get_object_or_404(Cislo, poradi=cislo, rocnik__rocnik=rocnik) | 	realne_cislo = get_object_or_404(Cislo, poradi=cislo, rocnik__rocnik=rocnik) | ||||||
| 	return obalkyView(request, aktivniResitele(realne_cislo)) | 	return personalni.views.obalkyView(request, utils.aktivniResitele(realne_cislo)) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def obalkyView(request, resitele): |  | ||||||
| 	if len(resitele) == 0: |  | ||||||
| 		return HttpResponse( |  | ||||||
| 			render(request, 'universal.html', { |  | ||||||
| 				'title': 'Není pro koho vyrobit obálky.', |  | ||||||
| 				'text': 'Právě ses pokusil/a vygenerovat obálky pro prázdnou množinu lidí. Můžeš to zkusit změnit, případně se zeptej webařů :-)', |  | ||||||
| 				}), |  | ||||||
| 			status=http.HTTPStatus.NOT_FOUND, |  | ||||||
| 			) |  | ||||||
| 
 |  | ||||||
| 	tex = render(request,'seminar/archiv/obalky.tex', {'resitele': resitele}).content |  | ||||||
| 
 |  | ||||||
| 	with tempfile.TemporaryDirectory() as tempdir: |  | ||||||
| 		with open(tempdir+"/obalky.tex","w") as texfile: |  | ||||||
| 			texfile.write(tex.decode()) |  | ||||||
| 		shutil.copy(find('seminar/lisak.pdf'), tempdir) |  | ||||||
| 		subprocess.call(["pdflatex","obalky.tex"], cwd = tempdir) |  | ||||||
| 
 |  | ||||||
| 		with open(tempdir+"/obalky.pdf","rb") as pdffile: |  | ||||||
| 			response = HttpResponse(pdffile.read(), content_type='application/pdf') |  | ||||||
| 	return response |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| ### Tituly | ### Tituly | ||||||
| def TitulyViewRocnik(request, rocnik): | def TitulyViewRocnik(request, rocnik): | ||||||
|  | @ -618,7 +517,7 @@ def TitulyView(request, rocnik, cislo): | ||||||
| 		else: | 		else: | ||||||
| 			jmenovci = True | 			jmenovci = True | ||||||
| 
 | 
 | ||||||
| 	return render(request, 'seminar/archiv/tituly.tex', | 	return render(request, 'tvorba/archiv/tituly.tex', | ||||||
| 		{'resitele': resitele,'jmenovci':jmenovci},content_type="text/plain") | 		{'resitele': resitele,'jmenovci':jmenovci},content_type="text/plain") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -649,7 +548,7 @@ def group_by_rocnik(clanky): | ||||||
| # FIXME: Původně tu byl kód přímo v těle třídy, což rozbíjelo migrace. Opravil jsem, ale vůbec nevím, jestli to funguje. | # FIXME: Původně tu byl kód přímo v těle třídy, což rozbíjelo migrace. Opravil jsem, ale vůbec nevím, jestli to funguje. | ||||||
| class ClankyResitelView(generic.ListView): | class ClankyResitelView(generic.ListView): | ||||||
| 	model = Problem | 	model = Problem | ||||||
| 	template_name = 'seminar/clanky/resitelske_clanky.html' | 	template_name = 'tvorba/clanky/resitelske_clanky.html' | ||||||
| 
 | 
 | ||||||
| 	# FIXME: QuerySet není pole! | 	# FIXME: QuerySet není pole! | ||||||
| 	def get_queryset(self): | 	def get_queryset(self): | ||||||
|  | @ -665,51 +564,11 @@ class ClankyResitelView(generic.ListView): | ||||||
| # 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>: | ||||||
| #	model = Problem | #	model = Problem | ||||||
| #	template_name = 'seminar/clanky/organizatorske_clanky.html' | #	template_name = 'tvorba/clanky/organizatorske_clanky.html' | ||||||
| #	queryset = Problem.objects.filter(stav=Problem.STAV_ZADANY).select_related('cislo_zadani__rocnik').order_by('-cislo_zadani__rocnik__rocnik', 'kod') | #	queryset = Problem.objects.filter(stav=Problem.STAV_ZADANY).select_related('cislo_zadani__rocnik').order_by('-cislo_zadani__rocnik__rocnik', 'kod') | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ### Status |  | ||||||
| 
 | 
 | ||||||
| def StavDatabazeView(request): |  | ||||||
| #	nastaveni = Nastaveni.objects.get() |  | ||||||
| 	problemy = utils.seznam_problemu() |  | ||||||
| 	muzi = Resitel.objects.filter(osoba__osloveni=m.Osoba.OSLOVENI_MUZSKE) |  | ||||||
| 	zeny = Resitel.objects.filter(osoba__osloveni=m.Osoba.OSLOVENI_ZENSKE) |  | ||||||
| 	return render(request, 'seminar/stav_databaze.html', |  | ||||||
| 			{ |  | ||||||
| #				'nastaveni': nastaveni, |  | ||||||
| 				'problemy': problemy, |  | ||||||
| 
 |  | ||||||
| 				'resitele': Resitel.objects.all(), |  | ||||||
| 				'muzi': muzi, |  | ||||||
| 				'zeny': zeny, |  | ||||||
| 				'jmena_muzu': utils.histogram([r.osoba.jmeno for r in muzi]), |  | ||||||
| 				'jmena_zen': utils.histogram([r.osoba.jmeno for r in zeny]), |  | ||||||
| 			}) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Interní, nemá se nikdy objevit v urls (jinak to účastníci vytrolí) |  | ||||||
| def formularOKView(request, text='', dalsi_odkazy: Sequence[tuple[str, str]] = ()): |  | ||||||
| 	template_name = 'seminar/formular_ok.html' |  | ||||||
| 	odkazy = list(dalsi_odkazy) + [ |  | ||||||
| 		# (Text, odkaz) |  | ||||||
| 		('Vrátit se na titulní stránku', reverse('titulni_strana')), |  | ||||||
| 		('Zobrazit aktuální zadání', reverse('seminar_aktualni_zadani')), |  | ||||||
| 	] |  | ||||||
| 	context = { |  | ||||||
| 		'odkazy': odkazy, |  | ||||||
| 				'text': text, |  | ||||||
| 	} |  | ||||||
| 	return render(request, template_name, context) |  | ||||||
| 
 |  | ||||||
| #------------------ Jak řešit - možná má být udělané úplně jinak |  | ||||||
| 
 |  | ||||||
| class JakResitView(generic.ListView): |  | ||||||
| 	template_name = 'seminar/jakresit/jak-resit.html' |  | ||||||
| 
 |  | ||||||
| 	def get_queryset(self): |  | ||||||
| 		return None |  | ||||||
| 
 | 
 | ||||||
| class AktualniRocnikRedirectView(RedirectView): | class AktualniRocnikRedirectView(RedirectView): | ||||||
| 	permanent=False | 	permanent=False | ||||||
|  | @ -1 +1,6 @@ | ||||||
|  | from solo.admin import SingletonModelAdmin | ||||||
| from django.contrib import admin | from django.contrib import admin | ||||||
|  | 
 | ||||||
|  | from .models import Nastaveni | ||||||
|  | 
 | ||||||
|  | admin.site.register(Nastaveni, SingletonModelAdmin) | ||||||
|  |  | ||||||
							
								
								
									
										0
									
								
								various/management/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										0
									
								
								various/management/commands/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -1,13 +1,11 @@ | ||||||
| import datetime |  | ||||||
| import os | import os | ||||||
| import random |  | ||||||
| 
 | 
 | ||||||
| from django.core.management.base import BaseCommand | from django.core.management.base import BaseCommand | ||||||
| from django.core.management import call_command | from django.core.management import call_command | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| 
 | 
 | ||||||
| from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni | from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni | ||||||
| from seminar.testutils import create_test_data | from various.testutils import create_test_data | ||||||
| import django.contrib.auth | import django.contrib.auth | ||||||
| User = django.contrib.auth.get_user_model() | User = django.contrib.auth.get_user_model() | ||||||
| 
 | 
 | ||||||
							
								
								
									
										16
									
								
								various/templates/various/jakresit/jak-resit.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,16 @@ | ||||||
|  | {% extends 'base.html' %} | ||||||
|  | 
 | ||||||
|  | {% load humanize %} | ||||||
|  | {% load static %} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | {% block content %} | ||||||
|  | 
 | ||||||
|  | <div class=jakresit> | ||||||
|  | 
 | ||||||
|  | {% include 'various/jakresit/jakresit_1.svg' %} | ||||||
|  | {% include 'various/jakresit/jakresit_2.svg' %} | ||||||
|  | {% include 'various/jakresit/jakresit_3.svg' %} | ||||||
|  | 
 | ||||||
|  | </div> | ||||||
|  | {% endblock %} | ||||||
| Before Width: | Height: | Size: 664 KiB After Width: | Height: | Size: 664 KiB | 
| Before Width: | Height: | Size: 689 KiB After Width: | Height: | Size: 689 KiB | 
| Before Width: | Height: | Size: 767 KiB After Width: | Height: | Size: 767 KiB | 
| Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB | 
|  | @ -79,7 +79,7 @@ function sousdeadline() { | ||||||
| <div class="TITULNI_STRANA_graf"> | <div class="TITULNI_STRANA_graf"> | ||||||
| 
 | 
 | ||||||
| <div class="TITULNI_STRANA_graf-svg"> | <div class="TITULNI_STRANA_graf-svg"> | ||||||
|   {% include 'seminar/titulnistrana/graph.svg' %} <!-- TODO: aby to nemuselo být v templates --> |   {% include 'various/titulnistrana/graph.svg' %} <!-- TODO: aby to nemuselo být v templates --> | ||||||
| </div> | </div> | ||||||
| 
 | 
 | ||||||
|   <span class="TITULNI_STRANA_zjistit_vic"> |   <span class="TITULNI_STRANA_zjistit_vic"> | ||||||
|  | @ -95,7 +95,7 @@ function sousdeadline() { | ||||||
| 
 | 
 | ||||||
|   {# Novinky #} |   {# Novinky #} | ||||||
|   <h1>Co je nového?</h1> |   <h1>Co je nového?</h1> | ||||||
|   {% include 'seminar/novinky.html' %} |   {% include 'novinky/novinky.html' %} | ||||||
| 
 | 
 | ||||||
|   <a href='/stare-novinky/'>Archiv novinek</a> |   <a href='/stare-novinky/'>Archiv novinek</a> | ||||||
| 
 | 
 | ||||||
							
								
								
									
										135
									
								
								various/testutils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,135 @@ | ||||||
|  | import datetime | ||||||
|  | import random | ||||||
|  | import logging | ||||||
|  | 
 | ||||||
|  | import django.contrib.auth | ||||||
|  | from django.contrib.flatpages.models import FlatPage | ||||||
|  | from django.contrib.sites.models import Site | ||||||
|  | from django.db import transaction | ||||||
|  | 
 | ||||||
|  | from seminar.models import Rocnik, Cislo, Nastaveni, Osoba, Organizator | ||||||
|  | 
 | ||||||
|  | from korektury.testutils import create_test_pdf | ||||||
|  | from novinky.testutils import gen_novinky | ||||||
|  | from personalni.testutils import gen_organizatori, gen_osoby, gen_prijemci, gen_resitele, gen_skoly | ||||||
|  | from soustredeni.testutils import gen_soustredeni, gen_konfery | ||||||
|  | from tvorba.testutils import gen_cisla, gen_clanek, gen_dlouhe_tema, gen_rocniky, gen_temata, gen_ulohy_do_cisla, gen_ulohy_k_tematum | ||||||
|  | 
 | ||||||
|  | logger = logging.getLogger(__name__) | ||||||
|  | 
 | ||||||
|  | User = django.contrib.auth.get_user_model() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @transaction.atomic | ||||||
|  | def create_test_data(size=6, rnd=None): | ||||||
|  | 	logger.info('Vyrábím testovací data (size={})...'.format(size)) | ||||||
|  | 
 | ||||||
|  | 	assert size >= 1 | ||||||
|  | 	# pevna pseudo-nahodnost | ||||||
|  | 	rnd = rnd or random.Random(x=42) | ||||||
|  | 
 | ||||||
|  | 	# static URL stranky | ||||||
|  | 	# FIXME: nakopirovat sem vsechny z produkcni databaze | ||||||
|  | 	s = Site.objects.filter(name="example.com") | ||||||
|  | 	f = FlatPage.objects.create( | ||||||
|  | 		url="/", title="Seminář M&M", | ||||||
|  | 		content="<p>Vítejte na stránce semináře MaM!</p>", | ||||||
|  | 	) | ||||||
|  | 	print(s) | ||||||
|  | 	f.sites.add(s[0]) | ||||||
|  | 	f.save() | ||||||
|  | 
 | ||||||
|  | 	# users | ||||||
|  | 	admin = User.objects.create_superuser( | ||||||
|  | 		username='admin', email='', password='admin', | ||||||
|  | 	) | ||||||
|  | 	os_admin = Osoba.objects.create( | ||||||
|  | 		user=admin, jmeno='admin', prijmeni='admin', | ||||||
|  | 		prezdivka='admin', osloveni='', email='admin@admin.admin', | ||||||
|  | 		telefon='123 456 789', datum_narozeni=datetime.date(2000, 1, 1), | ||||||
|  | 		ulice='admin', mesto='admin', psc='100 00', | ||||||
|  | 		datum_registrace=datetime.date(2020, 9, 6), | ||||||
|  | 	) | ||||||
|  | 	or_admin = Organizator.objects.create( | ||||||
|  | 		osoba=os_admin, organizuje_od=None, organizuje_do=None, | ||||||
|  | 		strucny_popis_organizatora="Organizátor k uživateli Admin", | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 	usernames = ['anet', 'bara', 'cyril', 'david', 'eva', 'filip'] | ||||||
|  | 	users = [] | ||||||
|  | 	for usr in usernames[:size]: | ||||||
|  | 		u = User.objects.create_user(username=usr, password=usr) | ||||||
|  | 		u.first_name = usr.capitalize() | ||||||
|  | 		u.save() | ||||||
|  | 		users.append(u) | ||||||
|  | 	print(users) | ||||||
|  | 
 | ||||||
|  | 	# skoly | ||||||
|  | 	skoly = gen_skoly() | ||||||
|  | 
 | ||||||
|  | 	# osoby | ||||||
|  | 	osoby = gen_osoby(rnd, size) | ||||||
|  | 
 | ||||||
|  | 	# resitele a organizatori | ||||||
|  | 	last_rocnik = 25 | ||||||
|  | 	organizatori = gen_organizatori(rnd, osoby, last_rocnik) | ||||||
|  | 	resitele = gen_resitele(rnd, osoby, skoly) | ||||||
|  | 
 | ||||||
|  | 	# generování novinek | ||||||
|  | 	novinky = gen_novinky(rnd, organizatori) | ||||||
|  | 
 | ||||||
|  | 	# prijemci | ||||||
|  | 	prijemci = gen_prijemci(rnd, osoby) | ||||||
|  | 
 | ||||||
|  | 	# rocniky | ||||||
|  | 	rocniky = gen_rocniky(last_rocnik, size) | ||||||
|  | 
 | ||||||
|  | 	# 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) | ||||||
|  | 
 | ||||||
|  | 	# generování obyčejných úloh do čísel | ||||||
|  | 	gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size) | ||||||
|  | 
 | ||||||
|  | 	# generování témat, zatím v prvních třech číslech po jednom | ||||||
|  | 	# 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 = Rocnik.objects.filter(rocnik=23).first() | ||||||
|  | 	dlouhe_tema = gen_dlouhe_tema( | ||||||
|  | 		rnd, organizatori, rocnik, "Strašně dlouhé téma", | ||||||
|  | 		"MFI", 8, | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 	# generování úloh k tématům ve všech číslech | ||||||
|  | 	gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori, resitele) | ||||||
|  | 
 | ||||||
|  | 	# generování soustředění | ||||||
|  | 	soustredeni = gen_soustredeni(size, resitele, organizatori, rnd=rnd) | ||||||
|  | 
 | ||||||
|  | 	# generování konfer | ||||||
|  | 	konfery = gen_konfery(size, organizatori, soustredeni, rnd=rnd) | ||||||
|  | 
 | ||||||
|  | 	# vytvoreni pdf ke korekturam | ||||||
|  | 	create_test_pdf(rnd, organizatori) | ||||||
|  | 
 | ||||||
|  | 	# TODO: nastavi správně, kolik se čeho generuje, aby rozsahy přibližně odpovídaly | ||||||
|  | 	# FIXME: misto typu ruzne typy objektu a vnoreni do sebe (Tom nechápe, co je tímto fixme míněno) | ||||||
|  | 	# TODO: vytvorit temata s ruznymi vlakny | ||||||
|  | 	# TODO: nagenerovat starsim rocnikum pohadku | ||||||
|  | 	# TODO: nagenerovat články | ||||||
|  | 	# TODO: vecpat obrázky všude, kde to jde | ||||||
|  | 	# 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, organizatori, resitele) | ||||||
|  | 
 | ||||||
|  | 	# TODO: přidat články včetně zařazení do struktury treenodů, | ||||||
|  | 	# a následně otestovat konsistency check databáze z utils.py | ||||||
|  | 	# pomocí stránky /stav | ||||||
|  | 
 | ||||||
|  | 	# obecné nastavení semináře, musí být už přidané ročníky a čísla, jinak se nastaví divně | ||||||
|  | 	nastaveni = Nastaveni.objects.create( | ||||||
|  | 		aktualni_cislo=Cislo.objects.all()[1]) | ||||||
							
								
								
									
										9
									
								
								various/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,9 @@ | ||||||
|  | from django.urls import path | ||||||
|  | from .views.final import TitulniStranaView, JakResitView, StavDatabazeView | ||||||
|  | from personalni.utils import org_required | ||||||
|  | 
 | ||||||
|  | urlpatterns = [ | ||||||
|  | 	path('', TitulniStranaView.as_view(), name='titulni_strana'), | ||||||
|  | 	path('jak-resit/', JakResitView.as_view(), name='jak_resit'), | ||||||
|  | 	path('stav', org_required(StavDatabazeView), name='stav_databaze'), | ||||||
|  | ] | ||||||