Merge branch 'new_design' of gimli.ms.mff.cuni.cz:/akce/mam/git/mamweb into new_design
This commit is contained in:
		
						commit
						2e4610c76f
					
				
					 48 changed files with 2190 additions and 1835 deletions
				
			
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							|  | @ -10,7 +10,7 @@ | |||
| 
 | ||||
| # aux files | ||||
| *.pyc | ||||
| *.swp | ||||
| *.sw[mnop] | ||||
| 
 | ||||
| # secrets | ||||
| /django.secret | ||||
|  |  | |||
|  | @ -13,11 +13,13 @@ Use git :-) | |||
| 
 | ||||
| Quickstart | ||||
| ---------- | ||||
| 
 | ||||
| Run the following commands: | ||||
| 	make install_venv | ||||
| 	. env/bin/activate | ||||
| 	make install_web | ||||
| 
 | ||||
| After finishing development, run "deactivate". | ||||
| 
 | ||||
| Make commands | ||||
| ------------- | ||||
| 
 | ||||
|  | @ -56,6 +58,8 @@ Make commands | |||
| 
 | ||||
| * `./manage.py test` - run the tests. | ||||
| 
 | ||||
| * `./manage.py shell` - run commands, list elemements of database, check syntax | ||||
|   by importing files, etc. | ||||
| 
 | ||||
| Configurations | ||||
| -------------- | ||||
|  |  | |||
|  | @ -5,7 +5,6 @@ from django.contrib import admin | |||
| from django.http import HttpResponseRedirect | ||||
| from django import forms | ||||
| from django.db import models | ||||
| from autocomplete_light import shortcuts as autocomplete_light | ||||
| 
 | ||||
| # akction | ||||
| 
 | ||||
|  | @ -39,11 +38,12 @@ class GalerieInline(admin.TabularInline): | |||
| 
 | ||||
| class ObrazekAdmin(admin.ModelAdmin): | ||||
| 	list_display = ('obrazek_velky', 'nazev', 'popis', 'obrazek_maly_tag') | ||||
| 	search_fields = ['nazev','popis'] | ||||
| 
 | ||||
| class GalerieAdmin(admin.ModelAdmin): | ||||
| 	form = autocomplete_light.modelform_factory(Galerie, autocomplete_fields=['titulni_obrazek'], fields=['titulni_obrazek']) | ||||
| 	model = Galerie | ||||
| 	fields = ('zobrazit', 'nazev', 'titulni_obrazek', 'popis', 'galerie_up', 'soustredeni', 'poradi') | ||||
| 	autocomplete_fields = ['titulni_obrazek'] | ||||
| 	list_display = ('nazev', 'soustredeni', 'galerie_up', 'poradi', 'zobrazit', 'datum_zmeny') | ||||
| 	inlines = [GalerieInline] | ||||
| 	actions = [zverejnit_fotogalerii, prepnout_fotogalerii_do_org_rezimu] | ||||
|  |  | |||
|  | @ -53,7 +53,7 @@ AUTHENTICATION_BACKENDS = ( | |||
| 
 | ||||
| 
 | ||||
| MIDDLEWARE = ( | ||||
|     'reversion.middleware.RevisionMiddleware', | ||||
| #    'reversion.middleware.RevisionMiddleware', | ||||
|     'django.contrib.sessions.middleware.SessionMiddleware', | ||||
|     'django.middleware.common.CommonMiddleware', | ||||
|     'django.middleware.csrf.CsrfViewMiddleware', | ||||
|  | @ -98,13 +98,14 @@ INSTALLED_APPS = ( | |||
| 
 | ||||
|     # Utilities | ||||
|     'sekizai', | ||||
|     'reversion', | ||||
| #    'reversion', | ||||
|     'django_countries', | ||||
|     'solo', | ||||
|     'ckeditor', | ||||
|     'ckeditor_uploader', | ||||
|     'taggit', | ||||
|     'autocomplete_light', | ||||
|     'dal', | ||||
|     'dal_select2', | ||||
| 
 | ||||
|     'fluent_comments', | ||||
|     'crispy_forms', | ||||
|  | @ -118,6 +119,8 @@ INSTALLED_APPS = ( | |||
| 
 | ||||
|     'imagekit', | ||||
| 
 | ||||
|     'polymorphic', | ||||
| 
 | ||||
|     # MaMweb | ||||
|     'mamweb', | ||||
|     'seminar', | ||||
|  | @ -215,6 +218,14 @@ LOGGING = { | |||
|                 'handlers': ['console'], | ||||
|                 'level': 'DEBUG', | ||||
|                 }, | ||||
|             'seminar.prihlaska.form':{ | ||||
| 		'handlers': ['console','registration_logfile'], | ||||
| 		'level': 'INFO' | ||||
| 		}, | ||||
|             'seminar.prihlaska.problem':{ | ||||
| 		'handlers': ['console','mail_registration','registration_error_log'], | ||||
| 		'level': 'INFO' | ||||
| 		}, | ||||
| 
 | ||||
|             # Catch-all logger | ||||
|             '': { | ||||
|  | @ -237,6 +248,24 @@ LOGGING = { | |||
|                 'class': 'django.utils.log.AdminEmailHandler', | ||||
|                 'formatter': 'verbose',  | ||||
|                 }, | ||||
|             'mail_registraion': { | ||||
|                 'level': 'WARN', | ||||
|                 'class': 'django.utils.log.AdminEmailHandler', | ||||
|                 'formatter': 'verbose',  | ||||
|                 }, | ||||
|             'registration_logfile':{ | ||||
|                 'level': 'INFO', | ||||
| 		'class': 'logging.FileHandler', | ||||
| 		# filename declared in specific configuration files | ||||
|                 'formatter': 'verbose',  | ||||
| 		}, | ||||
| 	    'registration_error_log':{ | ||||
|                 'level': 'INFO', | ||||
| 		'class': 'logging.FileHandler', | ||||
| 		# filename declared in specific configuration files | ||||
|                 'formatter': 'verbose',  | ||||
| 		}, | ||||
| 		 | ||||
|             }, | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										13
									
								
								mamweb/settings_debug.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								mamweb/settings_debug.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | |||
| # Debugovaci nastaveni settings.py | ||||
| # Pro vyber tohoto nastaveni muzete pouzit tez: | ||||
| # DJANGO_SETTINGS_MODULE=mamweb.settings_debug ./manage.py ... | ||||
| 
 | ||||
| # Import local settings | ||||
| from .settings_local import * | ||||
| 
 | ||||
| # Vypisovani databazovych dotazu do konzole | ||||
| LOGGING['loggers']['django.db.backends'] = { | ||||
| 	'level': 'DEBUG', | ||||
| 	'handlers': ['console'], | ||||
| 	'propagate': False, | ||||
| } | ||||
|  | @ -78,6 +78,11 @@ LOGGING = { | |||
|          #    'handlers': ['console'], | ||||
|          #    'propagate': False, | ||||
|          #}, | ||||
|         'werkzeug': { | ||||
|         	'handlers': ['console'], | ||||
|         	'level': 'DEBUG', | ||||
|         	'propagate': True, | ||||
|         }, | ||||
|         '': { | ||||
|             'handlers': ['console'], | ||||
|             'level': 'DEBUG', | ||||
|  | @ -88,3 +93,5 @@ LOGGING = { | |||
| 
 | ||||
| # set to 'DEBUG' for EXTRA verbose output | ||||
| # LOGGING['handlers']['console']['level'] = 'INFO' | ||||
| 
 | ||||
| EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' | ||||
|  |  | |||
|  | @ -61,6 +61,8 @@ CSRF_COOKIE_SECURE = True | |||
| 
 | ||||
| LOGGING['loggers']['']['handlers'] = ['console', 'mail_admins'] | ||||
| LOGGING['loggers']['django']['handlers'] = ['console', 'mail_admins'] | ||||
| LOGGING['handlers']['registration_logfile']['filename'] = '/home/mam-web/logs/prod/registration.log' | ||||
| LOGGING['handlers']['registration_error_log']['filename'] = '/home/mam-web/logs/prod/registration_errors.log' | ||||
| 
 | ||||
| 
 | ||||
| # E-MAIL NOTIFICATIONS | ||||
|  |  | |||
|  | @ -65,3 +65,5 @@ CSRF_COOKIE_SECURE = True | |||
| 
 | ||||
| LOGGING['loggers']['']['handlers'] = ['console', 'mail_admins'] | ||||
| LOGGING['loggers']['django']['handlers'] = ['console', 'mail_admins'] | ||||
| LOGGING['handlers']['registration_logfile']['filename'] = '/home/mam-web/logs/test/registration.log' | ||||
| LOGGING['handlers']['registration_error_log']['filename'] = '/home/mam-web/logs/test/registration_errors.log' | ||||
|  |  | |||
|  | @ -862,3 +862,27 @@ div.nahledy_cisel { | |||
| div.nahledy_cisel div, div.nahledy_cisel img { | ||||
|     position: absolute; | ||||
| } | ||||
| ul.form { | ||||
| 	list-style-type: none;	 | ||||
| 	padding-left: 0px; | ||||
| } | ||||
| label.field-label { | ||||
| 	font-weight: normal;	 | ||||
| } | ||||
| label.field-required { | ||||
| 	font-weight: bold;	 | ||||
| } | ||||
| .field-error { | ||||
| 	font-size: 14px; | ||||
| 	color: red; | ||||
| } | ||||
| ul.form li{ | ||||
| 	margin-bottom: 3px;	 | ||||
| } | ||||
| p.gdpr { | ||||
| 	font-size: 6pt; | ||||
| 	margin-bottom: .66em; | ||||
| } | ||||
| div.gdpr { | ||||
| 	font-size: 6pt; | ||||
| } | ||||
|  |  | |||
|  | @ -4,7 +4,6 @@ | |||
| {% block extrahead %} | ||||
| <link rel="shortcut icon" href="{% static 'favicon.ico' %}" type="image/x-icon"> | ||||
| <script src="{% static 'js/jquery-1.11.1.js' %}"></script> | ||||
| {% include 'autocomplete_light/static.html' %} | ||||
| {% endblock %} | ||||
| 
 | ||||
| {% block branding %} | ||||
|  |  | |||
|  | @ -27,8 +27,8 @@ | |||
|         } | ||||
|       }); | ||||
|     </script> | ||||
|     <script type="text/javascript" | ||||
|       src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"> | ||||
|     <script type="text/javascript" async | ||||
|       src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.6/MathJax.js?config=TeX-AMS-MML_HTMLorMML"> | ||||
|     </script> | ||||
| 
 | ||||
|     {# script specifický pro stránku #} | ||||
|  | @ -97,7 +97,27 @@ | |||
| 
 | ||||
| 
 | ||||
| 	<div class='row content'> | ||||
| 	<div class='col-md-12'> | ||||
| 	{% sitetree_menu from "main_menu" include "trunk" %} | ||||
| 
 | ||||
| 	{# | ||||
| 	{% for item in menu_top %} | ||||
|                 <li class="{% if item.selected %} active {% endif %}"> | ||||
|                 <a href="{{ item.url }}"> <i class="{{ item.icon_class }}"></i> {{ item.name }}</a> | ||||
|                 </li> | ||||
|                 {% if item.submenu %} | ||||
|                     <ul> | ||||
|                     {% for menu in item.submenu %} | ||||
|                         <li class="{% if menu.selected %} active {% endif %}"> | ||||
|                             <a href="{{ menu.url }}">{{ menu.name }}</a> | ||||
|                         </li> | ||||
|                     {% endfor %} | ||||
|                     </ul> | ||||
|                 {% endif %} | ||||
|             {% endfor %} | ||||
| 	   #} | ||||
| 
 | ||||
| 
 | ||||
| 	  <div class='col-md-12'> | ||||
| 	    {% block content %} | ||||
| 	    {% endblock content %} | ||||
| 	  </div> | ||||
|  |  | |||
|  | @ -11,7 +11,6 @@ urlpatterns = [ | |||
| 	# Admin a nastroje | ||||
| 	path('admin/', admin.site.urls),  # NOQA | ||||
| 	path('ckeditor/', include('ckeditor_uploader.urls')), | ||||
| 	path('autocomplete/', include('autocomplete_light.urls')), | ||||
| 
 | ||||
| 	# Seminarova aplikace (ma vlastni podadresare) | ||||
| 	path('', include('seminar.urls')), | ||||
|  |  | |||
|  | @ -22,11 +22,12 @@ django-solo | |||
| django-ckeditor | ||||
| django-flat-theme | ||||
| django-taggit | ||||
| django-autocomplete-light==2.3.6 | ||||
| django-autocomplete-light | ||||
| django-crispy-forms | ||||
| django-imagekit | ||||
| django-polymorphic | ||||
| django-sitetree | ||||
| django_reverse_admin | ||||
| 
 | ||||
| # Comments | ||||
| akismet==1.0.1 | ||||
|  |  | |||
							
								
								
									
										173
									
								
								seminar/admin.py
									
									
									
									
									
								
							
							
						
						
									
										173
									
								
								seminar/admin.py
									
									
									
									
									
								
							|  | @ -1,36 +1,171 @@ | |||
| from django.contrib import admin | ||||
| 
 | ||||
| from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter | ||||
| from reversion.admin import VersionAdmin | ||||
| from django_reverse_admin import ReverseModelAdmin | ||||
| 
 | ||||
| # Todo: reversion | ||||
| 
 | ||||
| import seminar.models as m | ||||
| 
 | ||||
| admin.site.register(m.Osoba) | ||||
| admin.site.register(m.Skola) | ||||
| admin.site.register(m.Prijemce) | ||||
| admin.site.register(m.Resitel) | ||||
| admin.site.register(m.Rocnik) | ||||
| admin.site.register(m.Cislo) | ||||
| admin.site.register(m.Organizator) | ||||
| admin.site.register(m.Soustredeni) | ||||
| admin.site.register(m.Problem) | ||||
| admin.site.register(m.Tema) | ||||
| admin.site.register(m.Clanek) | ||||
| 
 | ||||
| @admin.register(m.Osoba) | ||||
| class OsobaAdmin(admin.ModelAdmin): | ||||
| 	actions = ['synchronizuj_maily'] | ||||
| 
 | ||||
| 	def synchronizuj_maily(self, request, queryset): | ||||
| 		for o in queryset: | ||||
| 			if o.user is not None: | ||||
| 				u = o.user | ||||
| 				u.email = o.email | ||||
| 				u.save() | ||||
| 		self.message_user(request, "E-maily synchronizovány.") | ||||
| 	synchronizuj_maily.short_description = "Synchronizuj vybraným osobám e-maily do uživatelů" | ||||
| 
 | ||||
| @admin.register(m.Problem) | ||||
| class ProblemAdmin(PolymorphicParentModelAdmin): | ||||
| 	base_model = m.Problem | ||||
| 	child_models = [ | ||||
| 		m.Tema, | ||||
| 		m.Clanek, | ||||
| 		m.Uloha, | ||||
| 		] | ||||
| 
 | ||||
| @admin.register(m.Tema) | ||||
| class TemaAdmin(PolymorphicChildModelAdmin): | ||||
| 	base_model = m.Tema | ||||
| 	show_in_index = True | ||||
| 
 | ||||
| @admin.register(m.Clanek) | ||||
| class ClanekAdmin(PolymorphicChildModelAdmin): | ||||
| 	base_model = m.Clanek | ||||
| 	show_in_index = True | ||||
| 
 | ||||
| @admin.register(m.Uloha) | ||||
| class UlohaAdmin(PolymorphicChildModelAdmin): | ||||
| 	base_model = m.Uloha | ||||
| 	show_in_index = True | ||||
| 
 | ||||
| class TextAdminInline(admin.TabularInline): | ||||
| 	model = m.Text | ||||
| 	exclude = ['text_zkraceny_set','text_zkraceny'] | ||||
| admin.site.register(m.Text) | ||||
| admin.site.register(m.Uloha) | ||||
| admin.site.register(m.Reseni) | ||||
| admin.site.register(m.Hodnoceni) | ||||
| 
 | ||||
| class ResitelInline(admin.TabularInline): | ||||
| 	model = m.Resitel | ||||
| 	extra = 1 | ||||
| admin.site.register(m.Resitel) | ||||
| 
 | ||||
| class PrilohaReseniInline(admin.TabularInline): | ||||
| 	model = m.PrilohaReseni | ||||
| 	extra = 1 | ||||
| admin.site.register(m.PrilohaReseni) | ||||
| 
 | ||||
| class Reseni_ResiteleInline(admin.TabularInline): | ||||
| 	model = m.Reseni_Resitele | ||||
| 
 | ||||
| @admin.register(m.Reseni) | ||||
| class ReseniAdmin(ReverseModelAdmin): | ||||
| 	base_model = m.Reseni | ||||
| 	inline_type = 'tabular' | ||||
| 	inline_reverse = ['text_cely','resitele'] | ||||
| 	exclude = ['text_zkraceny', 'text_zkraceny_set'] | ||||
| 	inlines = [PrilohaReseniInline] | ||||
| # FAIL in template | ||||
| #	inlines = [PrilohaReseniInline,Reseni_ResiteleInline] | ||||
| 
 | ||||
| admin.site.register(m.Hodnoceni) | ||||
| admin.site.register(m.Pohadka) | ||||
| admin.site.register(m.Konfera) | ||||
| admin.site.register(m.Obrazek) | ||||
| admin.site.register(m.TreeNode) | ||||
| admin.site.register(m.RocnikNode) | ||||
| admin.site.register(m.CisloNode) | ||||
| admin.site.register(m.MezicisloNode) | ||||
| admin.site.register(m.TemaVCisleNode) | ||||
| admin.site.register(m.KonferaNode) | ||||
| admin.site.register(m.ClanekNode) | ||||
| admin.site.register(m.UlohaZadaniNode) | ||||
| admin.site.register(m.PohadkaNode) | ||||
| admin.site.register(m.UlohaVzorakNode) | ||||
| admin.site.register(m.TextNode) | ||||
| 
 | ||||
| 
 | ||||
| # Polymorfismus pro stromy | ||||
| # TODO: Inlines podle https://django-polymorphic.readthedocs.io/en/stable/admin.html | ||||
| 
 | ||||
| @admin.register(m.TreeNode) | ||||
| class TreeNodeAdmin(PolymorphicParentModelAdmin): | ||||
| 	base_model = m.TreeNode | ||||
| 	child_models = [ | ||||
| 		m.RocnikNode, | ||||
| 		m.CisloNode, | ||||
| 		m.MezicisloNode, | ||||
| 		m.TemaVCisleNode, | ||||
| 		m.KonferaNode, | ||||
| 		m.ClanekNode, | ||||
| 		m.UlohaZadaniNode, | ||||
| 		m.PohadkaNode, | ||||
| 		m.UlohaVzorakNode, | ||||
| 		m.TextNode, | ||||
| 		] | ||||
| 
 | ||||
| 	actions = ['aktualizuj_nazvy'] | ||||
| 
 | ||||
| 	# XXX: nejspíš je to totální DB HOG, nechcete to použít moc často. | ||||
| 	def aktualizuj_nazvy(self, request, queryset): | ||||
| 		newqs = queryset.get_real_instances() | ||||
| 		for tn in newqs: | ||||
| 			tn.aktualizuj_nazev() | ||||
| 			tn.save() | ||||
| 		self.message_user(request, "Názvy aktualizovány.") | ||||
| 	aktualizuj_nazvy.short_description = "Aktualizuj vybraným TreeNodům názvy" | ||||
| 
 | ||||
| @admin.register(m.RocnikNode) | ||||
| class RocnikNodeAdmin(PolymorphicChildModelAdmin): | ||||
| 	base_model = m.RocnikNode | ||||
| 	show_in_index = True | ||||
| 
 | ||||
| @admin.register(m.CisloNode) | ||||
| class CisloNodeAdmin(PolymorphicChildModelAdmin): | ||||
| 	base_model = m.CisloNode | ||||
| 	show_in_index = True | ||||
| 
 | ||||
| @admin.register(m.MezicisloNode) | ||||
| class MezicisloNodeAdmin(PolymorphicChildModelAdmin): | ||||
| 	base_model = m.MezicisloNode | ||||
| 	show_in_index = True | ||||
| 
 | ||||
| @admin.register(m.TemaVCisleNode) | ||||
| class TemaVCisleNodeAdmin(PolymorphicChildModelAdmin): | ||||
| 	base_model = m.TemaVCisleNode | ||||
| 	show_in_index = True | ||||
| 
 | ||||
| @admin.register(m.KonferaNode) | ||||
| class KonferaNodeAdmin(PolymorphicChildModelAdmin): | ||||
| 	base_model = m.KonferaNode | ||||
| 	show_in_index = True | ||||
| 
 | ||||
| @admin.register(m.ClanekNode) | ||||
| class ClanekNodeAdmin(PolymorphicChildModelAdmin): | ||||
| 	base_model = m.ClanekNode | ||||
| 	show_in_index = True | ||||
| 
 | ||||
| @admin.register(m.UlohaZadaniNode) | ||||
| class UlohaZadaniNodeAdmin(PolymorphicChildModelAdmin): | ||||
| 	base_model = m.UlohaZadaniNode | ||||
| 	show_in_index = True | ||||
| 
 | ||||
| @admin.register(m.PohadkaNode) | ||||
| class PohadkaNodeAdmin(PolymorphicChildModelAdmin): | ||||
| 	base_model = m.PohadkaNode | ||||
| 	show_in_index = True | ||||
| 
 | ||||
| @admin.register(m.UlohaVzorakNode) | ||||
| class UlohaVzorakNodeAdmin(PolymorphicChildModelAdmin): | ||||
| 	base_model = m.UlohaVzorakNode | ||||
| 	show_in_index = True | ||||
| 
 | ||||
| @admin.register(m.TextNode) | ||||
| class TextNodeAdmin(PolymorphicChildModelAdmin): | ||||
| 	base_model = m.TextNode | ||||
| 	show_in_index = True | ||||
| 
 | ||||
| 
 | ||||
| admin.site.register(m.Nastaveni) | ||||
| admin.site.register(m.Novinky) | ||||
|  |  | |||
							
								
								
									
										255
									
								
								seminar/forms.py
									
									
									
									
									
								
							
							
						
						
									
										255
									
								
								seminar/forms.py
									
									
									
									
									
								
							|  | @ -1,6 +1,257 @@ | |||
| from django import forms | ||||
| from dal import autocomplete | ||||
| from django.core.exceptions import ObjectDoesNotExist | ||||
| from django.contrib.auth.models import User | ||||
| 
 | ||||
| from .models import Skola, Resitel, Osoba, Problem | ||||
| import seminar.models as m | ||||
| 
 | ||||
| from datetime import date | ||||
| import logging | ||||
| 
 | ||||
| class LoginForm(forms.Form): | ||||
| 	username = forms.CharField(label='Přihlašovací jméno',  | ||||
| 			max_length=256,  | ||||
| 			required=True) | ||||
| 	password = forms.CharField( | ||||
| 			label='Heslo', | ||||
| 			max_length=256, | ||||
| 			required=True, | ||||
| 			widget=forms.PasswordInput()) | ||||
| 
 | ||||
| 
 | ||||
| class PrihlaskaForm(forms.Form): | ||||
| 	username = forms.CharField(label='Přihlašovací jméno',  | ||||
| 			max_length=256,  | ||||
| 			required=True, | ||||
| 			help_text='Tímto jménem se následně budeš přihlašovat pro odevzdání řešení a další činnosti v semináři') | ||||
| 	password = forms.CharField( | ||||
| 			label='Heslo', | ||||
| 			max_length=256, | ||||
| 			required=True, | ||||
| 			widget=forms.PasswordInput()) | ||||
| 	password_check = forms.CharField( | ||||
| 			label='Ověření hesla', | ||||
| 			max_length=256, | ||||
| 			required=True, | ||||
| 			widget=forms.PasswordInput()) | ||||
| 
 | ||||
| 	jmeno = forms.CharField(label='Jméno', max_length=256, required=True) | ||||
| 	prijmeni = forms.CharField(label='Příjmení', max_length=256, required=True) | ||||
| 	pohlavi_muz = forms.ChoiceField(label='Pohlaví', | ||||
| 			choices = ((True,'muž'),(False,'žena')), required=True) | ||||
| 	email = forms.EmailField(label='E-mail',max_length=256, required=True) | ||||
| 	telefon = forms.CharField(label='Telefon',max_length=256, required=False) | ||||
| 	datum_narozeni = forms.DateField(label='Datum narození', required=False) | ||||
| 	ulice = forms.CharField(label='Ulice', max_length=256, required=False) | ||||
| 	mesto = forms.CharField(label='Město', max_length=256, required=False) | ||||
| 	psc = forms.CharField(label='PSČ', max_length=32, required=False) | ||||
| 	stat = forms.ChoiceField(label='Stát',  | ||||
| 			choices = (('CZ', 'Česká Republika'), | ||||
| 				('SK', 'Slovenská Republika'), | ||||
| 				('other', 'Jiné')), | ||||
| 			required=False) | ||||
| 	stat_text = forms.CharField(label='Stát', max_length=256, required=False) | ||||
| 
 | ||||
| 	skola = forms.ModelChoiceField(label="Škola", | ||||
| 		queryset=Skola.objects.all(), | ||||
| 		widget=autocomplete.ModelSelect2( | ||||
| 			url='autocomplete_skola', | ||||
| 			attrs = {'data-placeholder--id': '-1', | ||||
| 				'data-placeholder--text' : '---', | ||||
| 				'data-allow-clear': 'true'}) | ||||
|     		,required=False) | ||||
| 	 | ||||
| 	skola_nazev = forms.CharField(label='Název školy', max_length=256, required=False) | ||||
| 	skola_adresa = forms.CharField(label='Adresa školy', max_length=256, required=False) | ||||
| 
 | ||||
| #	trida = forms.CharField(label='Třída',max_length=10, required=True) | ||||
| 
 | ||||
| 	rok_maturity = forms.IntegerField( | ||||
| 		label='Rok maturity',  | ||||
| 		min_value=date.today().year,  | ||||
| 		max_value=date.today().year+8, | ||||
| 		required=True) | ||||
| 	zasilat = forms.ChoiceField(label='Kam zasílat čísla a řešení',choices = Resitel.ZASILAT_CHOICES, required=True) | ||||
| 	gdpr = forms.BooleanField(label='Souhlasím se zpracováním osobních údajů', required=True) | ||||
| 	spam = forms.BooleanField(label='Souhlasím se zasíláním materiálů od MFF UK', required=False) | ||||
| 	 | ||||
| 	def clean_username(self): | ||||
| 		err_logger = logging.getLogger('seminar.prihlaska.problem') | ||||
| 		username = self.cleaned_data.get('username') | ||||
| 		try: | ||||
| 			User.objects.get(username=username) | ||||
| 			msg = "Username {} exists".format(username) | ||||
| 			err_logger.info(msg) | ||||
| 			raise forms.ValidationError('Přihlašovací jméno je již použito') | ||||
| 
 | ||||
| 		except ObjectDoesNotExist: | ||||
| 			pass | ||||
| 		return username | ||||
| 
 | ||||
| 	def clean_email(self): | ||||
| 		err_logger = logging.getLogger('seminar.prihlaska.problem') | ||||
| 		email = self.cleaned_data.get('email') | ||||
| 		try: | ||||
| 			Osoba.objects.get(email=email) | ||||
| 			msg = "Email {} exists".format(email) | ||||
| 			err_logger.info(msg) | ||||
| 			raise forms.ValidationError('Email je již použit') | ||||
| 
 | ||||
| 		except ObjectDoesNotExist: | ||||
| 			pass | ||||
| 		return email | ||||
| 
 | ||||
| 
 | ||||
| 	def clean(self): | ||||
| 		super().clean() | ||||
| 		 | ||||
| 		err_logger = logging.getLogger('seminar.prihlaska.problem') | ||||
| 
 | ||||
| 		data = self.cleaned_data | ||||
| 		if data.get('password') != data.get('password_check'): | ||||
| 			self.add_error('password_check',forms.ValidationError('Hesla se neshodují')) | ||||
| 		if data.get('stat') != '' and data.get('stat_text') != '': | ||||
| 			self.add_error('stat',forms.ValidationError('Nelze mít vybraný stát z menu a zároven zapsaný textem')) | ||||
| 		if data.get('skola') and (data.get('skola_nazev') or data.get('skola_adresa')): | ||||
| 			self.add_error('skola',forms.ValidationError('Pokud je škola v seznamu, nevypisujte ji ručně, pokud není, zrušte výběr ze seznamu (křížek vpravo)')) | ||||
| 		if not data.get('skola'): | ||||
| 			if data.get('skola_nazev')=='' and data.get('skola_adresa')=='': | ||||
| 				self.add_error('skola',forms.ValidationError('Je nutné vyplnit školu')) | ||||
| 			elif data.get('skola_nazev')=='': | ||||
| 				self.add_error('skola_nazev',forms.ValidationError('Je nutné vyplnit název školy')) | ||||
| 			elif data.get('skola_adresa')=='': | ||||
| 				self.add_error('skola_adresa',forms.ValidationError('Je nutné vyplnit adresu školy')) | ||||
| 
 | ||||
| 
 | ||||
| class ProfileEditForm(forms.Form): | ||||
| 	username = forms.CharField(label='Přihlašovací jméno',  | ||||
| 			max_length=256,  | ||||
| 			required=True) | ||||
| 
 | ||||
| 	jmeno = forms.CharField(label='Jméno', max_length=256, required=True) | ||||
| 	prijmeni = forms.CharField(label='Příjmení', max_length=256, required=True) | ||||
| 	pohlavi_muz = forms.ChoiceField(label='Pohlaví', | ||||
| 			choices = ((True,'muž'),(False,'žena')), required=True) | ||||
| 	email = forms.EmailField(label='E-mail',max_length=256, required=True) | ||||
| 	telefon = forms.CharField(label='Telefon',max_length=256, required=False) | ||||
| 	datum_narozeni = forms.DateField(label='Datum narození', required=False) | ||||
| 	ulice = forms.CharField(label='Ulice', max_length=256, required=False) | ||||
| 	mesto = forms.CharField(label='Město', max_length=256, required=False) | ||||
| 	psc = forms.CharField(label='PSČ', max_length=32, required=False) | ||||
| 	stat = forms.ChoiceField(label='Stát',  | ||||
| 			choices = (('CZ', 'Česká Republika'), | ||||
| 				('SK', 'Slovenská Republika'), | ||||
| 				('other', 'Jiné')), | ||||
| 			required=False) | ||||
| 	stat_text = forms.CharField(label='Stát', max_length=256, required=False) | ||||
| 
 | ||||
| 	skola = forms.ModelChoiceField(label="Škola", | ||||
| 		queryset=Skola.objects.all(), | ||||
| 		widget=autocomplete.ModelSelect2( | ||||
| 			url='autocomplete_skola', | ||||
| 			attrs = {'data-placeholder--id': '-1', | ||||
| 				'data-placeholder--text' : '---', | ||||
| 				'data-allow-clear': 'true'}) | ||||
|     		,required=False) | ||||
| 	 | ||||
| 	skola_nazev = forms.CharField(label='Název školy', max_length=256, required=False) | ||||
| 	skola_adresa = forms.CharField(label='Adresa školy', max_length=256, required=False) | ||||
| 
 | ||||
| #	trida = forms.CharField(label='Třída',max_length=10, required=True) | ||||
| 
 | ||||
| 	rok_maturity = forms.IntegerField( | ||||
| 		label='Rok maturity',  | ||||
| 		min_value=date.today().year,  | ||||
| 		max_value=date.today().year+8, | ||||
| 		required=True) | ||||
| 	zasilat = forms.ChoiceField(label='Kam zasílat čísla a řešení',choices = Resitel.ZASILAT_CHOICES, required=True) | ||||
| 	spam = forms.BooleanField(label='Souhlasím se zasíláním materiálů od MFF UK', required=False) | ||||
| #	def clean_username(self): | ||||
| #		err_logger = logging.getLogger('seminar.prihlaska.problem') | ||||
| #		username = self.cleaned_data.get('username') | ||||
| #		try: | ||||
| #			User.objects.get(username=username) | ||||
| #			msg = "Username {} exists".format(username) | ||||
| #			err_logger.info(msg) | ||||
| #			raise forms.ValidationError('Přihlašovací jméno je již použito') | ||||
| # | ||||
| #		except ObjectDoesNotExist: | ||||
| #			pass | ||||
| #		return username | ||||
| # | ||||
| #	def clean_email(self): | ||||
| #		err_logger = logging.getLogger('seminar.prihlaska.problem') | ||||
| #		email = self.cleaned_data.get('email') | ||||
| #		try: | ||||
| #			Osoba.objects.get(email=email) | ||||
| #			msg = "Email {} exists".format(email) | ||||
| #			err_logger.info(msg) | ||||
| #			raise forms.ValidationError('Email je již použit') | ||||
| # | ||||
| #		except ObjectDoesNotExist: | ||||
| #			pass | ||||
| #		return email | ||||
| 	#def clean(self): | ||||
| 	#	super().clean() | ||||
| 	#	 | ||||
| 	#	err_logger = logging.getLogger('seminar.prihlaska.problem') | ||||
| 
 | ||||
| 	#	data = self.cleaned_data | ||||
| 	#	if data.get('password') != data.get('password_check'): | ||||
| 	#		self.add_error('password_check',forms.ValidationError('Hesla se neshodují')) | ||||
| 	#	if data.get('stat') != '' and data.get('stat_text') != '': | ||||
| 	#		self.add_error('stat',forms.ValidationError('Nelze mít vybraný stát z menu a zároven zapsaný textem')) | ||||
| 	#	if data.get('skola') and (data.get('skola_nazev') or data.get('skola_adresa')): | ||||
| 	#		self.add_error('skola',forms.ValidationError('Pokud je škola v seznamu, nevypisujte ji ručně, pokud není, zrušte výběr ze seznamu (křížek vpravo)')) | ||||
| 	#	if not data.get('skola'): | ||||
| 	#		if data.get('skola_nazev')=='' and data.get('skola_adresa')=='': | ||||
| 	#			self.add_error('skola',forms.ValidationError('Je nutné vyplnit školu')) | ||||
| 	#		elif data.get('skola_nazev')=='': | ||||
| 	#			self.add_error('skola_nazev',forms.ValidationError('Je nutné vyplnit název školy')) | ||||
| 	#		elif data.get('skola_adresa')=='': | ||||
| 	#			self.add_error('skola_adresa',forms.ValidationError('Je nutné vyplnit adresu školy')) | ||||
| 
 | ||||
| class VlozReseniForm(forms.Form): | ||||
| 	#FIXME jen podproblémy daného problému | ||||
| 	problem = forms.ModelChoiceField(label='Problém',queryset=m.Problem.objects.all()) | ||||
| 	# to_field_name | ||||
| 	#problem = models.ManyToManyField(Problem, verbose_name='problém', help_text='Problém', | ||||
| 	#	through='Hodnoceni') | ||||
| 
 | ||||
| 	# FIXME pridat vice resitelu | ||||
| 	resitel = forms.ModelChoiceField(label="Řešitel", | ||||
| 		queryset=Resitel.objects.all(), | ||||
| 		widget=autocomplete.ModelSelect2( | ||||
| 			url='autocomplete_resitel', | ||||
| 			attrs = {'data-placeholder--id': '-1', | ||||
| 				'data-placeholder--text' : '---', | ||||
| 				'data-allow-clear': 'true'}) | ||||
|     		) | ||||
| 
 | ||||
| 
 | ||||
| 	#resitele = models.ManyToManyField(Resitel, verbose_name='autoři řešení', | ||||
| 	#	help_text='Seznam autorů řešení', through='Reseni_Resitele') | ||||
| 	 | ||||
| 	cas_doruceni = forms.DateField(label="Čas doručení") | ||||
| 
 | ||||
| 	#cas_doruceni = models.DateTimeField('čas_doručení', default=timezone.now, blank=True) | ||||
| 
 | ||||
| 	forma = forms.ChoiceField(label="Forma řešení",choices = m.Reseni.FORMA_CHOICES) | ||||
| 	#forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False, | ||||
| 	#	 default=FORMA_EMAIL) | ||||
| 
 | ||||
| 	poznamka = forms.CharField(label='Neveřejná poznámka')	 | ||||
| 	#poznamka = models.TextField('neveřejná poznámka', blank=True, | ||||
| 	#	help_text='Neveřejná poznámka k řešení (plain text)') | ||||
| 
 | ||||
| 	#TODO body do cisla | ||||
| 	#TODO prilohy | ||||
| 
 | ||||
| 	def __init__(self, *args, **kwargs): | ||||
| 		super().__init__(*args, **kwargs) | ||||
| 		#self.fields['favorite_color'] = forms.ChoiceField(choices=[(color.id, color.name) for color in Resitel.objects.all()]) | ||||
| 
 | ||||
| 
 | ||||
| class NameForm(forms.Form): | ||||
| 	your_name = forms.CharField(label='Your name', max_length=100) | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										20
									
								
								seminar/management/commands/nukedb.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								seminar/management/commands/nukedb.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | |||
| from mamweb.settings import INSTALLED_APPS | ||||
| from django.core.management.base import BaseCommand, CommandError | ||||
| from django.core.management import call_command | ||||
| 
 | ||||
| class Command(BaseCommand): | ||||
| 	help = "Odmigruje všechny moduly (i.e. smaže všechny tabulky, ale databázi nechá)" | ||||
| 
 | ||||
| 	def add_arguments(self, parser): | ||||
| 		# TODO: --force (makat a neblábolit) | ||||
| 		pass | ||||
| 	def handle(self, *args, **options): | ||||
| 		# TODO: zeptat se | ||||
| 		for app in INSTALLED_APPS: | ||||
| 			app = app.split('.')[-1] | ||||
| 			try: | ||||
| 				call_command('migrate', app, 'zero') | ||||
| 			except CommandError: | ||||
| 				# app nemá migrace (aspoň typicky) | ||||
| 				pass | ||||
| 		call_command('showmigrations') | ||||
|  | @ -15,21 +15,34 @@ User = django.contrib.auth.get_user_model() | |||
| 
 | ||||
| 
 | ||||
| class Command(BaseCommand): | ||||
|     help = "Clear database and load testing data." | ||||
| 	help = "Clear database and load testing data." | ||||
| 
 | ||||
|     def handle(self, *args, **options): | ||||
|         assert settings.DEBUG == True | ||||
|         dbfile = settings.DATABASES['default']['NAME'] | ||||
|         if os.path.exists(dbfile): | ||||
|             os.rename(dbfile, dbfile + '.old') | ||||
|             self.stderr.write('Stara databaze prejmenovana na "%s"' % (dbfile + '.old')) | ||||
|         call_command('migrate', no_input=True) | ||||
|         self.stdout.write('Vytvarim uzivatele "admin" (heslo "admin") a pseudo-nahodna data ...') | ||||
|         create_test_data(size=8) | ||||
|         self.stdout.write('Vytvoreno {} uzivatelu, {} skol, {} resitelu, {} rocniku, {} cisel,'  | ||||
|                ' {} problemu, {} reseni.'.format(User.objects.count(), Skola.objects.count(),  | ||||
|                     Resitel.objects.count(), Rocnik.objects.count(), Cislo.objects.count(),  | ||||
|                     Problem.objects.count(), Reseni.objects.count())) | ||||
| 	def add_arguments(self, parser): | ||||
| 		parser.add_argument( | ||||
| 			'--no-clean', | ||||
| 			action='store_true', | ||||
| 			help='Změny se provedou v aktuální DB, ne v čisté. Aktuální DB se nezachová. (jen k debugování)', | ||||
| 			) | ||||
| 		parser.add_argument( | ||||
| 			'--no-migrate', | ||||
| 			action='store_true', | ||||
| 			help='Neprovádět migrace před generováním testovacích dat (jen k debugování)', | ||||
| 		) | ||||
| 
 | ||||
| 	def handle(self, *args, **options): | ||||
| 		assert settings.DEBUG == True | ||||
| 		dbfile = settings.DATABASES['default']['NAME'] | ||||
| 		if os.path.exists(dbfile) and not options['no_clean']: | ||||
| 			os.rename(dbfile, dbfile + '.old') | ||||
| 			self.stderr.write('Stara databaze prejmenovana na "%s"' % (dbfile + '.old')) | ||||
| 		if not options['no_migrate']: | ||||
| 			call_command('migrate', no_input=True) | ||||
| 		self.stdout.write('Vytvarim uzivatele "admin" (heslo "admin") a pseudo-nahodna data ...') | ||||
| 		create_test_data(size=8) | ||||
| 		self.stdout.write('Vytvoreno {} uzivatelu, {} skol, {} resitelu, {} rocniku, {} cisel,'  | ||||
| 			   ' {} problemu, {} reseni.'.format(User.objects.count(), Skola.objects.count(),  | ||||
| 					Resitel.objects.count(), Rocnik.objects.count(), Cislo.objects.count(),  | ||||
| 					Problem.objects.count(), Reseni.objects.count())) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										31
									
								
								seminar/migrations/0065_treenode_polymorphic_ctype.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								seminar/migrations/0065_treenode_polymorphic_ctype.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | |||
| # Generated by Django 2.2.4 on 2019-08-13 19:36 | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| import django.db.models.deletion | ||||
| 
 | ||||
| def vyrob_treenodum_ctypes(apps, schema_editor): | ||||
| 	# Kód zkopírovaný z dokumentace: https://django-polymorphic.readthedocs.io/en/stable/migrating.html | ||||
| 	# XXX: Nevím, jestli se tohle náhodou nemělo spustit na všech childech (jen/i) | ||||
| 	TreeNode = apps.get_model('seminar', 'TreeNode') | ||||
| 	ContentType = apps.get_model('contenttypes', 'ContentType') | ||||
| 	 | ||||
| 	new_ct = ContentType.objects.get_for_model(TreeNode) | ||||
| 	TreeNode.objects.filter(polymorphic_ctype__isnull=True).update(polymorphic_ctype=new_ct) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 	 | ||||
| 	dependencies = [ | ||||
| 		('contenttypes', '0002_remove_content_type_name'), | ||||
| 		('seminar', '0064_auto_20190610_2358'), | ||||
| 	] | ||||
| 	 | ||||
| 	operations = [ | ||||
| 		migrations.AddField( | ||||
| 			model_name='treenode', | ||||
| 			name='polymorphic_ctype', | ||||
| 			field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_seminar.treenode_set+', to='contenttypes.ContentType'), | ||||
| 		), | ||||
| 		migrations.RunPython(vyrob_treenodum_ctypes, migrations.RunPython.noop), | ||||
| 	] | ||||
							
								
								
									
										29
									
								
								seminar/migrations/0066_problem_polymorphic_ctype.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								seminar/migrations/0066_problem_polymorphic_ctype.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | |||
| # Generated by Django 2.2.4 on 2019-08-13 19:45 | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| import django.db.models.deletion | ||||
| 
 | ||||
| def vyrob_problemum_ctypes(apps, schema_editor): | ||||
| 	# Kód zkopírovaný z dokumentace: https://django-polymorphic.readthedocs.io/en/stable/migrating.html | ||||
| 	# XXX: Nevím, jestli se tohle náhodou nemělo spustit na všech childech (jen/i) | ||||
| 	Problem = apps.get_model('seminar', 'Problem') | ||||
| 	ContentType = apps.get_model('contenttypes', 'ContentType') | ||||
| 	 | ||||
| 	new_ct = ContentType.objects.get_for_model(Problem) | ||||
| 	Problem.objects.filter(polymorphic_ctype__isnull=True).update(polymorphic_ctype=new_ct) | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 	 | ||||
| 	dependencies = [ | ||||
| 		('contenttypes', '0002_remove_content_type_name'), | ||||
| 		('seminar', '0065_treenode_polymorphic_ctype'), | ||||
| 	] | ||||
| 	 | ||||
| 	operations = [ | ||||
| 		migrations.AddField( | ||||
| 			model_name='problem', | ||||
| 			name='polymorphic_ctype', | ||||
| 			field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_seminar.problem_set+', to='contenttypes.ContentType'), | ||||
| 		), | ||||
| 		migrations.RunPython(vyrob_problemum_ctypes, migrations.RunPython.noop), | ||||
| 	] | ||||
							
								
								
									
										18
									
								
								seminar/migrations/0067_auto_20190814_0805.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								seminar/migrations/0067_auto_20190814_0805.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| # Generated by Django 2.2.4 on 2019-08-14 06:05 | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('seminar', '0066_problem_polymorphic_ctype'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.AlterField( | ||||
|             model_name='konfera', | ||||
|             name='nazev', | ||||
|             field=models.CharField(help_text='Název konfery', max_length=100, verbose_name='název konfery'), | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										107
									
								
								seminar/migrations/0068_treenode_nazev.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								seminar/migrations/0068_treenode_nazev.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,107 @@ | |||
| # Generated by Django 2.2.5 on 2019-09-26 19:35 | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| 
 | ||||
| # Migrace nejspíš neumí volat metody modelů: | ||||
| # https://stackoverflow.com/questions/28777338/django-migrations-runpython-not-able-to-call-model-methods#37685925 | ||||
| 
 | ||||
| def fix_RocnikNode_names(apps,schema_editor): | ||||
| 	Objects = apps.get_model('seminar', 'RocnikNode') | ||||
| 	for obj in Objects.objects.all(): | ||||
| 		obj.nazev = str(obj.rocnik)+" (RocnikNode)" | ||||
| 		obj.save() | ||||
| 
 | ||||
| def fix_CisloNode_names(apps,schema_editor): | ||||
| 	Objects = apps.get_model('seminar', 'CisloNode') | ||||
| 	for obj in Objects.objects.all(): | ||||
| 		obj.nazev = str(obj.cislo)+" (CisloNode)" | ||||
| 		obj.save() | ||||
| 
 | ||||
| def fix_MezicisloNode_names(apps,schema_editor): | ||||
| 	Objects = apps.get_model('seminar', 'MezicisloNode') | ||||
| 	for obj in Objects.objects.all(): | ||||
| 		if obj.prev: | ||||
| 			if (obj.prev.get_real_instance_class() != CisloNode and | ||||
| 				obj.prev.get_real_instance_class() != MezicisloNode): | ||||
| 					raise ValueError("Předchůdce není číslo!") | ||||
| 			posledni = obj.prev.cislo | ||||
| 			obj.nazev = "Mezičíslo po čísle"+str(posledni)+" (MezicisloNode)" | ||||
| 		elif obj.root: | ||||
| 			if obj.root.get_real_instance_class() != RocnikNode: | ||||
| 				raise ValueError("Kořen stromu není ročník!") | ||||
| 			rocnik = obj.root.rocnik | ||||
| 			obj.nazev = "První mezičíslo ročníku "+" (MezicisloNode)" | ||||
| 		else: | ||||
| 			print("!!!!! Nějaké neidentifikované mezičíslo !!!!!") | ||||
| 			obj.nazev = "Neidentifikovatelné mezičíslo! (MezicisloNode)" | ||||
| 		obj.save() | ||||
| 
 | ||||
| def fix_TemaVCisleNode_names(apps,schema_editor): | ||||
| 	Objects = apps.get_model('seminar', 'TemaVCisleNode') | ||||
| 	for obj in Objects.objects.all(): | ||||
| 		obj.nazev = str(obj.tema)+" (TemaVCisleNode)" | ||||
| 		obj.save() | ||||
| 
 | ||||
| def fix_KonferaNode_names(apps,schema_editor): | ||||
| 	Objects = apps.get_model('seminar', 'KonferaNode') | ||||
| 	for obj in Objects.objects.all(): | ||||
| 		obj.nazev = str(obj.konfera)+" (KonferaNode)" | ||||
| 		obj.save() | ||||
| 
 | ||||
| def fix_ClanekNode_names(apps,schema_editor): | ||||
| 	Objects = apps.get_model('seminar', 'ClanekNode') | ||||
| 	for obj in Objects.objects.all(): | ||||
| 		obj.nazev = str(obj.clanek)+" (ClanekNode)" | ||||
| 		obj.save() | ||||
| 
 | ||||
| def fix_UlohaZadaniNode_names(apps,schema_editor): | ||||
| 	Objects = apps.get_model('seminar', 'UlohaZadaniNode') | ||||
| 	for obj in Objects.objects.all(): | ||||
| 		obj.nazev = str(obj.uloha)+" (UlohaZadaniNode)" | ||||
| 		obj.save() | ||||
| 
 | ||||
| def fix_PohadkaNode_names(apps,schema_editor): | ||||
| 	Objects = apps.get_model('seminar', 'PohadkaNode') | ||||
| 	for obj in Objects.objects.all(): | ||||
| 		obj.nazev = str(obj.pohadka)+" (PohadkaNode)" | ||||
| 		obj.save() | ||||
| 
 | ||||
| def fix_UlohaVzorakNode_names(apps,schema_editor): | ||||
| 	Objects = apps.get_model('seminar', 'UlohaVzorakNode') | ||||
| 	for obj in Objects.objects.all(): | ||||
| 		obj.nazev = str(obj.uloha)+" (UlohaVzorakNode)" | ||||
| 		obj.save() | ||||
| 
 | ||||
| def fix_TextNode_names(apps,schema_editor): | ||||
| 	Objects = apps.get_model('seminar', 'TextNode') | ||||
| 	for obj in Objects.objects.all(): | ||||
| 		obj.nazev = str(obj.text)+" (TextNode)" | ||||
| 		obj.save() | ||||
| 
 | ||||
| def fix_all_names(apps,schema_editor): | ||||
| 	fix_RocnikNode_names(apps,schema_editor) | ||||
| 	fix_CisloNode_names(apps,schema_editor) | ||||
| 	fix_MezicisloNode_names(apps,schema_editor) | ||||
| 	fix_TemaVCisleNode_names(apps,schema_editor) | ||||
| 	fix_KonferaNode_names(apps,schema_editor) | ||||
| 	fix_ClanekNode_names(apps,schema_editor) | ||||
| 	fix_UlohaZadaniNode_names(apps,schema_editor) | ||||
| 	fix_PohadkaNode_names(apps,schema_editor) | ||||
| 	fix_UlohaVzorakNode_names(apps,schema_editor) | ||||
| 	fix_TextNode_names(apps,schema_editor) | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('seminar', '0067_auto_20190814_0805'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.AddField( | ||||
|             model_name='treenode', | ||||
|             name='nazev', | ||||
|             field=models.TextField(help_text='Tento název se zobrazuje v nabídkách pro výběr vhodného TreeNode', null=True, verbose_name='název tohoto node'), | ||||
|         ), | ||||
|         migrations.RunPython(fix_all_names), | ||||
|     ] | ||||
							
								
								
									
										28
									
								
								seminar/migrations/0069_auto_20191120_2115.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								seminar/migrations/0069_auto_20191120_2115.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | |||
| # Generated by Django 2.2.7 on 2019-11-20 20:15 | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| import django.db.models.deletion | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('seminar', '0068_treenode_nazev'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.AlterModelOptions( | ||||
|             name='cislo', | ||||
|             options={'ordering': ['-rocnik__rocnik', '-poradi'], 'verbose_name': 'Číslo', 'verbose_name_plural': 'Čísla'}, | ||||
|         ), | ||||
|         migrations.RenameField( | ||||
|             model_name='cislo', | ||||
|             old_name='cislo', | ||||
|             new_name='poradi', | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='problem', | ||||
|             name='nadproblem', | ||||
|             field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='podproblem', to='seminar.Problem', verbose_name='nadřazený problém'), | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										23
									
								
								seminar/migrations/0070_auto_20191120_2357.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								seminar/migrations/0070_auto_20191120_2357.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | |||
| # Generated by Django 2.2.7 on 2019-11-20 22:57 | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('seminar', '0069_auto_20191120_2115'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.AddField( | ||||
|             model_name='tema', | ||||
|             name='abstrakt', | ||||
|             field=models.TextField(blank=True, verbose_name='Abstrakt na rozcestník'), | ||||
|         ), | ||||
|         migrations.AddField( | ||||
|             model_name='tema', | ||||
|             name='obrazek', | ||||
|             field=models.ImageField(null=True, upload_to='', verbose_name='Obrázek na rozcestník'), | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										17
									
								
								seminar/migrations/0071_remove_nastaveni_aktualni_rocnik.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								seminar/migrations/0071_remove_nastaveni_aktualni_rocnik.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | |||
| # Generated by Django 2.2.7 on 2019-11-21 17:38 | ||||
| 
 | ||||
| from django.db import migrations | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('seminar', '0070_auto_20191120_2357'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.RemoveField( | ||||
|             model_name='nastaveni', | ||||
|             name='aktualni_rocnik', | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										23
									
								
								seminar/migrations/0072_auto_20191204_2257.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								seminar/migrations/0072_auto_20191204_2257.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | |||
| # Generated by Django 2.2.7 on 2019-12-04 21:57 | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('seminar', '0071_remove_nastaveni_aktualni_rocnik'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.AddField( | ||||
|             model_name='treenode', | ||||
|             name='srolovatelne', | ||||
|             field=models.BooleanField(blank=True, help_text='Bude na stránce témátka možnost tuto položku skrýt', null=True, verbose_name='Srolovatelné'), | ||||
|         ), | ||||
|         migrations.AddField( | ||||
|             model_name='treenode', | ||||
|             name='zajimave', | ||||
|             field=models.BooleanField(default=False, help_text='Zobrazí se daná věc na rozcestníku témátek', verbose_name='Zajímavé'), | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										22
									
								
								seminar/migrations/0073_copy_osoba_email_to_user_email.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								seminar/migrations/0073_copy_osoba_email_to_user_email.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| # Generated by Django 2.2.9 on 2020-01-15 21:28 | ||||
| 
 | ||||
| from django.db import migrations | ||||
| 
 | ||||
| def copy_mails(apps, schema_editor): | ||||
| 	Osoba = apps.get_model('seminar', 'Osoba') | ||||
| 
 | ||||
| 	for o in Osoba.objects.all(): | ||||
| 		if o.user is not None: | ||||
| 			u = o.user | ||||
| 			u.email = o.email | ||||
| 			u.save() | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('seminar', '0072_auto_20191204_2257'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.RunPython(copy_mails, migrations.RunPython.noop) | ||||
|     ] | ||||
|  | @ -21,10 +21,11 @@ from taggit.managers import TaggableManager | |||
| 
 | ||||
| from reversion import revisions as reversion | ||||
| 
 | ||||
| from seminar.utils import roman | ||||
| from seminar.utils import roman, FirstTagParser # Pro získání úryvku z TextNode | ||||
| 
 | ||||
| from unidecode import unidecode | ||||
| from unidecode import unidecode # Používám pro získání ID odkazu (ještě je to někde po někom zakomentované) | ||||
| 
 | ||||
| from polymorphic.models import PolymorphicModel | ||||
| 
 | ||||
| class SeminarModelBase(models.Model): | ||||
| 
 | ||||
|  | @ -129,6 +130,17 @@ class Osoba(SeminarModelBase): | |||
| 	def __str__(self): | ||||
| 		return self.plne_jmeno() | ||||
| 
 | ||||
| 	# Overridujeme save Osoby, aby když si změní e-mail, aby se projevil i v | ||||
| 	# Userovi (a tak se dal poslat mail s resetem hesla) | ||||
| 	def save(self, *args, **kwargs): | ||||
| 		if self.user is not None: | ||||
| 			u = self.user | ||||
| 			# U svatého tučňáka, prosím ať tohle funguje. | ||||
| 			# (Takhle se kódit asi nemá...) | ||||
| 			u.email = self.email | ||||
| 			u.save() | ||||
| 		super().save() | ||||
| 
 | ||||
| # | ||||
| # Mělo by být částečně vytaženo z Aesopa | ||||
| # viz https://ovvp.mff.cuni.cz/wiki/aesop/export-skol. | ||||
|  | @ -352,7 +364,7 @@ class Rocnik(SeminarModelBase): | |||
| 	 | ||||
| 	def verejna_cisla(self): | ||||
| 		vc = [c for c in self.cisla.all() if c.verejne()] | ||||
| 		vc.sort(key=lambda c: c.cislo) | ||||
| 		vc.sort(key=lambda c: c.poradi) | ||||
| 		return vc | ||||
| 
 | ||||
| 	def posledni_verejne_cislo(self): | ||||
|  | @ -361,7 +373,7 @@ class Rocnik(SeminarModelBase): | |||
| 
 | ||||
| 	def verejne_vysledkovky_cisla(self): | ||||
| 		vc = list(self.cisla.filter(verejna_vysledkovka=True)) | ||||
| 		vc.sort(key=lambda c: c.cislo) | ||||
| 		vc.sort(key=lambda c: c.poradi) | ||||
| 		return vc | ||||
| 
 | ||||
| 	def posledni_zverejnena_vysledkovka_cislo(self): | ||||
|  | @ -383,10 +395,18 @@ class Rocnik(SeminarModelBase): | |||
| 			cache.set(name, c, 300) | ||||
| 		return c | ||||
| 		 | ||||
| 	def save(self, *args, **kwargs): | ||||
| 		super().save(*args, **kwargs) | ||||
| 		# *Node.save() aktualizuje název *Nodu. | ||||
| 		try: | ||||
| 			self.rocniknode.save() | ||||
| 		except ObjectDoesNotExist: | ||||
| 			# Neexistující *Node nemá smysl aktualizovat. | ||||
| 			pass | ||||
| 
 | ||||
| def cislo_pdf_filename(self, filename): | ||||
| 	rocnik = str(self.rocnik.rocnik) | ||||
| 	return os.path.join('cislo', 'pdf', rocnik, '{}-{}.pdf'.format(rocnik, self.cislo)) | ||||
| 	return os.path.join('cislo', 'pdf', rocnik, '{}-{}.pdf'.format(rocnik, self.poradi)) | ||||
| 
 | ||||
| @reversion.register(ignore_duplicates=True) | ||||
| class Cislo(SeminarModelBase): | ||||
|  | @ -395,7 +415,7 @@ class Cislo(SeminarModelBase): | |||
| 		db_table = 'seminar_cisla' | ||||
| 		verbose_name = 'Číslo' | ||||
| 		verbose_name_plural = 'Čísla' | ||||
| 		ordering = ['-rocnik__rocnik', '-cislo'] | ||||
| 		ordering = ['-rocnik__rocnik', '-poradi'] | ||||
| 
 | ||||
| 	# Interní ID | ||||
| 	id = models.AutoField(primary_key = True) | ||||
|  | @ -403,7 +423,7 @@ class Cislo(SeminarModelBase): | |||
| 	rocnik = models.ForeignKey(Rocnik, verbose_name='ročník', related_name='cisla', | ||||
| 		db_index=True,on_delete=models.PROTECT) | ||||
| 
 | ||||
| 	cislo = models.CharField('název čísla', max_length=32, db_index=True, | ||||
| 	poradi = models.CharField('název čísla', max_length=32, db_index=True, | ||||
| 		help_text='Většinou jen "1", vyjímečně "7-8", lexikograficky určuje pořadí v ročníku!') | ||||
| 
 | ||||
| 	datum_vydani = models.DateField('datum vydání', blank=True, null=True, | ||||
|  | @ -436,20 +456,20 @@ class Cislo(SeminarModelBase): | |||
| 	# CisloNode | ||||
| 
 | ||||
| 	def kod(self): | ||||
| 		return '%s.%s' % (self.rocnik.rocnik, self.cislo) | ||||
| 		return '%s.%s' % (self.rocnik.rocnik, self.poradi) | ||||
| 	kod.short_description = 'Kód čísla' | ||||
| 
 | ||||
| 	def __str__(self): | ||||
| 		# Potenciální DB HOG, pokud by se ročník necachoval | ||||
| 		r = Rocnik.cached_rocnik(self.rocnik_id) | ||||
| 		return '{}.{}'.format(r.rocnik, self.cislo) | ||||
| 		return '{}.{}'.format(r.rocnik, self.poradi) | ||||
| 
 | ||||
| 	def verejne(self): | ||||
| 		return self.verejne_db | ||||
| 	verejne.boolean = True | ||||
| 
 | ||||
| 	def verejne_url(self): | ||||
| 		return reverse('seminar_cislo', kwargs={'rocnik': self.rocnik.rocnik, 'cislo': self.cislo}) | ||||
| 		return reverse('seminar_cislo', kwargs={'rocnik': self.rocnik.rocnik, 'cislo': self.poradi}) | ||||
| 
 | ||||
| 	def nasledujici(self): | ||||
| 		"Vrací None, pokud je toto poslední" | ||||
|  | @ -471,11 +491,20 @@ class Cislo(SeminarModelBase): | |||
| 	def get(cls, rocnik, cislo): | ||||
| 		try: | ||||
| 			r = Rocnik.objects.get(rocnik=rocnik) | ||||
| 			c = r.cisla.get(cislo=cislo) | ||||
| 			c = r.cisla.get(poradi=cislo) | ||||
| 		except ObjectDoesNotExist: | ||||
| 			return None | ||||
| 		return c | ||||
| 
 | ||||
| 	def save(self, *args, **kwargs): | ||||
| 		super().save(*args, **kwargs) | ||||
| 		# *Node.save() aktualizuje název *Nodu. | ||||
| 		try: | ||||
| 			self.cislonode.save() | ||||
| 		except ObjectDoesNotExist: | ||||
| 			# Neexistující *Node nemá smysl aktualizovat. | ||||
| 			pass | ||||
| 
 | ||||
| @reversion.register(ignore_duplicates=True) | ||||
| class Organizator(SeminarModelBase): | ||||
| # zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu | ||||
|  | @ -583,7 +612,8 @@ class Soustredeni(SeminarModelBase): | |||
| 
 | ||||
| 
 | ||||
| @reversion.register(ignore_duplicates=True) | ||||
| class Problem(SeminarModelBase): | ||||
| # Pozor na následující řádek. *Nekrmit, asi kouše!* | ||||
| class Problem(SeminarModelBase,PolymorphicModel): | ||||
| 
 | ||||
| 	class Meta: | ||||
| 		# Není abstraktní, protože se na něj jinak nedají dělat ForeignKeys. | ||||
|  | @ -601,11 +631,11 @@ class Problem(SeminarModelBase): | |||
| 	id = models.AutoField(primary_key = True) | ||||
| 
 | ||||
| 	# Název | ||||
| 	nazev = models.CharField('název', max_length=256) | ||||
| 	nazev = models.CharField('název', max_length=256) # Zveřejnitelný na stránky | ||||
| 
 | ||||
| 	# Problém má podproblémy | ||||
| 	nadproblem = models.ForeignKey('self', verbose_name='nadřazený problém', | ||||
| 		related_name='nadproblem_%(class)s', null=True, blank=True, | ||||
| 		related_name='podproblem', null=True, blank=True, | ||||
| 		on_delete=models.SET_NULL) | ||||
| 
 | ||||
| 	STAV_NAVRH = 'navrh' | ||||
|  | @ -698,6 +728,9 @@ class Tema(Problem): | |||
| 	rocnik = models.ForeignKey(Rocnik, verbose_name='ročník',blank=True, null=True, | ||||
| 		on_delete=models.PROTECT) | ||||
| 
 | ||||
| 	abstrakt = models.TextField('Abstrakt na rozcestník', blank=True) | ||||
| 	obrazek = models.ImageField('Obrázek na rozcestník', null=True) | ||||
| 
 | ||||
| 	def kod_v_rocniku(self): | ||||
| 		if self.stav == 'zadany': | ||||
| 			if self.nadproblem: | ||||
|  | @ -705,6 +738,12 @@ class Tema(Problem): | |||
| 			return "t{}".format(self.kod) | ||||
| 		return '<Není zadaný>' | ||||
| 
 | ||||
| 	def save(self, *args, **kwargs): | ||||
| 		super().save(*args, **kwargs) | ||||
| 		# *Node.save() aktualizuje název *Nodu. | ||||
| 		for tvcn in self.temavcislenode_set.all(): | ||||
| 			tvcn.save() | ||||
| 
 | ||||
| class Clanek(Problem): | ||||
| 	class Meta: | ||||
| 		db_table = 'seminar_clanky' | ||||
|  | @ -725,6 +764,15 @@ class Clanek(Problem): | |||
| 			return "c{}".format(self.kod) | ||||
| 		return '<Není zadaný>' | ||||
| 
 | ||||
| 	def save(self, *args, **kwargs): | ||||
| 		super().save(*args, **kwargs) | ||||
| 		# *Node.save() aktualizuje název *Nodu. | ||||
| 		try: | ||||
| 			self.claneknode.save() | ||||
| 		except ObjectDoesNotExist: | ||||
| 			# Neexistující *Node nemá smysl aktualizovat. | ||||
| 			pass | ||||
| 
 | ||||
| class Text(SeminarModelBase): | ||||
| 	class Meta: | ||||
| 		db_table = 'seminar_texty' | ||||
|  | @ -742,7 +790,16 @@ class Text(SeminarModelBase): | |||
| 	 | ||||
| 	# obrázky mají návaznost opačným směrem (vazba z druhé strany) | ||||
| 
 | ||||
| 	def save(self, *args, **kwargs): | ||||
| 		super().save(*args, **kwargs) | ||||
| 		# *Node.save() aktualizuje název *Nodu. | ||||
| 		for tn in self.textnode_set.all(): | ||||
| 			tn.save() | ||||
| 
 | ||||
| 	def __str__(self): | ||||
| 		parser = FirstTagParser()		 | ||||
| 		parser.feed(str(self.na_web)) | ||||
| 		return parser.firstTag | ||||
| 		 | ||||
| class Uloha(Problem): | ||||
| 	class Meta: | ||||
|  | @ -770,12 +827,26 @@ class Uloha(Problem): | |||
| 
 | ||||
| 	def kod_v_rocniku(self): | ||||
| 		if self.stav == 'zadany': | ||||
| 			name="{}.u{}".format(self.cislo_zadani.cislo,self.kod) | ||||
| 			name="{}.u{}".format(self.cislo_zadani.poradi,self.kod) | ||||
| 			if self.nadproblem: | ||||
| 				return self.nadproblem.kod_v_rocniku()+name | ||||
| 			return name | ||||
| 		return '<Není zadaný>' | ||||
| 
 | ||||
| 	def save(self, *args, **kwargs): | ||||
| 		super().save(*args, **kwargs) | ||||
| 		# *Node.save() aktualizuje název *Nodu. | ||||
| 		try: | ||||
| 			self.ulohazadaninode.save() | ||||
| 		except ObjectDoesNotExist: | ||||
| 			# Neexistující *Node nemá smysl aktualizovat. | ||||
| 			pass | ||||
| 		try: | ||||
| 			self.ulohavzoraknode.save() | ||||
| 		except ObjectDoesNotExist: | ||||
| 			# Neexistující *Node nemá smysl aktualizovat. | ||||
| 			pass | ||||
| 
 | ||||
| 
 | ||||
| @reversion.register(ignore_duplicates=True) | ||||
| class Reseni(SeminarModelBase): | ||||
|  | @ -828,7 +899,7 @@ class Reseni(SeminarModelBase): | |||
| 	# Konfera | ||||
| 
 | ||||
| 	def __str__(self): | ||||
| 		return "{}: {}".format(self.resitel.osoba.plne_jmeno(), self.problem.nazev) | ||||
| 		return "{}({}): {}({})".format(self.resitele.first(),len(self.resitele.all()), self.problem.first() ,len(self.problem.all())) | ||||
| 		# NOTE: Potenciální DB HOG (bez select_related) | ||||
| 
 | ||||
| ## Pravdepodobne uz nebude potreba: | ||||
|  | @ -856,7 +927,8 @@ class Hodnoceni(SeminarModelBase): | |||
| 
 | ||||
| 	reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) | ||||
| 
 | ||||
| 	problem = models.ForeignKey(Problem, verbose_name='problém', on_delete=models.PROTECT) | ||||
| 	problem = models.ForeignKey(Problem, verbose_name='problém',  | ||||
| 		related_name='hodnoceni', on_delete=models.PROTECT) | ||||
| 
 | ||||
| 	def __str__(self): | ||||
| 		return "{}, {}, {}".format(self.problem, self.reseni, self.body) | ||||
|  | @ -957,6 +1029,14 @@ class Pohadka(SeminarModelBase): | |||
| 		uryvek = self.text if len(self.text) < 50 else self.text[:(50-3)]+"..." | ||||
| 		return uryvek | ||||
| 
 | ||||
| 	def save(self, *args, **kwargs): | ||||
| 		super().save(*args, **kwargs) | ||||
| 		# *Node.save() aktualizuje název *Nodu. | ||||
| 		try: | ||||
| 			self.pohadkanode.save() | ||||
| 		except ObjectDoesNotExist: | ||||
| 			# Neexistující *Node nemá smysl aktualizovat. | ||||
| 			pass | ||||
| 
 | ||||
| @reversion.register(ignore_duplicates=True) | ||||
| class Soustredeni_Ucastnici(SeminarModelBase): | ||||
|  | @ -1020,7 +1100,7 @@ class Konfera(models.Model): | |||
| 	# Interní ID | ||||
| 	id = models.AutoField(primary_key = True) | ||||
| 
 | ||||
| 	nazev = models.CharField('název konfery', max_length=40, help_text = 'Název konfery') | ||||
| 	nazev = models.CharField('název konfery', max_length=100, help_text = 'Název konfery') | ||||
| 	 | ||||
| 	anotace = models.TextField('anotace', blank=True, | ||||
| 		help_text='Popis, o čem bude konfera.') | ||||
|  | @ -1067,6 +1147,15 @@ class Konfera(models.Model): | |||
| 	def __str__(self): | ||||
| 		return "{}: ({})".format(self.nazev, self.soustredeni) | ||||
| 
 | ||||
| 	def save(self, *args, **kwargs): | ||||
| 		super().save(*args, **kwargs) | ||||
| 		# *Node.save() aktualizuje název *Nodu. | ||||
| 		try: | ||||
| 			self.konferanode.save() | ||||
| 		except ObjectDoesNotExist: | ||||
| 			# Neexistující *Node nemá smysl aktualizovat. | ||||
| 			pass | ||||
| 
 | ||||
| 
 | ||||
| # Vazebna tabulka. Mozna se generuje automaticky. | ||||
| @reversion.register(ignore_duplicates=True) | ||||
|  | @ -1139,12 +1228,13 @@ class Obrazek(SeminarModelBase): | |||
| 			help_text = 'Černobílá verze obrázku do čísla',  | ||||
| 			upload_to = 'obrazky/%Y/%m/%d/', blank=True, null=True) | ||||
| 
 | ||||
| class TreeNode(models.Model): | ||||
| class TreeNode(PolymorphicModel): | ||||
| 	class Meta: | ||||
| 		db_table = "seminar_nodes_treenode" | ||||
| 		verbose_name = "TreeNode" | ||||
| 		verbose_name_plural = "TreeNody" | ||||
| 
 | ||||
| 	# TODO: Nechceme radši jako root vyžadovat přímo RocnikNode? | ||||
| 	root = models.ForeignKey('TreeNode', | ||||
| 		related_name="potomci_set", | ||||
| 		null = True, | ||||
|  | @ -1162,14 +1252,59 @@ class TreeNode(models.Model): | |||
| 		blank = True, | ||||
| 		on_delete=models.SET_NULL, | ||||
| 		verbose_name="další element na stejné úrovni") | ||||
| 	nazev = models.TextField("název tohoto node", | ||||
| 		help_text = "Tento název se zobrazuje v nabídkách pro výběr vhodného TreeNode", | ||||
| 		blank=False,  | ||||
| 		null=True) # Nezveřejnitelný název na stránky - pouze do adminu | ||||
| 	zajimave = models.BooleanField(default = False, | ||||
| 		verbose_name = "Zajímavé", | ||||
| 		help_text = "Zobrazí se daná věc na rozcestníku témátek") | ||||
| 	srolovatelne = models.BooleanField(null = True, blank = True, | ||||
| 		verbose_name = "Srolovatelné", | ||||
| 		help_text = "Bude na stránce témátka možnost tuto položku skrýt") | ||||
| 	 | ||||
| 	# Slouží k debugování pro rychlé získání představy o podobě podstromu pod tímto TreeNode. | ||||
| 	def print_tree(self,indent=0): | ||||
| 		print("{}TreeNode({})".format(" "*indent,self.id)) | ||||
| 		# FIXME: Tady se spoléháme na to, že nedeklarovaný primární klíč se jmenuje by default 'id', což není úplně správně | ||||
| 		print("{}{} (id: {})".format(" "*indent,self, self.id)) | ||||
| 		if self.first_child: | ||||
| 			self.first_child.print_tree(indent=indent+2) | ||||
| 		if self.succ: | ||||
| 			self.succ.print_tree(indent=indent) | ||||
| 
 | ||||
| 	def getOdkazStr(self): # String na rozcestník | ||||
| 		return self.first_child.getOdkazStr() | ||||
| 
 | ||||
| 	def getOdkaz(self): # ID HTML tagu, na který se bude scrollovat #{{self.getOdkaz}} | ||||
| 	# Jsem si vědom, že tu potenciálně vznikají kolize. | ||||
| 	# Přijdou mi natolik nepravděpodobné, že je neřeším | ||||
| 	# Chtěl jsem ale hezké odkazy | ||||
| 		string = unidecode(self.getOdkazStr()) | ||||
| 		returnVal = "" | ||||
| 		i = 0	 | ||||
| 		while len(returnVal) < 16: # Max 15 znaků | ||||
| 			if i == len(string): | ||||
| 				break | ||||
| 			if string[i] == " ": | ||||
| 				returnVal += "-" | ||||
| 			if string[i].isalnum(): | ||||
| 				returnVal += string[i].lower() | ||||
| 			i += 1 | ||||
| 		return returnVal | ||||
| 
 | ||||
| 	def __str__(self): | ||||
| 		if self.nazev: | ||||
| 			return self.nazev | ||||
| 		else: | ||||
| 			#TODO: logování | ||||
| 			return "Nepojmenovaný Treenode" | ||||
| 	 | ||||
| 	def save(self, *args, **kwargs): | ||||
| 		self.aktualizuj_nazev() | ||||
| 		super().save(*args, **kwargs) | ||||
| 
 | ||||
| 	def aktualizuj_nazev(self): | ||||
| 		raise NotImplementedError("Pokus o aktualizaci názvu obecného TreeNode místo konkrétní instance") | ||||
| 
 | ||||
| class RocnikNode(TreeNode): | ||||
| 	class Meta: | ||||
|  | @ -1180,6 +1315,9 @@ class RocnikNode(TreeNode): | |||
| 		on_delete = models.PROTECT, # Pokud chci mazat ročník, musím si Node pořešit ručně | ||||
| 		verbose_name = "ročník") | ||||
| 
 | ||||
| 	def aktualizuj_nazev(self): | ||||
| 		self.nazev = "RocnikNode: "+str(self.rocnik) | ||||
| 
 | ||||
| class CisloNode(TreeNode): | ||||
| 	class Meta: | ||||
| 		db_table = 'seminar_nodes_cislo' | ||||
|  | @ -1189,12 +1327,36 @@ class CisloNode(TreeNode): | |||
| 		on_delete = models.PROTECT, # Pokud chci mazat číslo, musím si Node pořešit ručně | ||||
| 		verbose_name = "číslo") | ||||
| 
 | ||||
| 	def aktualizuj_nazev(self): | ||||
| 		self.nazev = "CisloNode: "+str(self.cislo) | ||||
| 
 | ||||
| 	def getOdkazStr(self): | ||||
| 		return "Číslo " + str(self.cislo) | ||||
| 
 | ||||
| class MezicisloNode(TreeNode): | ||||
| 	class Meta: | ||||
| 		db_table = 'seminar_nodes_mezicislo' | ||||
| 		verbose_name = 'Mezičíslo (Node)' | ||||
| 		verbose_name_plural = 'Mezičísla (Node)' | ||||
| 
 | ||||
| 	def aktualizuj_nazev(self): | ||||
| 		if self.prev: | ||||
| 			if (self.prev.get_real_instance_class() != CisloNode and | ||||
| 				self.prev.get_real_instance_class() != MezicisloNode): | ||||
| 					raise ValueError("Předchůdce není číslo!") | ||||
| 			posledni = self.prev.cislo | ||||
| 			self.nazev = "MezicisloNode: Mezičíslo po čísle"+str(posledni) | ||||
| 		elif self.root: | ||||
| 			if self.root.get_real_instance_class() != RocnikNode: | ||||
| 				raise ValueError("Kořen stromu není ročník!") | ||||
| 			rocnik = self.root.rocnik | ||||
| 			self.nazev = "MezicisloNode: První mezičíslo ročníku "+str(rocnik) | ||||
| 		else: | ||||
| 			print("!!!!! Nějaké neidentifikované mezičíslo !!!!!") | ||||
| 			self.nazev = "MezicisloNode: Neidentifikovatelné mezičíslo!" | ||||
| 	def getOdkazStr(self): | ||||
| 		return "Obsah dostupný pouze na webu" | ||||
| 
 | ||||
| class TemaVCisleNode(TreeNode): | ||||
| 	""" Obsahuje příspěvky k tématu v daném čísle """ | ||||
| 	class Meta: | ||||
|  | @ -1205,6 +1367,12 @@ class TemaVCisleNode(TreeNode): | |||
| 		on_delete=models.PROTECT, # Pokud chci mazat téma, musím si Node pořešit ručně | ||||
| 		verbose_name = "téma v čísle") | ||||
| 
 | ||||
| 	def aktualizuj_nazev(self): | ||||
| 		self.nazev = "TemaVCisleNode: "+str(self.tema) | ||||
| 
 | ||||
| 	def getOdkazStr(self): | ||||
| 		return str(self.tema) | ||||
| 
 | ||||
| class KonferaNode(TreeNode): | ||||
| 	class Meta: | ||||
| 		db_table = 'seminar_nodes_konfera' | ||||
|  | @ -1216,6 +1384,9 @@ class KonferaNode(TreeNode): | |||
| 		null=True, | ||||
| 		blank=False) | ||||
| 
 | ||||
| 	def aktualizuj_nazev(self): | ||||
| 		self.nazev = "KonferaNode: "+str(self.konfera) | ||||
| 
 | ||||
| class ClanekNode(TreeNode): | ||||
| 	class Meta: | ||||
| 		db_table = 'seminar_nodes_clanek' | ||||
|  | @ -1227,6 +1398,13 @@ class ClanekNode(TreeNode): | |||
| 		null=True, | ||||
| 		blank=False) | ||||
| 
 | ||||
| 	def aktualizuj_nazev(self): | ||||
| 		self.nazev = "ClanekNode: "+str(self.clanek) | ||||
| 
 | ||||
| 	def getOdkazStr(self): | ||||
| 		return str(self.clanek) | ||||
| 
 | ||||
| 
 | ||||
| class UlohaZadaniNode(TreeNode): | ||||
| 	class Meta: | ||||
| 		db_table = 'seminar_nodes_uloha_zadani' | ||||
|  | @ -1238,6 +1416,13 @@ class UlohaZadaniNode(TreeNode): | |||
| 		null=True, | ||||
| 		blank=False) | ||||
| 
 | ||||
| 	def aktualizuj_nazev(self): | ||||
| 		self.nazev = "UlohaZadaniNode: "+str(self.uloha) | ||||
| 
 | ||||
| 	def getOdkazStr(self): | ||||
| 		return str(self.uloha) | ||||
| 
 | ||||
| 
 | ||||
| class PohadkaNode(TreeNode): | ||||
| 	class Meta: | ||||
| 		db_table = 'seminar_nodes_pohadka' | ||||
|  | @ -1248,6 +1433,9 @@ class PohadkaNode(TreeNode): | |||
| 		verbose_name = "pohádka", | ||||
| 		) | ||||
| 
 | ||||
| 	def aktualizuj_nazev(self): | ||||
| 		self.nazev = "PohadkaNode: "+str(self.pohadka) | ||||
| 
 | ||||
| class UlohaVzorakNode(TreeNode): | ||||
| 	class Meta: | ||||
| 		db_table = 'seminar_nodes_uloha_vzorak' | ||||
|  | @ -1259,6 +1447,13 @@ class UlohaVzorakNode(TreeNode): | |||
| 		null=True, | ||||
| 		blank=False) | ||||
| 
 | ||||
| 	def aktualizuj_nazev(self): | ||||
| 		self.nazev = "UlohaVzorakNode: "+str(self.uloha) | ||||
| 
 | ||||
| 	def getOdkazStr(self): | ||||
| 		return str(self.uloha) | ||||
| 
 | ||||
| 
 | ||||
| class TextNode(TreeNode): | ||||
| 	class Meta: | ||||
| 		db_table = 'seminar_nodes_obsah' | ||||
|  | @ -1268,6 +1463,13 @@ class TextNode(TreeNode): | |||
| 		on_delete=models.PROTECT, | ||||
| 		verbose_name = 'text') | ||||
| 	 | ||||
| 	def aktualizuj_nazev(self): | ||||
| 		self.nazev = "TextNode: "+str(self.text) | ||||
| 
 | ||||
| 	def getOdkazStr(self): | ||||
| 		return str(self.text) | ||||
| 
 | ||||
| 
 | ||||
| ## FIXME: Logiku přesunout do views. | ||||
| #class VysledkyBase(SeminarModelBase): | ||||
| # | ||||
|  | @ -1292,7 +1494,7 @@ class TextNode(TreeNode): | |||
| # | ||||
| #	def __str__(self): | ||||
| #		return "%s: %sb (%s)".format(self.resitel.plne_jmeno(), self.body,  | ||||
| #		str(self.cislo)) | ||||
| #		str(self.poradi)) | ||||
| #		# NOTE: DB zatez pri vypisu (ale nepouzivany) | ||||
| 
 | ||||
| 
 | ||||
|  | @ -1342,7 +1544,7 @@ class TextNode(TreeNode): | |||
| # | ||||
| #	def __str__(self): | ||||
| #		# NOTE: DB HOG (ale nepouzivany) | ||||
| #		return "%s: %sb / %sb (do %s)" % (self.resitel.plne_jmeno(), self.body, self.body_celkem, str(self.cislo)) | ||||
| #		return "%s: %sb / %sb (do %s)" % (self.resitel.plne_jmeno(), self.body, self.body_celkem, str(self.poradi)) | ||||
| ##mozna potreba upravit | ||||
| 
 | ||||
| 
 | ||||
|  | @ -1353,12 +1555,16 @@ class Nastaveni(SingletonModel): | |||
| 		db_table = 'seminar_nastaveni' | ||||
| 		verbose_name = 'Nastavení semináře' | ||||
| 
 | ||||
| 	aktualni_rocnik = models.ForeignKey(Rocnik, verbose_name='aktuální ročník', | ||||
| 		null=False, on_delete=models.PROTECT) | ||||
| #	aktualni_rocnik = models.ForeignKey(Rocnik, verbose_name='aktuální ročník', | ||||
| #		null=False, on_delete=models.PROTECT) | ||||
| 
 | ||||
| 	aktualni_cislo = models.ForeignKey(Cislo, verbose_name='poslední vydané číslo',  | ||||
| 		null=False, on_delete=models.PROTECT) | ||||
| 
 | ||||
| 	@property | ||||
| 	def aktualni_rocnik(self): | ||||
| 		return self.aktualni_cislo.rocnik | ||||
| 
 | ||||
| 	def __str__(self): | ||||
| 		return 'Nastavení semináře' | ||||
| 
 | ||||
|  | @ -1399,3 +1605,35 @@ class Novinky(models.Model): | |||
| 			return '[' + str(self.datum) + '] ' + self.text[0:50] | ||||
| 		else: | ||||
| 			return '[' + str(self.datum) + '] ' | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| # FIXME: Tohle nepatří do aplikace 'seminar' | ||||
| # Nefunkční alternativa vestavěného Usera, který má jméno a mail v přidružené Osobě | ||||
| # from django.contrib.auth.models import User as Django_User | ||||
| #  | ||||
| # class Uzivatel(Django_User): | ||||
| # 	class Meta: | ||||
| # 		proxy = True | ||||
| #  | ||||
| # 	@property | ||||
| # 	def first_name(self): | ||||
| # 		osoby = Osoba.objects.filter(user=self) | ||||
| # 		if len(osoby) == 0: | ||||
| # 			return None | ||||
| # 		return osoby.first().krestni_jmeno | ||||
| #  | ||||
| # 	@property | ||||
| # 	def last_name(self): | ||||
| # 		osoby = Osoba.objects.filter(user=self) | ||||
| # 		if len(osoby) == 0: | ||||
| # 			return None | ||||
| # 		return osoby.first().prijmeni | ||||
| #  | ||||
| # 	@property | ||||
| # 	def email(self): | ||||
| # 		osoby = Osoba.objects.filter(user=self) | ||||
| # 		if len(osoby) == 0: | ||||
| # 			return None | ||||
| # 		return osoby.first().email | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								seminar/static/seminar/lisak.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								seminar/static/seminar/lisak.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										32
									
								
								seminar/static/seminar/prihlaska.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								seminar/static/seminar/prihlaska.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | |||
| function addrCountryChanged(){ | ||||
| 	var stat_select = document.getElementById('id_stat'); | ||||
| 	var stat_text = document.getElementById('id_li_stat_text');	 | ||||
| 	var stat = stat_select[stat_select.selectedIndex].value; | ||||
| 	if (stat === "other"){ | ||||
| 		stat_text.style.display="block";	 | ||||
| 	} else { | ||||
| 		stat_text.style.display="none";	 | ||||
| 		$('#id_stat_text').val(""); | ||||
| 	} | ||||
| } | ||||
| function hideSchoolTextfields(){ | ||||
| 	var skola_nazev = document.getElementById('id_li_skola_nazev'); | ||||
| 	var skola_adresa = document.getElementById('id_li_skola_adresa'); | ||||
| 	skola_nazev.style.display="none"; | ||||
| 	skola_adresa.style.display="none"; | ||||
| 	 | ||||
| } | ||||
| function schoolNotInList(){ | ||||
| 	var skola_nazev = document.getElementById('id_li_skola_nazev'); | ||||
| 	var skola_adresa = document.getElementById('id_li_skola_adresa'); | ||||
| 	// FIXME nefunguje a nevim proc (TypeError: $(...).select2 is not a function)
 | ||||
| 	//var skola_select = $('#id_skola').select2();
 | ||||
| 	//skola_select.val(null).trigger('change');
 | ||||
| 	skola_nazev.style.display="block"; | ||||
| 	skola_adresa.style.display="block"; | ||||
| } | ||||
| 
 | ||||
| document.addEventListener("DOMContentLoaded", function(){ | ||||
| 	addrCountryChanged(); | ||||
| 	hideSchoolTextfields(); | ||||
| }); | ||||
|  | @ -86,7 +86,8 @@ | |||
| % Tohle makro vysází samotnou obálku | ||||
| \def\obalka#1#2#3#4#5#6#7{ | ||||
| % Horní a pravý okraj je zároveň okraj stránky, resetujeme odsazení | ||||
| \includegraphics[height=2.55cm]{lisak.eps}\hskip 1 em\vbox{% | ||||
| \includegraphics[height=2.55cm]{lisak.pdf} | ||||
| \vbox{% | ||||
| \adresaMaM} | ||||
| \vskip 7.3 cm % Od oka | ||||
| \hskip\toskip% | ||||
|  |  | |||
							
								
								
									
										78
									
								
								seminar/templates/seminar/edit.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								seminar/templates/seminar/edit.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,78 @@ | |||
| {% extends "seminar/zadani/base.html" %} | ||||
| {% load staticfiles %} | ||||
| 
 | ||||
| {% block script %} | ||||
|     <!--script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script!--> | ||||
|     {{form.media}} | ||||
|     <script src="{% static 'seminar/prihlaska.js' %}"></script> | ||||
| {% endblock %} | ||||
| {% block content %} | ||||
| <h1> | ||||
|   {% block nadpis1a %}{% block nadpis1b %} | ||||
|    Změna osobních údajů | ||||
|   {% endblock %}{% endblock %} | ||||
| </h1> | ||||
| <form action="{% url 'seminar_resitel_edit' %}" method="post"> | ||||
|  {% csrf_token %} | ||||
|  {{form.non_field_errors}} | ||||
|  <ul class="form"> | ||||
|      <li> | ||||
|      Přihlašovací údaje | ||||
|      </li><li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.username %} | ||||
|      </li><li> | ||||
|      Osobní údaje | ||||
|      </li><li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.jmeno %} | ||||
|      </li><li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.prijmeni %} | ||||
|      </li><li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.pohlavi_muz%} | ||||
|      </li><li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.email %} | ||||
|      </li><li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.telefon %} | ||||
|      </li><li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.datum_narozeni %} | ||||
|      </li><li> | ||||
|   <hr> | ||||
|        Bydliště | ||||
|      </li><li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.ulice %} | ||||
|      </li><li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.mesto %} | ||||
|      </li><li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.psc %} | ||||
|      </li><li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.stat %} | ||||
|      </li> | ||||
|      <li id="id_li_stat_text"> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.stat_text %} | ||||
|      </li><li> | ||||
|  <hr> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.skola %} | ||||
|      </li><li> | ||||
|        <button id="id_skola_text_button" type="button">Škola není v seznamu</button> | ||||
|      </li> | ||||
|      <li id="id_li_skola_nazev"> | ||||
|        Vyplň prosím celý název a adresu školy.<br> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.skola_nazev %} | ||||
|      </li> | ||||
|      <li id="id_li_skola_adresa"> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.skola_adresa %} | ||||
|      </li><li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.rok_maturity %} | ||||
|      </li><li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.zasilat %} | ||||
|      </li><li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.spam %} | ||||
|      </li> | ||||
| </ul> | ||||
|     <input type="submit" value="Změnit"> | ||||
| </form> | ||||
| <script> | ||||
| $("#id_stat").on("change",addrCountryChanged); | ||||
| $("#id_skola_text_button").on("click",schoolNotInList); | ||||
| </script> | ||||
| {% endblock %} | ||||
| 
 | ||||
							
								
								
									
										49
									
								
								seminar/templates/seminar/gdpr.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								seminar/templates/seminar/gdpr.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | |||
| <p> | ||||
| TL;DR: | ||||
| K tomu, abychom mohli zpracovávat Tvá data (uložit si tvou adresu, zobrazit Tvé jméno ve výsledkové listině, opravit Tvá řešení) od Tebe potřebujeme souhlas. | ||||
| Pokud se zpracováváním souhlasíš dle níže uvedených podmínek, zaškrtni políčko níže. | ||||
| </p> | ||||
| <div class="gdpr"> | ||||
| <p class="gdpr"> | ||||
| Získáváme od Tebe údaje vyplněné v přihlášce do semináře (jméno, příjmení, poštovní a e-mailovou adresu, školu, kterou navštěvuješ a rok maturity), případně v přihlášce na soustředění (navíc datum narození, telefonní číslo). Také uchováváme všechna řešení, která nám pošleš, a jejich hodnocení. | ||||
| </p> | ||||
| <p class="gdpr"> | ||||
| Slibujeme Ti, že Tvá osobní data nezneužijeme k ničemu, co by nesouviselo s M&M nebo s dalšími aktivitami Matfyzu, a nikdy je nepředáme nikomu cizímu. Údaje využíváme k zajištění chodu semináře a také je sdílíme s ostatními propagačními akcemi Matfyzu, abychom mohli vyhodnocovat úspěšnost akcí. Pokud budeš mít zájem, budeme Ti také posílat zajímavé zprávy a novinky týkajíci se Matfyzu. | ||||
| </p> | ||||
| <p class="gdpr"> | ||||
| Veřejně vystavujeme pouze výsledkové listiny, které také uchováváme pro archivní účely. Pokud ale z nějakého důvodu nebudeš chtít mít své jméno či školu uvedené ve výsledkové listině, není problém to zařídit, napiš nám. Z tištěných materiálů samozřejmě údaje už odstranit nemůžeme. | ||||
| </p> | ||||
| <p class="gdpr"> | ||||
| Na soustředěních a dalších akcích semináře navíc pořizujeme fotografie a videozáznamy a používáme je ke zpravodajským a propagačním účelům. Pro propagační účely si od Tebe vyžádáme samostatný souhlas na začátku akce. | ||||
| </p> | ||||
| <p class="gdpr"> | ||||
| <i>Souhlas se zpracováním osobních údajů pro potřeby chodu semináře</i> | ||||
| </p> | ||||
| <p class="gdpr"> | ||||
| Tímto uděluji souhlas Univerzitě Karlově, se sídlem Ovocný trh 560/5, 116 36 Praha 1, IČO 00216208 (dále jen UK), která je správcem osobních údajů všech fakult a součástí UK, ke zpracování osobních údajů pro potřeby Korespondenčního semináře M&M a Matematicko-fyzikální fakulty UK (dále jen M&M a MFF UK). | ||||
| </p> | ||||
| <p class="gdpr"> | ||||
| Tento souhlas uděluji pro všechny výše uvedené osobní údaje, a to po dobu účasti v semináři a 10 let poté, a dále souhlasím s uchováváním potřebných dat pro archivní účely i po této lhůtě (vystavené výsledkové listiny aj.). | ||||
| </p> | ||||
| <p class="gdpr"> | ||||
| MFF UK tyto údaje zpracovává za účelem evidence řešitelů a účastníků M&M, k zajištění celoročního fungování semináře, analýze účinnosti jednotlivých propagačních akcí MFF UK a zpravodajským účelům. Osobám, které o to projeví zájem v nastavení svého účtu, bude MFF UK také zasílat propagační materiály. | ||||
| </p> | ||||
| <p class="gdpr"> | ||||
| Údaje nebudou předány třetí osobě ani využívány k jiným účelům, než ke kterým byly poskytnuty. | ||||
| </p> | ||||
| <p class="gdpr"> | ||||
| Tento souhlas uděluji ze své vlastní a svobodné vůle a beru na vědomí, že jej mohu kdykoliv odvolat zasláním e-mailu na adresu mam@matfyz.cz. Stejně tak může být požadováno vymazání i z archivních údajů M&M, pokud to bude technicky možné. Beru na vědomí, že údaje z tištěných publikací není možné zpětně odstranit. | ||||
| </p> | ||||
| <p class="gdpr"> | ||||
| Dále máte právo: | ||||
| <ul> | ||||
| <li>požádat o informaci, jaké osobní údaje jsou o vás zpracovávány, | ||||
| <li>požadovat opravu osobních údajů, pokud jsou neplatné nebo zastaralé, | ||||
| <li>požadovat, aby nebyly vaše osobní údaje zpracovávány do doby, než bude vyřešena oprávněnost výše uvedených požadavků, | ||||
| <li>požadovat, aby byly vaše osobní údaje předány jinému správci, | ||||
| <li>podat stížnost u dozorového úřadu. | ||||
| </p> | ||||
| <p class="gdpr"> | ||||
| V případě jakéhokoliv dotazu nebo uplatnění svých práv můžete kontaktovat pověřence pro ochranu osobních údajů na e-mailové adrese gdpr@cuni.cz. | ||||
| </p> | ||||
| </div> | ||||
							
								
								
									
										26
									
								
								seminar/templates/seminar/login.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								seminar/templates/seminar/login.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | |||
| {% extends "seminar/zadani/base.html" %} | ||||
| {% load staticfiles %} | ||||
| 
 | ||||
| 
 | ||||
| {% block content %} | ||||
| <h1> | ||||
|   {% block nadpis1a %}{% block nadpis1b %} | ||||
|     Přihlášení | ||||
|   {% endblock %}{% endblock %} | ||||
| </h1> | ||||
| <form action="{% url 'login' %}" method="post"> | ||||
|   {% csrf_token %} | ||||
|   <ul class="form"> | ||||
|     {{ form.as_ul }} | ||||
| </ul> | ||||
| 	{# Django si posílá jméno další stránky jako obsah formuláře a výchozí hodnota (mi přišlo, že) nejde změnit... #} | ||||
| 	<input type="hidden" name='next' value="{{ next }}"> | ||||
|     <input type="submit" value="Přihlásit"> | ||||
| </form> | ||||
| 
 | ||||
| <a href="{% url 'reset_password' %}">Zapomněl jsem heslo</a><br> | ||||
| <a href="{% url 'seminar_prihlaska' %}">Zaregistrovat</a><br> | ||||
| 
 | ||||
| 
 | ||||
| {% endblock %} | ||||
| 
 | ||||
							
								
								
									
										18
									
								
								seminar/templates/seminar/logout.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								seminar/templates/seminar/logout.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| {% extends "seminar/zadani/base.html" %} | ||||
| {% load staticfiles %} | ||||
| 
 | ||||
| 
 | ||||
| {% block content %} | ||||
| <h1> | ||||
|   {% block nadpis1a %}{% block nadpis1b %} | ||||
|     Odhlášení | ||||
|   {% endblock %}{% endblock %} | ||||
| </h1> | ||||
| 
 | ||||
| Byl jsi úspěšně odhlášen | ||||
| {# Tohle by se asi mělo udělat přes kontext (title), ale kašlu na to, stejně je to jen jednojazyčná stránka #} | ||||
| 
 | ||||
| {# TODO: odkaz na znovupřihlášení? #} | ||||
| 
 | ||||
| {% endblock %} | ||||
| 
 | ||||
							
								
								
									
										30
									
								
								seminar/templates/seminar/org/obalkovani.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								seminar/templates/seminar/org/obalkovani.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,30 @@ | |||
| {% extends "base.html" %} | ||||
| 
 | ||||
| {% block content %} | ||||
|   <h1> | ||||
|     {% block nadpis1a %}{% block nadpis1b %} | ||||
|       Obálkování {{ cislo }} | ||||
|     {% endblock %}{% endblock %} | ||||
|   </h1> | ||||
|   <ul> | ||||
|   {% for reseni in object_list %} | ||||
| 	{% ifchanged reseni.resitele %} | ||||
| 		{% if not forloop.first %} | ||||
| 		 </ul> | ||||
| 		{% endif %} | ||||
| 		<h4>{% for resitel in reseni.resitele.all %}{{resitel.osoba}},{% endfor %}</h4> | ||||
| 	<ul> | ||||
| 	{% endifchanged %} | ||||
| 
 | ||||
|   <li>Celkem {{reseni.hodnoceni__body__sum}} bodů z {{reseni.hodnoceni__count}} hodnocení | ||||
| 	<ul> | ||||
| 		{% for h in reseni.hodnoceni_set.all %} | ||||
| 		<li> {{ h.problem }}: {{ h.body }}b </li> | ||||
| 		{% endfor %} | ||||
| 	</ul> | ||||
|   </li> | ||||
|   {% endfor %} | ||||
|   </ul> | ||||
| 
 | ||||
| 
 | ||||
| {% endblock content %} | ||||
							
								
								
									
										21
									
								
								seminar/templates/seminar/org/vloz_reseni.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								seminar/templates/seminar/org/vloz_reseni.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| {% extends "seminar/zadani/base.html" %} | ||||
| {% load staticfiles %} | ||||
| {% block script %} | ||||
|     <!--script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script!--> | ||||
|     {{form.media}} | ||||
|     <script src="{% static 'seminar/prihlaska.js' %}"></script> | ||||
| {% endblock %} | ||||
| 
 | ||||
| {% block content %} | ||||
| <h1> | ||||
|   {% block nadpis1a %}{% block nadpis1b %} | ||||
|     Vložit řešení | ||||
|   {% endblock %}{% endblock %} | ||||
| </h1> | ||||
| <form action="{% url 'seminar_vloz_reseni' %}" method="post"> | ||||
|   {% csrf_token %} | ||||
| {{form.as_p}} | ||||
|     <input type="submit" value="Vložit"> | ||||
| </form> | ||||
| 
 | ||||
| {% endblock %} | ||||
|  | @ -1,5 +1,112 @@ | |||
| <form action="/prihlaska/" method="post"> | ||||
|     {% csrf_token %} | ||||
|     {{ form }} | ||||
|     <input type="submit" value="Submit"> | ||||
| {% extends "seminar/zadani/base.html" %} | ||||
| {% load staticfiles %} | ||||
| 
 | ||||
| {% block script %} | ||||
|     <!--script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script!--> | ||||
|     {{form.media}} | ||||
|     <script src="{% static 'seminar/prihlaska.js' %}"></script> | ||||
| {% endblock %} | ||||
| 
 | ||||
| {% block content %} | ||||
| <h1> | ||||
|   {% block nadpis1a %}{% block nadpis1b %} | ||||
|     Přihláška do semináře | ||||
|   {% endblock %}{% endblock %} | ||||
| </h1> | ||||
| 
 | ||||
| 
 | ||||
| <form action="{% url 'seminar_prihlaska' %}" method="post"> | ||||
|   {% csrf_token %} | ||||
|   {{form.non_field_errors}} | ||||
|   <ul class="form"> | ||||
|      <li> | ||||
|      Přihlašovací údaje | ||||
|      </li> | ||||
|      <li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.username %} | ||||
|      </li> | ||||
|      <li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.password %} | ||||
|      </li> | ||||
|      <li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.password_check %} | ||||
|      </li> | ||||
|      <li> | ||||
|      Osobní údaje | ||||
|      </li> | ||||
|      <li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.jmeno %} | ||||
|      </li> | ||||
|      <li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.prijmeni %} | ||||
|      </li> | ||||
|      <li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.pohlavi_muz%} | ||||
|      </li> | ||||
|      <li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.email %} | ||||
|      </li> | ||||
|      <li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.telefon %} | ||||
|      </li> | ||||
|      <li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.datum_narozeni %} | ||||
|      </li> | ||||
|      <li> | ||||
|        <hr> | ||||
|        Bydliště | ||||
|      </li> | ||||
|      <li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.ulice %} | ||||
|      </li> | ||||
|      <li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.mesto %} | ||||
|      </li> | ||||
|      <li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.psc %} | ||||
|      </li> | ||||
|      <li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.stat %} | ||||
|      </li> | ||||
|      <li id="id_li_stat_text"> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.stat_text %} | ||||
|      </li> | ||||
| 
 | ||||
|      <li> | ||||
|        <hr> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.skola %} | ||||
|      </li> | ||||
|      <li> | ||||
|        <button id="id_skola_text_button" type="button">Škola není v seznamu</button> | ||||
|      </li> | ||||
|      <li id="id_li_skola_nazev"> | ||||
|        Vyplň prosím celý název a adresu školy.<br>  | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.skola_nazev %} | ||||
|      </li> | ||||
|      <li id="id_li_skola_adresa"> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.skola_adresa %} | ||||
|      </li> | ||||
|      <li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.rok_maturity %} | ||||
|      </li> | ||||
|      <li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.zasilat %} | ||||
|      </li> | ||||
|      <li> | ||||
|        {% include "seminar/gdpr.html" %} | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.gdpr %} | ||||
|      </li> | ||||
|      <li> | ||||
|        {% include "seminar/prihlaska_field.html" with field=form.spam %} | ||||
|      </li> | ||||
| </ul> | ||||
|     <input type="submit" value="Odeslat"> | ||||
| </form> | ||||
| <script> | ||||
| $("#id_stat").on("change",addrCountryChanged); | ||||
| $("#id_skola_text_button").on("click",schoolNotInList); | ||||
| </script> | ||||
| 
 | ||||
| 
 | ||||
| {% endblock %} | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										4
									
								
								seminar/templates/seminar/prihlaska_field.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								seminar/templates/seminar/prihlaska_field.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | |||
|        <label class="field-label{% if field.field.required %} field-required{% endif %}" for="{{ field.id_for_label }}">{{ field.label }}:</label> | ||||
|        {{field}} | ||||
|        {% if field.help_text %}<span class="field-helptext">{{ field.help_text|safe }}</span>{% endif %} | ||||
|        {% if field.errors %}<span class="field-error">{{ field.errors }}</span>{% endif %} | ||||
							
								
								
									
										17
									
								
								seminar/templates/seminar/resitel.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								seminar/templates/seminar/resitel.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | |||
| {% extends "seminar/zadani/base.html" %} | ||||
| {% load staticfiles %} | ||||
| 
 | ||||
| 
 | ||||
| {% block content %} | ||||
| <h1> | ||||
|   {% block nadpis1a %}{% block nadpis1b %} | ||||
|     Stránka řešitele - {{ object.osoba.jmeno }} {{ object.osoba.prijmeni }} | ||||
|   {% endblock %}{% endblock %} | ||||
| </h1> | ||||
| 
 | ||||
| <a href="{% url 'logout' %}">Odhlásit se</a><br> | ||||
| <a href="{% url 'seminar_resitel_edit' %}">Upravit údaje</a><br> | ||||
| 
 | ||||
| 
 | ||||
| {% endblock %} | ||||
| 
 | ||||
							
								
								
									
										14
									
								
								seminar/templates/seminar/tematka/rozcestnik.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								seminar/templates/seminar/tematka/rozcestnik.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | |||
| {% for tematko in tematka %} | ||||
| <h1>{{tematko.nazev}}</h1> | ||||
| <p>{{tematko.abstrakt}}</p> | ||||
| 	<ul> | ||||
| 	{% for cislo in tematko.cisla %} | ||||
| 		<li><a href="/{{rocnik}}/t{{tematko.kod}}/#{{cislo.0.1}}">{{cislo.0.0}}</a></li> | ||||
| 		<ul> | ||||
| 		{% for odkaz in cislo.1 %} | ||||
| 			<li><a href="/{{rocnik}}/t{{tematko.kod}}/#{{odkaz.1}}">{{odkaz.0}}</a></li>	 | ||||
| 		{% endfor %} | ||||
| 		</ul> | ||||
| 	{% endfor %} | ||||
| 	</ul> | ||||
| {% endfor %} | ||||
							
								
								
									
										1
									
								
								seminar/templates/seminar/tematka/toaletak.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								seminar/templates/seminar/tematka/toaletak.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | |||
| Stránká témátka | ||||
|  | @ -1,11 +1,13 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| 
 | ||||
| import datetime | ||||
| from pytz import timezone | ||||
| import random | ||||
| import lorem | ||||
| import django.contrib.auth | ||||
| from django.db import transaction | ||||
| import unidecode | ||||
| import logging | ||||
| 
 | ||||
| from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni, Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Osoba, Organizator, Prijemce, Tema, Uloha, Konfera, KonferaNode, TextNode, UlohaVzorakNode, RocnikNode, CisloNode, TemaVCisleNode, Text, Hodnoceni, UlohaZadaniNode, Novinky | ||||
| 
 | ||||
|  | @ -15,7 +17,11 @@ from django.contrib.sites.models import Site | |||
| 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__) | ||||
| 
 | ||||
| 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'] | ||||
|  | @ -67,6 +73,8 @@ def gen_osoby(rnd, size): | |||
| 
 | ||||
| 
 | ||||
| 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) | ||||
|  | @ -90,22 +98,26 @@ def gen_skoly(): #TODO někdy to přepsat, aby jich bylo více | |||
| 	return skoly | ||||
| 
 | ||||
| def gen_resitele(rnd, osoby, skoly): | ||||
| 	logger.info('Generuji řešitele...') | ||||
| 
 | ||||
| 	resitele = [] | ||||
| 	for os in osoby: | ||||
| 		rand = rnd.randint(0, 8) | ||||
| 		if not (rand % 8 == 0): | ||||
| 			resitele.append(Resitel.objects.create(osoba=os, skola=rnd.choice(skoly), | ||||
| 				rok_maturity=rnd.randint(2019, 2029), | ||||
| 				zasilat=rnd.choice(Resitel.ZASILAT_CHOICES))) | ||||
| 				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, users): | ||||
| 	logger.info('Generuji organizátory...') | ||||
| 	organizatori = [] | ||||
| 
 | ||||
| 	 | ||||
|  | @ -118,9 +130,18 @@ def gen_organizatori(rnd, osoby, last_rocnik, users): | |||
| 		rand = rnd.randint(0, 8) | ||||
| 		if (rand % 8 == 0): | ||||
| 			pusobnost = rnd.randint(1, last_rocnik) | ||||
| 			od = datetime.date(1993 + pusobnost, rnd.randint(1, 12), rnd.randint(1, 28)) | ||||
| 			do = datetime.date(od.year + rnd.randint(1, 6), rnd.randint(1, 12), | ||||
| 				rnd.randint(1, 28)) | ||||
| 			od = datetime.datetime( | ||||
| 				year=1993 + pusobnost, | ||||
| 				month=rnd.randint(1, 12), | ||||
| 				day=rnd.randint(1, 28), | ||||
| 				tzinfo=timezone('CET'), | ||||
| 				) | ||||
| 			do = datetime.datetime( | ||||
| 				year=od.year + rnd.randint(1, 6), | ||||
| 				month=rnd.randint(1, 12), | ||||
| 				day=rnd.randint(1, 28), | ||||
| 				tzinfo=timezone('CET'), | ||||
| 				) | ||||
| 			#aktualni organizatori jeste nemaji vyplnene organizuje_do | ||||
| 
 | ||||
| 			#popis orga | ||||
|  | @ -136,6 +157,8 @@ def gen_organizatori(rnd, osoby, last_rocnik, users): | |||
| 	return organizatori | ||||
| 
 | ||||
| def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size): | ||||
| 	logger.info('Generuji úlohy do čísla (size={})...'.format(size)) | ||||
| 
 | ||||
| 	# ulohy resene v cisle | ||||
| 	jaka = ["Šachová", "Černá", "Větrná", "Dlouhá", "Křehká", "Rychlá", | ||||
| 		"Zákeřná", "Fyzikální"] | ||||
|  | @ -158,7 +181,14 @@ def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size) | |||
| 	for rocnik in rocniky: | ||||
| 		k+=1 | ||||
| 		cisla = rocnik_cisla[k-1] | ||||
| 		for ci in range(3, len(cisla)+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(round(resitele_size/8), round(3*resitele_size/4)) | ||||
|  			# dané číslo řeší něco mezi osminou a tříč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): | ||||
| 
 | ||||
| 				poc_op = rnd.randint(1, 4) # počet opravovatelů | ||||
|  | @ -199,6 +229,7 @@ def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size) | |||
| 				p.ulohazadaninode = uloha_zadani | ||||
| 				otec_syn(cisla[ci-2-1].cislonode, uloha_zadani) | ||||
| 
 | ||||
| 				# generování vzorového textu | ||||
| 				text_vzoraku = Text.objects.create( | ||||
| 					na_web = rnd.choice(reseni), | ||||
| 					do_cisla = rnd.choice(reseni) | ||||
|  | @ -211,17 +242,18 @@ def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size) | |||
| 				p.opravovatele.set(rnd.sample(organizatori,poc_op)) | ||||
| 				p.save() | ||||
| 
 | ||||
| 				# reseni ulohy | ||||
| 				# generování řešení | ||||
| 				poc_reseni = rnd.randint(size // 2, size * 2) | ||||
| 				#poc_resitel = rnd.randint(1, 3) <- k čemu je himbajs tahle proměnná? | ||||
| 				# vybereme nahodny vzorek resitelu o delce poctu reseni | ||||
| 				# (nebo skoro vsechny resitele, pokud jich je mene nez pocet reseni) | ||||
| 				# generujeme náhodný počet řešení | ||||
| 				for ri in range(poc_reseni): | ||||
| 					res_vyber = rnd.sample(resitele, rnd.randint(1, 5)) | ||||
| 					if rnd.randint(1, 10) == 6: | ||||
| 					# 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) | ||||
| 					res = Reseni.objects.create(forma=rnd.choice(Reseni.FORMA_CHOICES)[0]) | ||||
| 					# problem a resitele přiřadíme později, ManyToManyField | ||||
| 					# se nedá vyplnit v create() | ||||
| 					res = Reseni.objects.create(forma=rnd.choice(Reseni.FORMA_CHOICES)) | ||||
| 					#res.save() <- asi smazat | ||||
| 					res.resitele.set(res_vyber) | ||||
| 					res.save() | ||||
| 					hod = Hodnoceni.objects.create( | ||||
|  | @ -234,29 +266,33 @@ def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size) | |||
| 	return | ||||
| 
 | ||||
| def gen_soustredeni(rnd, resitele, organizatori): | ||||
| 		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 | ||||
| 	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): | ||||
|  | @ -268,6 +304,8 @@ def gen_rocniky(last_rocnik, size): | |||
| 	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, | ||||
|  | @ -292,6 +330,8 @@ def gen_konfery(size, rnd, organizatori, resitele, soustredeni): | |||
| 	return konfery | ||||
| 
 | ||||
| def gen_cisla(rnd, rocniky): | ||||
| 	logger.info('Generuji čísla...') | ||||
| 
 | ||||
| 	rocnik_cisla = [] | ||||
| 	for rocnik in rocniky: | ||||
| 		otec = True | ||||
|  | @ -317,7 +357,7 @@ def gen_cisla(rnd, rocniky): | |||
| 
 | ||||
| 			cislo = Cislo.objects.create( | ||||
| 				rocnik = rocnik, | ||||
| 				cislo = str(ci), | ||||
| 				poradi = str(ci),  | ||||
| 				datum_vydani=vydano, | ||||
| 				datum_deadline=deadline, | ||||
| 				verejne_db=True | ||||
|  | @ -335,6 +375,8 @@ def gen_cisla(rnd, rocniky): | |||
| 	return rocnik_cisla | ||||
| 
 | ||||
| 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í"] | ||||
|  | @ -361,8 +403,9 @@ def gen_temata(rnd, rocniky, rocnik_cisla, organizatori): | |||
| 				garant=rnd.choice(organizatori), | ||||
| 				kod=str(n), | ||||
| 				# atributy třídy Téma | ||||
| 				tema_typ=rnd.choice(Tema.TEMA_CHOICES), | ||||
| 				rocnik=rocnik | ||||
| 				tema_typ=rnd.choice(Tema.TEMA_CHOICES)[0], | ||||
| 				rocnik=rocnik, | ||||
| 				abstrakt = "Abstrakt tematka {}".format(n) | ||||
| 			) | ||||
| 			konec_tematu = min(rnd.randint(ci, 7), len(cisla)) | ||||
| 			for i in range(ci, konec_tematu+1): | ||||
|  | @ -378,6 +421,8 @@ def gen_temata(rnd, rocniky, rocnik_cisla, organizatori): | |||
| 
 | ||||
| 
 | ||||
| def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori): | ||||
| 	logger.info('Generuji úlohy k tématům...') | ||||
| 
 | ||||
| 	# ulohy resene v cisle | ||||
| 	jaka = ["Šachová", "Černá", "Větrná", "Dlouhá", "Křehká", "Rychlá", | ||||
| 		"Zákeřná", "Fyzikální"] | ||||
|  | @ -474,6 +519,8 @@ def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori) | |||
| 	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í"] | ||||
|  | @ -495,6 +542,8 @@ def otec_syn(otec, syn): | |||
| 
 | ||||
| @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) | ||||
|  | @ -575,5 +624,5 @@ def create_test_data(size = 6, rnd = None): | |||
| 
 | ||||
| 
 | ||||
| 	# obecné nastavení semináře, musí být už přidané ročníky a čísla, jinak se nastaví divně | ||||
| 	nastaveni = Nastaveni.objects.create(aktualni_rocnik = Rocnik.objects.last(), | ||||
| 	nastaveni = Nastaveni.objects.create( | ||||
| 			aktualni_cislo = Cislo.objects.all()[1]) | ||||
|  |  | |||
|  | @ -3,10 +3,14 @@ from django.contrib.auth.decorators import user_passes_test | |||
| from . import views, export | ||||
| from .utils import staff_member_required | ||||
| from django.views.generic.base import RedirectView | ||||
| from django.contrib.auth import views as auth_views | ||||
| 
 | ||||
| staff_member_required = user_passes_test(lambda u: u.is_staff) | ||||
| 
 | ||||
| urlpatterns = [ | ||||
|   path('aktualni/temata/', views.TemataRozcestnikView), | ||||
| 	path('<int:rocnik>/t<int:tematko>/', views.TematkoView), | ||||
| 
 | ||||
| 	# REDIRECTy | ||||
| 	path('jak-resit/', RedirectView.as_view(url='/co-je-MaM/jak-resit/')), | ||||
| 
 | ||||
|  | @ -85,19 +89,36 @@ urlpatterns = [ | |||
| 	path('stav', | ||||
| 		staff_member_required(views.StavDatabazeView), name='stav_databaze'), | ||||
| 	path('cislo/<int:rocnik>.<int:cislo>/obalkovani', | ||||
| 		staff_member_required(views.obalkovaniView), name='seminar_cislo_resitel_obalkovani'), | ||||
| 		staff_member_required(views.ObalkovaniView.as_view()), name='seminar_cislo_resitel_obalkovani'), | ||||
| 	path('cislo/<int:rocnik>.<int:cislo>/tex-download.json', | ||||
| 		staff_member_required(views.texDownloadView), name='seminar_tex_download'), | ||||
| 	path('soustredeni/<int:soustredeni>/obalky.pdf', | ||||
| 		staff_member_required(views.soustredeniObalkyView), name='seminar_soustredeni_obalky'), | ||||
| 
 | ||||
| 	path('tex-upload/login/', views.LoginView, name='seminar_login'), | ||||
| 	path('tex-upload/login/', views.TeXUploadLoginView, name='seminar_login'), | ||||
| 	path( | ||||
| 		'tex-upload/', | ||||
| 		staff_member_required(views.texUploadView), | ||||
| 		name='seminar_tex_upload' | ||||
| 	), | ||||
| 	path('prihlaska/',views.get_name), | ||||
| 	path('org/vloz_body/<int:tema>/', | ||||
| 		staff_member_required(views.VlozBodyView.as_view()),name='seminar_org_vlozbody'), | ||||
| 	path('auth/prihlaska/',views.prihlaskaView, name='seminar_prihlaska'), | ||||
| 	path('auth/login/', views.LoginView.as_view(), name='login'), | ||||
| 	path('auth/logout/', views.LogoutView.as_view(), name='logout'), | ||||
| 	path('auth/resitel/', views.ResitelView.as_view(), name='seminar_resitel'), | ||||
| 	path('autocomplete/skola/',views.SkolaAutocomplete.as_view(), name='autocomplete_skola'), | ||||
| 	path('autocomplete/resitel/',views.ResitelAutocomplete.as_view(), name='autocomplete_resitel'), | ||||
| 	path('auth/reset_password/', views.PasswordResetView.as_view(), name='reset_password'), | ||||
| 	path('auth/change_password/', views.PasswordChangeView.as_view(), name='change_password'), | ||||
| 	path('auth/reset_password_done/', views.PasswordResetDoneView.as_view(), name='reset_password_done'), | ||||
| 	path('auth/reset_password_confirm/<uidb64>/<token>/', views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'), | ||||
| 	path('auth/reset_password_complete/', views.PasswordResetCompleteView.as_view(), name='reset_password_complete'), | ||||
| 	path('auth/resitel_edit', views.resitelEditView, name='seminar_resitel_edit'), | ||||
| 
 | ||||
| 
 | ||||
| 	path('temp/add_solution', views.AddSolutionView.as_view(),name='seminar_vloz_reseni'), | ||||
| 
 | ||||
| 	path('', views.TitulniStranaView.as_view(), name='titulni_strana'), | ||||
| 
 | ||||
| 	# Ceka na autocomplete v3 | ||||
|  |  | |||
|  | @ -2,9 +2,18 @@ | |||
| 
 | ||||
| import datetime | ||||
| from django.contrib.auth.decorators import user_passes_test | ||||
| from html.parser import HTMLParser  | ||||
| 
 | ||||
| staff_member_required = user_passes_test(lambda u: u.is_staff) | ||||
| 
 | ||||
| class FirstTagParser(HTMLParser): | ||||
| 	def __init__(self, *args, **kwargs): | ||||
| 		self.firstTag = None | ||||
| 		super().__init__(*args, **kwargs) | ||||
| 	def handle_data(self, data): | ||||
| 		if self.firstTag == None: | ||||
| 			self.firstTag = data | ||||
| 	 | ||||
| def histogram(seznam): | ||||
| 	d = {} | ||||
| 	for i in seznam: | ||||
|  |  | |||
							
								
								
									
										652
									
								
								seminar/views.py
									
									
									
									
									
								
							
							
						
						
									
										652
									
								
								seminar/views.py
									
									
									
									
									
								
							|  | @ -2,22 +2,31 @@ | |||
| 
 | ||||
| from django.shortcuts import get_object_or_404, render | ||||
| from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, JsonResponse | ||||
| from django.urls import reverse | ||||
| from django.urls import reverse,reverse_lazy | ||||
| from django.core.exceptions import PermissionDenied, ObjectDoesNotExist | ||||
| from django.views import generic | ||||
| from django.utils.translation import ugettext as _ | ||||
| from django.http import Http404,HttpResponseBadRequest,HttpResponseRedirect | ||||
| from django.db.models import Q | ||||
| from django.db.models import Q, Sum, Count | ||||
| from django.views.decorators.csrf import ensure_csrf_cookie | ||||
| from django.contrib.auth import authenticate, login | ||||
| from django.views.generic.edit import FormView | ||||
| from django.contrib.auth import authenticate, login, get_user_model, logout | ||||
| from django.contrib.auth import views as auth_views | ||||
| from django.contrib.auth.models import User | ||||
| from django.contrib.auth.mixins import LoginRequiredMixin | ||||
| from django.db import transaction | ||||
| from dal import autocomplete | ||||
| 
 | ||||
| from .models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni, Organizator, Resitel, Novinky, Soustredeni_Ucastnici, Pohadka, Tema, Clanek | ||||
| import seminar.models as s | ||||
| from .models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni, Organizator, Resitel, Novinky, Soustredeni_Ucastnici, Pohadka, Tema, Clanek, Osoba, Skola # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci | ||||
| #from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva | ||||
| from . import utils | ||||
| from .unicodecsv import UnicodeWriter | ||||
| from .forms import NameForm | ||||
| from .forms import PrihlaskaForm, LoginForm, ProfileEditForm | ||||
| import seminar.forms as f | ||||
| 
 | ||||
| from datetime import timedelta, date, datetime | ||||
| from django.utils import timezone | ||||
| from itertools import groupby | ||||
| import tempfile | ||||
| import subprocess | ||||
|  | @ -30,6 +39,7 @@ import json | |||
| import traceback | ||||
| import sys | ||||
| import csv | ||||
| import logging | ||||
| 
 | ||||
| 
 | ||||
| def verejna_temata(rocnik): | ||||
|  | @ -37,6 +47,45 @@ def verejna_temata(rocnik): | |||
| 	""" | ||||
| 	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) | ||||
| 
 | ||||
| def get_problemy_k_tematu(tema): | ||||
| 	return Problemy.objects.filter(nadproblem = tema) | ||||
| 
 | ||||
| 
 | ||||
| class VlozBodyView(generic.ListView): | ||||
| 	template_name = 'seminar/org/vloz_body.html' | ||||
| 
 | ||||
| 	def get_queryset(self): | ||||
| 		self.tema = get_object_or_404(Problem,id=self.kwargs['tema']) | ||||
| 		print(self.tema) | ||||
| 		self.problemy = Problem.objects.filter(nadproblem = self.tema) | ||||
| 		print(self.problemy) | ||||
| 		self.reseni = Reseni.objects.filter(problem__in=self.problemy)	 | ||||
| 		print(self.reseni) | ||||
| 		return self.reseni | ||||
| 
 | ||||
| 
 | ||||
| class ObalkovaniView(generic.ListView): | ||||
| 	template_name = 'seminar/org/obalkovani.html' | ||||
| 
 | ||||
| 	def get_queryset(self): | ||||
| 		rocnik = get_object_or_404(Rocnik,rocnik=self.kwargs['rocnik']) | ||||
| 		cislo = get_object_or_404(Cislo,rocnik=rocnik,poradi=self.kwargs['cislo']) | ||||
| 		self.cislo = cislo | ||||
| 		self.hodnoceni = s.Hodnoceni.objects.filter(cislo_body=cislo) | ||||
| 		self.reseni = Reseni.objects.filter(hodnoceni__in = self.hodnoceni).annotate(Sum('hodnoceni__body')).annotate(Count('hodnoceni')).order_by('resitele__osoba') | ||||
| 		return self.reseni | ||||
| 
 | ||||
| 	def get_context_data(self, **kwargs): | ||||
| 		context = super(ObalkovaniView, self).get_context_data(**kwargs) | ||||
| 		print(self.cislo) | ||||
| 		context['cislo'] = self.cislo  | ||||
| 		return context | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| def AktualniZadaniView(request): | ||||
| 	nastaveni = get_object_or_404(Nastaveni) | ||||
|  | @ -67,6 +116,99 @@ def ZadaniTemataView(request): | |||
| 		} | ||||
| 	) | ||||
| 
 | ||||
| # TODO Napsat tuto funkci znovu rekurzivně podle Jethrorad. Potom se podívat, jak lehce se dá modifikovat pro Rozcestník. Pokud lehce, rozšířit ji. Pokud složitě - použít tuhle | ||||
| def vytahniZLesaSeznam(tematko, koren, pouze_zajimave=False): | ||||
| 	returnVal = [] | ||||
| 
 | ||||
| 	stack = [] | ||||
| 	stack.append((koren.first_child, 0, False)) #Tuple of node, depth and relevance | ||||
| 
 | ||||
| 	while len(stack) > 0: | ||||
| 		wn, wd, wr = stack.pop() | ||||
| 
 | ||||
| 		if wn.succ != None: | ||||
| 			stack.append((wn.succ, wd, wr)) | ||||
| 		if isinstance(wn, s.TemaVCisleNode): | ||||
| 			print("TEMA") | ||||
| 			print(wn.tema.id)	 | ||||
| 			print(tematko.id)	 | ||||
| 			if wn.tema.id == tematko.id: | ||||
| 				returnVal.append((posledni_cislo, 0)) | ||||
| 				print("PRIDANO") | ||||
| 				wr = True | ||||
| 				wd = 1 | ||||
| 
 | ||||
| 		if wn.srolovatelne: | ||||
| 			tagOpen = s.Text(na_web = "Otevírací srolovací tag") | ||||
| 			tagOpenNode = s.TextNode(text = tagOpen) | ||||
| 			tagClose = s.Text(na_web = "Zavírací srolovací tag") | ||||
| 			tagCloseNode = s.TextNode(text = tagClose) | ||||
| 			stack.append((tagCloseNode, wd, True)) | ||||
| 			 | ||||
| 		if wn.first_child != None: | ||||
| 			stack.append((wn.first_child, wd + 1, wr)) | ||||
| 
 | ||||
| 		if isinstance(wn, s.CisloNode): | ||||
| 			posledni_cislo = wn | ||||
| 		print(wn) | ||||
| 
 | ||||
| 		if wr:						 | ||||
| 			print("ZAJIMAVE") | ||||
| 			if pouze_zajimave: | ||||
| 				if not wn.zajimave: | ||||
| 					continue | ||||
| 			returnVal.append((wn, wd)) | ||||
| 	return returnVal | ||||
|    | ||||
| def TematkoView(request, rocnik, tematko): | ||||
| 	nastaveni = s.Nastaveni.objects.first() | ||||
| 	rocnik_object = s.Rocnik.objects.filter(rocnik=rocnik) | ||||
| 	tematko_object = s.Tema.objects.filter(rocnik=rocnik_object[0], kod=tematko) | ||||
| 	seznam = vytahniZLesaSeznam(tematko_object[0], nastaveni.aktualni_rocnik().rocniknode) | ||||
| 	for node, depth in seznam: | ||||
| 		if node.isinstance(node, s.KonferaNode): | ||||
| 			raise Exception("Not implemented yet") | ||||
| 		if node.isinstance(node, s.PohadkaNode): # Mohu ignorovat, má pod sebou | ||||
| 			pass | ||||
| 
 | ||||
| 	return render(request, 'seminar/tematka/toaletak.html', {}) | ||||
| 	 | ||||
| 
 | ||||
| def TemataRozcestnikView(request): | ||||
| 	print("=============================================") | ||||
| 	nastaveni = s.Nastaveni.objects.first() | ||||
| 	tematka_objects = s.Tema.objects.filter(rocnik=nastaveni.aktualni_rocnik()) | ||||
| 	tematka = [] #List tematka obsahuje pro kazde tematko object a list vsech TemaVCisleNodu - implementované pomocí slovníku | ||||
| 	for tematko_object in tematka_objects: | ||||
| 		print("AKTUALNI TEMATKO") | ||||
| 		print(tematko_object.id) | ||||
| 		odkazy = vytahniZLesaSeznam(tematko_object, nastaveni.aktualni_rocnik().rocniknode, pouze_zajimave = True) #Odkazy jsou tuply (node, depth) v listu | ||||
| 		print(odkazy) | ||||
| 		cisla = [] # List tuplů (nazev cisla, list odkazů) | ||||
| 		vcisle = [] | ||||
| 		cislo = None | ||||
| 		for odkaz	in odkazy: | ||||
| 			if odkaz[1] == 0: | ||||
| 				if cislo != None: | ||||
| 					cisla.append((cislo, vcisle)) | ||||
| 				cislo = (odkaz[0].getOdkazStr(), odkaz[0].getOdkaz()) | ||||
| 				vcisle = [] | ||||
| 			else: | ||||
| 				print(odkaz[0].getOdkaz()) | ||||
| 				vcisle.append((odkaz[0].getOdkazStr(), odkaz[0].getOdkaz())) | ||||
| 		if cislo != None: | ||||
| 			cisla.append((cislo, vcisle)) | ||||
| 				 | ||||
| 		print(cisla) | ||||
| 		tematka.append({ | ||||
| 			"kod" : tematko_object.kod, | ||||
| 			"nazev" : tematko_object.nazev, | ||||
| 			"abstrakt" : tematko_object.abstrakt,  | ||||
| 			"obrazek": tematko_object.obrazek, | ||||
| 			"cisla" : cisla | ||||
| 		}) | ||||
| 	return render(request, 'seminar/tematka/rozcestnik.html', {"tematka": tematka, "rocnik" : nastaveni.aktualni_rocnik().rocnik}) | ||||
|      | ||||
| 
 | ||||
| #def ZadaniAktualniVysledkovkaView(request): | ||||
| #	nastaveni = get_object_or_404(Nastaveni) | ||||
|  | @ -143,7 +285,7 @@ class StareNovinkyView(generic.ListView): | |||
| 
 | ||||
| 
 | ||||
| # Organizatori | ||||
| def aktivniOrganizatori(datum=date.today()): | ||||
| def aktivniOrganizatori(datum=timezone.now()): | ||||
| 	return Organizator.objects.exclude( | ||||
| 		organizuje_do__isnull=False, | ||||
| 		organizuje_do__lt=datum | ||||
|  | @ -252,26 +394,115 @@ class ArchivView(generic.ListView): | |||
| 		context["nahledy"] = "\n".join(tags) | ||||
| 		return context | ||||
| 
 | ||||
| ### Výsledky | ||||
| 
 | ||||
| def sloupec_s_poradim(vysledky): | ||||
| 	# počet řešitelů ve výsledkovce nad aktuálním | ||||
| 	lepsich_resitelu = 0 | ||||
| # ze setřízeného(!) seznamu všech bodů vytvoří seznam s pořadími (včetně 3.-5. a pak 2 volná místa atp.) | ||||
| def sloupec_s_poradim(seznam_s_body): | ||||
| 	aktualni_poradi = 1 | ||||
| 	sloupec_s_poradim = [] | ||||
| 
 | ||||
| 	poradi_l = [] | ||||
| 	# projdeme skupiny řešitelů se stejným počtem bodů | ||||
| 	for skupina in (list(x) for _, x in groupby(vysledky, lambda x: x.body)): | ||||
| 
 | ||||
| 		# připravíme si obsahy buněk ve sloupci pořadí pro skupinu | ||||
| 		if len(skupina) == 1: | ||||
| 			poradi_l += ["{}.".format(lepsich_resitelu + 1)] | ||||
| 		# je-li účastníků se stejným počtem bodů víc, pořadí (rozsah X.-Y.) je jen u prvního | ||||
| 	# seskupíme seznam všech bodů podle hodnot | ||||
| 	for index in range(0, len(seznam_s_body)): | ||||
| 		# pokud je pořadí větší než číslo řádku, tak jsme vypsali větší rozsah a chceme | ||||
| 		# vypsat už jen prázdné místo, než dojdeme na správný řádek | ||||
| 		if (index + 1) < aktualni_poradi: | ||||
| 			sloupec_s_poradim.append("") | ||||
| 			continue | ||||
| 		velikost_skupiny = 0 | ||||
| 		# zjistíme počet po sobě jdoucích stejných hodnot | ||||
| 		while seznam_s_body[index] == seznam_s_body[index + velikost_skupiny]: | ||||
| 			velikost_skupiny = velikost_skupiny + 1 | ||||
| 			# na konci musíme ošetřit přetečení seznamu | ||||
| 			if (index + velikost_skupiny) > len(seznam_s_body) - 1: | ||||
| 				break | ||||
| 		# pokud je velikost skupiny 1, vypíšu pořadí | ||||
| 		if velikost_skupiny == 1: | ||||
| 			sloupec_s_poradim.append("{}.".format(aktualni_poradi)) | ||||
| 		# pokud je skupina větší, vypíšu rozsah | ||||
| 		else: | ||||
| 			poradi_l += [u"{}.–{}.".format(lepsich_resitelu + 1, lepsich_resitelu + len(skupina))] + [""] * (len(skupina)-1) | ||||
| 		lepsich_resitelu += len(skupina) | ||||
| 	#pomlcka je opravdu pomlcka v unicode!!dulezite pro vysledkovku v TeXu | ||||
| 			sloupec_s_poradim.append("{}.–{}.".format(aktualni_poradi,  | ||||
| 						aktualni_poradi+velikost_skupiny-1)) | ||||
| 		# zvětšíme aktuální pořadí o tolik, kolik pozic bylo přeskočeno | ||||
| 		aktualni_poradi = aktualni_poradi + velikost_skupiny | ||||
| 	return sloupec_s_poradim | ||||
| 
 | ||||
| 	return poradi_l | ||||
| # spočítá součet bodů získaných daným řešitelem za zadaný problém a všechny jeho podproblémy | ||||
| def __soucet_resitele_problemu(problem, resitel, cislo, soucet): | ||||
| 	# sečteme body za daný problém přes všechna řešení daného problému  | ||||
| 	# od daného řešitele | ||||
| 	reseni_resitele = problem.hodnoceni_set.filter(reseni__resitele=resitel,  | ||||
| 				cislo_body=cislo) | ||||
| 	# XXX chyba na řádku výše - řešení může mít více řešitelů, asi chceme contains | ||||
| 	# nebo in | ||||
| 	for r in reseni_resitele: | ||||
| 		soucet += r.body | ||||
| 
 | ||||
| 	# a přičteme k tomu hodnocení všech podproblémů | ||||
| 	for p in problem.podproblem.all():  | ||||
| 	# i přes jméno by to měla být množina jeho podproblémů | ||||
| 		soucet += __soucet_resitele_problemu(p, resitel, soucet) | ||||
| 	return soucet | ||||
| 
 | ||||
| # spočítá součet všech bodů ze všech podproblémů daného problému daného řešitele | ||||
| def body_resitele_problemu_v_cisle(problem, resitel, cislo): | ||||
| 	# probably FIXED: nezohledňuje číslo, do kterého se body počítají | ||||
| 	return __soucet_resitele_problemu(problem, resitel, cislo, 0) | ||||
| 
 | ||||
| # vrátí list všech problémů s body v daném čísle, které již nemají nadproblém | ||||
| def hlavni_problemy_cisla(cislo): | ||||
| 	hodnoceni = cislo.hodnoceni.select_related('problem', 'reseni').all()	# hodnocení, která se vážou k danému číslu | ||||
| 	 | ||||
| 	reseni = [h.reseni for h in hodnoceni] | ||||
| 	problemy = [h.problem for h in hodnoceni] | ||||
| 	problemy_set = set(problemy)	# chceme každý problém unikátně, | ||||
| 	problemy = (list(problemy_set)) # převedení na množinu a zpět to zaručí | ||||
| 
 | ||||
| 	# hlavní problémy čísla  | ||||
| 	# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém) | ||||
| 	hlavni_problemy = [] | ||||
| 	for p in problemy: | ||||
| 		while not(p.nadproblem == None): | ||||
| 			p = p.nadproblem | ||||
| 		hlavni_problemy.append(p) | ||||
| 		 | ||||
| 	# zunikátnění | ||||
| 	hlavni_problemy_set = set(hlavni_problemy) | ||||
| 	hlavni_problemy = list(hlavni_problemy_set) | ||||
| 	hlavni_problemy.sort(key=lambda k: k.kod_v_rocniku()) # setřídit podle t1, t2, c3, ... | ||||
| 	 | ||||
| 	return hlavni_problemy | ||||
| 
 | ||||
| def body_resitele_odjakziva(resitel): | ||||
| 	body = 0 | ||||
| 	resitelova_hodnoceni = Hodnoceni.objects.select_related('body').all().filter(reseni_resitele=resitel) | ||||
| 	# TODO: v radku nahore chceme _in nebo _contains | ||||
| 	for hodnoceni in resitelova_hodnoceni: | ||||
| 		body = body + hodnoceni.body | ||||
| 	return body | ||||
| 
 | ||||
| # spočítá součet všech bodů řešitele za dané číslo | ||||
| def body_resitele_v_cisle(resitel, cislo): | ||||
| 	hlavni_problemy = hlavni_problemy_cisla(cislo) | ||||
| 	body_resitele = 0 | ||||
| 	for h in hlavni_problemy: | ||||
| 		body_resitele = body_resitele + body_resitele_problemu_v_cisle(h, resitel, cislo) | ||||
| 	# TODO: je rozdíl mezi odevzdanou úlohou za 0 a tím, když řešitel nic neodevzdal | ||||
| 	# řešit přes kontrolu velikosti množiny řešení daného problému do daného čísla? | ||||
| 	# Tady to ale nevadí, tady se počítá součet za číslo. | ||||
| 	return body_resitele | ||||
| 
 | ||||
| # spočítá součet všech bodů řešitele za daný rok (nebo jen do daného čísla včetně) | ||||
| def body_resitele_v_rocniku(resitel, rocnik, do_cisla=None): | ||||
| 	# pokud do_cisla=None, tak do posledního čísla v ročníku | ||||
| 	# do_cisla je objekt Cislo | ||||
| 	cisla = rocnik.cisla.all() # funkce vrátí pole objektů  | ||||
| 	# Cislo už lexikograficky setřízené, viz models | ||||
| 	body = 0 | ||||
| 	for cislo in cisla: | ||||
| 		if cislo.poradi == do_cisla.poradi: break | ||||
| 		# druhá část zaručuje, že máme výsledky do daného čísla včetně | ||||
| 		body = body + body_resitele_v_cisle(resitel, cislo) | ||||
| 	return body | ||||
| 
 | ||||
| #def vysledkovka_rocniku(rocnik, jen_verejne=True): | ||||
| #	"""Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve | ||||
|  | @ -288,7 +519,7 @@ def sloupec_s_poradim(vysledky): | |||
| #		return None | ||||
| # | ||||
| #	#vybere vsechny vysledky z posledniho (verejneho) cisla a setridi sestupne dle bodu | ||||
| #	vysledky = list(cisla_v_rocniku.filter(cislo = cisla_v_rocniku[0].cislo).order_by('-body', 'resitel__prijmeni', 'resitel__jmeno').select_related('resitel')) | ||||
| #	vysledky = list(cisla_v_rocniku.filter(cislo = cisla_v_rocniku[0].poradi).order_by('-body', 'resitel__prijmeni', 'resitel__jmeno').select_related('resitel')) | ||||
| # | ||||
| #	class Vysledkovka: | ||||
| #		def __init__(self): | ||||
|  | @ -304,7 +535,7 @@ def sloupec_s_poradim(vysledky): | |||
| #		v.poradi = poradi | ||||
| #		v.resitel.rocnik = v.resitel.rocnik(rocnik) | ||||
| # | ||||
| #		verejne_vysl_odjakziva = VysledkyKCisluOdjakziva.objects.filter(cislo__rocnik=rocnik, cislo=cisla_v_rocniku[0].cislo) | ||||
| #		verejne_vysl_odjakziva = VysledkyKCisluOdjakziva.objects.filter(cislo__rocnik=rocnik, cislo=cisla_v_rocniku[0].poradi) | ||||
| #		if jen_verejne: | ||||
| #			verejne_vysl_odjakziva = verejne_vysl_odjakziva.filter(cislo__verejna_vysledkovka=True) | ||||
| # | ||||
|  | @ -380,16 +611,17 @@ class ProblemView(generic.DetailView): | |||
| 
 | ||||
| class VysledkyResitele(object): | ||||
| 	"""Pro daného řešitele ukládá počet bodů za jednotlivé úlohy a celkový | ||||
| 	počet bodů za číslo.""" | ||||
| 	počet bodů za konkrétní ročník do daného čísla a za dané číslo.""" | ||||
| 
 | ||||
| 	def __init__(self, jmeno, prijmeni): | ||||
| 		resitel_jmeno = jmeno | ||||
| 		resitel_prijmeni = prijmeni | ||||
| 		body = {} | ||||
| 		body_cislo = 0 | ||||
| 
 | ||||
| 	def body_za_cislo(self): | ||||
| 		return sum(body.values()) | ||||
| 	def __init__(self, resitel, cislo, rocnik): | ||||
| 		self.resitel = resitel | ||||
| 		self.cislo = cislo | ||||
| 		self.body_cislo = body_resitele_v_cisle(resitel, cislo) | ||||
| 		self.body = [] | ||||
| 		self.rocnik = rocnik | ||||
| 		self.body_rocnik = body_resitele_v_rocniku(resitel, rocnik, cislo) | ||||
| 		self.body_celkem_odjakziva = resitel.vsechny_body() | ||||
| 		self.poradi = 0 | ||||
| 
 | ||||
| class CisloView(generic.DetailView): | ||||
| 	model = Cislo | ||||
|  | @ -400,8 +632,8 @@ class CisloView(generic.DetailView): | |||
| 		if queryset is None: | ||||
| 			queryset = self.get_queryset() | ||||
| 		rocnik_arg = self.kwargs.get('rocnik') | ||||
| 		cislo_arg = self.kwargs.get('cislo') | ||||
| 		queryset = queryset.filter(rocnik__rocnik=rocnik_arg, cislo=cislo_arg) | ||||
| 		poradi_arg = self.kwargs.get('cislo') | ||||
| 		queryset = queryset.filter(rocnik__rocnik=rocnik_arg, poradi=poradi_arg) | ||||
| 
 | ||||
| 		try: | ||||
| 			obj = queryset.get() | ||||
|  | @ -410,88 +642,53 @@ class CisloView(generic.DetailView): | |||
| 						{'verbose_name': queryset.model._meta.verbose_name}) | ||||
| 		return obj | ||||
| 
 | ||||
| 	# spočítá součet bodů získaných daným řešitelem za zadaný problém a všechny jeho podproblémy | ||||
| 	def __soucet_resitele_problemu(problem, resitel, soucet): | ||||
| 		# FIXME: správně je nadproblem_(typ problemu), ale to by bylo potřeba nějak  | ||||
| 		# zjistit, jaký typ nodu to vlastně je a aplikovat to ve volání funkce | ||||
| 	 | ||||
| 		# sečteme body za daný problém přes všechna řešení daného problému  | ||||
| 		# od daného řešitele | ||||
| 		reseni_resitele = problem.hodnoceni_set.filter(reseni_resitele__contains=resitel) | ||||
| 		for r in reseni_resitele: | ||||
| 			soucet += r.body | ||||
| 
 | ||||
| 		for p in problem.nadproblem_set:  | ||||
| 		# i přes jméno by to měla být množina jeho podproblémů | ||||
| 			soucet += __soucet_resitele_problemu(p, resitel, soucet) | ||||
| 		return soucet | ||||
| 
 | ||||
| 
 | ||||
| 	def vysledky_resitele_problemu(problem, resitel, cislo): | ||||
| 		return __soucet_resitele_problemu(problem, resitel, 0)	 | ||||
| 		 | ||||
| 
 | ||||
| 	def get_context_data(self, **kwargs): | ||||
| 		context = super(CisloView, self).get_context_data(**kwargs) | ||||
| 
 | ||||
| 		## TODO upravit dle nového modelu | ||||
| 		cislo = context['cislo'] | ||||
| 		hodnoceni = cislo.hodnoceni_set	# hodnocení, která se vážou k danému číslu | ||||
| 		 | ||||
| 		reseni = [h.reseni for h in hodnoceni] | ||||
| 		problemy = [h.problem for h in hodnoceni] | ||||
| 		problemy_set = set(problemy)	# chceme každý problém unikátně, | ||||
| 		problemy = (list(problemy_set)) # převedení na množinu a zpět to zaručí | ||||
| 
 | ||||
| 		# hlavní problémy čísla  | ||||
| 		# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém) | ||||
| 		hlavni_problemy = [] | ||||
| 		for p in problemy: | ||||
| 			while not(p.nadproblem == None): | ||||
| 				p = nadproblem | ||||
| 			hlavni_problemy.append(p) | ||||
| 
 | ||||
| 		# zunikátnění | ||||
| 		hlavni_problemy_set = set(hlavni_problemy) | ||||
| 		hlavni_problemy = list(hlavni_problemy_set) | ||||
| 		hlavni_problemy = hlavni_problemy_cisla(cislo) | ||||
| 
 | ||||
| 		## TODO dostat pro tyto problémy součet v daném čísle pro daného řešitele | ||||
| 		## TODO možná chytřeji vybírat aktivní řešitele | ||||
| 		## chceme letos něco poslal | ||||
| 		aktivni_resitele = Resitel.objects.filter( | ||||
| 				rok_maturity__gte=context['rocnik'].druhy_rok()) | ||||
| 				rok_maturity__gte=cislo.rocnik.druhy_rok()) | ||||
| 				# TODO: zkusit hodnoceni__rocnik... | ||||
| 				#.filter(hodnoceni_set__rocnik__eq=cislo_rocnik) | ||||
| 		radky_vysledkovky = [] | ||||
| 		for ar in aktivni_resitele: | ||||
| 			vr = VysledkyResitele(ar.jmeno, ar.prijmeni) | ||||
| 			for h in hlavni_problemy:  | ||||
| 				body = vysledky_resitele_problemu(h, ar, cislo) | ||||
| 				vr.body[h.kod_v_rocniku] = body | ||||
| 				vr.body_cislo = vr.body_cislo + body  | ||||
| 			# získáme výsledky řešitele - součty přes číslo a ročník | ||||
| 			vr = VysledkyResitele(ar, cislo, cislo.rocnik) | ||||
| 			for hp in hlavni_problemy: | ||||
| 				vr.body.append( | ||||
| 				body_resitele_problemu_v_cisle(hp, ar, cislo)) | ||||
| 			radky_vysledkovky.append(vr) | ||||
| 
 | ||||
| 		## TODO: spočítat počet bodů řešitele v daném ročníku a seřadit je podle toho | ||||
| 		## TODO: možná použít tyto funkce i v RocnikVysledkovkaView (a umístit sem nebo tam)? | ||||
| 		# setřídíme řádky výsledkovky/objekty VysledkyResitele podle bodů | ||||
| 		radky_vysledkovky.sort(key=lambda vr: vr.body_rocnik, reverse=True) | ||||
| 
 | ||||
| 		# generujeme sloupec s pořadím pomocí stejně zvané funkce | ||||
| 		pocty_bodu = [rv.body_rocnik for rv in radky_vysledkovky] | ||||
| 		sloupec_poradi = sloupec_s_poradim(pocty_bodu) | ||||
| 		 | ||||
| #		vysledky = VysledkyKCisluZaRocnik.objects.filter(cislo = context['cislo']).\ | ||||
| #			order_by('-body', 'resitel__prijmeni', 'resitel__jmeno') | ||||
| #		reseni = Reseni.objects.filter(cislo_body = context['cislo']).select_related("resitel") | ||||
| 		# každému řádku výsledkovky přidáme jeho pořadí | ||||
| 		i = 0 | ||||
| 		for rv in radky_vysledkovky: | ||||
| 			rv.poradi = sloupec_poradi[i] | ||||
| 			i = i + 1 | ||||
| 
 | ||||
| 		# typy úloh, které se mají zobrazovat u čísla, tj. těch, které byly  | ||||
| 		# v čísle skutečně zadány | ||||
| #		typy_skutecne_zadanych = [Problem.TYP_ULOHA, Problem.TYP_SERIAL, Problem.TYP_ORG_CLANEK] | ||||
| #		v_cisle_zadane = Problem.objects.filter(cislo_zadani=context['cislo']).filter(typ__in=typy_skutecne_zadanych).order_by('kod') | ||||
| 		# vytahané informace předáváme do kontextu | ||||
| 		context['cislo'] = cislo | ||||
| 		context['radky_vysledkovky'] = radky_vysledkovky	 | ||||
| 		context['problemy'] = hlavni_problemy | ||||
| #		context['v_cisle_zadane'] = TODO | ||||
| #		context['resene_problemy'] = resene_problemy | ||||
| 		#XXX testovat | ||||
| 		#XXX opravit to, že se nezobrazují body za jednotlivé úlohy | ||||
| 
 | ||||
| 		return context | ||||
| 
 | ||||
| #		resene_problemy = Problem.objects.filter(cislo_reseni=context['cislo']).filter(typ__in=typy_skutecne_zadanych).order_by('cislo_zadani__cislo', 'kod') | ||||
| # | ||||
| #		poradi_typu = { | ||||
| #			Problem.TYP_ULOHA: 1, | ||||
| #			Problem.TYP_SERIAL: 2, | ||||
| #			Problem.TYP_ORG_CLANEK: 3, | ||||
| #			Problem.TYP_TEMA: 4, | ||||
| #			Problem.TYP_RES_CLANEK: 5 | ||||
| #		} | ||||
| #		problemy = sorted(set(r.problem for r in reseni), key=lambda x:(poradi_typu[x.typ], x.kod_v_rocniku())) | ||||
| #		#setridi problemy podle typu a poradi zadani | ||||
| #		problem_index = {} | ||||
|  | @ -529,7 +726,6 @@ class CisloView(generic.DetailView): | |||
| #		context['problemy'] = problemy | ||||
| #		context['v_cisle_zadane'] = v_cisle_zadane | ||||
| #		context['resene_problemy'] = resene_problemy | ||||
| #		return context | ||||
| 
 | ||||
| class ArchivTemataView(generic.ListView): | ||||
| 	model = Problem | ||||
|  | @ -601,9 +797,9 @@ def obalkyView(request,resitele): | |||
| 	tex = render(request,'seminar/archiv/obalky.tex', {'resitele': resitele}).content | ||||
| 
 | ||||
| 	tempdir = tempfile.mkdtemp() | ||||
| 	with open(tempdir+"/obalky.tex","wb") as texfile: | ||||
| 	with open(tempdir+"/obalky.tex","w") as texfile: | ||||
| 		texfile.write(tex) | ||||
| 	shutil.copy(os.path.join(settings.STATIC_ROOT, 'seminar/lisak.eps'),tempdir) | ||||
| 	shutil.copy(os.path.join(settings.STATIC_ROOT, 'seminar/lisak.pdf'),tempdir) | ||||
| 	subprocess.call(["pdflatex","obalky.tex"],cwd = tempdir) | ||||
| 
 | ||||
| 	with open(tempdir+"/obalky.pdf","rb") as pdffile: | ||||
|  | @ -612,7 +808,7 @@ def obalkyView(request,resitele): | |||
| 	return response | ||||
| 
 | ||||
| 
 | ||||
| def obalkovaniView(request, rocnik, cislo): | ||||
| def oldObalkovaniView(request, rocnik, cislo): | ||||
| 	rocnik = Rocnik.objects.get(rocnik=rocnik) | ||||
| 	cislo = Cislo.objects.get(rocnik=rocnik, cislo=cislo) | ||||
| 
 | ||||
|  | @ -749,7 +945,7 @@ def StavDatabazeView(request): | |||
| 
 | ||||
| 
 | ||||
| @ensure_csrf_cookie | ||||
| def LoginView(request): | ||||
| def TeXUploadLoginView(request): | ||||
| 	"""Pro přihlášení při nahrávání z texu""" | ||||
| 	q = request.POST | ||||
| 	# nastavení cookie csrftoken | ||||
|  | @ -938,7 +1134,7 @@ def texDownloadView(request, rocnik, cislo): | |||
| 				"body": p.body, | ||||
| 				"zadani": p.text_zadani, | ||||
| 				"reseni": p.text_reseni, | ||||
| 				"cislo_zadani": p.cislo_zadani.cislo, | ||||
| 				"cislo_zadani": p.cislo_zadani.poradi, | ||||
| 			} for p in resene | ||||
| 		], | ||||
| 	} | ||||
|  | @ -947,26 +1143,213 @@ def texDownloadView(request, rocnik, cislo): | |||
| 	cislo.save() | ||||
| 	return JsonResponse(response) | ||||
| 
 | ||||
| ## Formulare | ||||
| class ResitelView(LoginRequiredMixin,generic.DetailView): | ||||
| 	model = Resitel | ||||
| 	template_name = 'seminar/resitel.html' | ||||
| 
 | ||||
| def get_name(request): | ||||
| 	# if this is a POST request we need to process the form data | ||||
| 	def get_object(self, queryset=None): | ||||
| 		print(self.request.user) | ||||
| 		return Resitel.objects.get(osoba__user=self.request.user) | ||||
| 
 | ||||
| ## Formulare | ||||
| class AddSolutionView(LoginRequiredMixin, FormView): | ||||
| 	template_name = 'seminar/org/vloz_reseni.html' | ||||
| 	form_class = f.VlozReseniForm | ||||
| 	success_url = '/' | ||||
| 
 | ||||
| def resetPasswordView(request): | ||||
| 	pass | ||||
| 
 | ||||
| def loginView(request): | ||||
| 	if request.method == 'POST': | ||||
| 		# create a form instance and populate it with data from the request: | ||||
| 		form = NameForm(request.POST) | ||||
| 		# check whether it's valid: | ||||
| 		form = LoginForm(request.POST) | ||||
| 		if form.is_valid(): | ||||
| 			# process the data in form.cleaned_data as required | ||||
| 			# ... | ||||
| 			# redirect to a new URL: | ||||
| 			user = authenticate(request,  | ||||
| 				username=form.cleaned_data['username'], | ||||
| 				password=form.cleaned_data['password']) | ||||
| 			print(form.cleaned_data) | ||||
| 			if user is not None: | ||||
| 				login(request,user) | ||||
| 				return HttpResponseRedirect('/') | ||||
| 			else: | ||||
| 				return render(request,  | ||||
| 					'seminar/login.html',  | ||||
| 					{'form': form, 'login_error': 'Neplatné jméno nebo heslo'}) | ||||
| 
 | ||||
| 	else: | ||||
| 		form = LoginForm() | ||||
| 	return render(request, 'seminar/login.html', {'form': form}) | ||||
| 
 | ||||
| def logoutView(request): | ||||
| 	form = LoginForm() | ||||
| 	if request.user.is_authenticated: | ||||
| 		logout(request) | ||||
| 		return render(request, 'seminar/login.html', {'form': form, 'login_error': 'Byli jste úspěšně odhlášeni'}) | ||||
| 	return render(request, 'seminar/login.html', {'form': form}) | ||||
| 
 | ||||
| 
 | ||||
| def prihlaska_log_gdpr_safe(logger, gdpr_logger, msg, form_data): | ||||
| 	msg = "{}, form_hash:{}".format(msg,hash(form_data)) | ||||
| 	logger.warn(msg) | ||||
| 	gdpr_logger.warn(msg+", form:{}".format(form_data))		 | ||||
| 
 | ||||
| from django.forms.models import model_to_dict | ||||
| def resitelEditView(request): | ||||
|     err_logger = logging.getLogger('seminar.prihlaska.problem') | ||||
|     ## Načtení objektu Osoba a Resitel, patrici k aktuálně přihlášenému uživately | ||||
|     u = request.user | ||||
|     osoba_edit = Osoba.objects.get(user=u) | ||||
|     resitel_edit = osoba_edit.resitel | ||||
|     user_edit = osoba_edit.user | ||||
|     ## Vytvoření slovníku, kterým předvyplním formulář  | ||||
|     prefill_1=model_to_dict(user_edit) | ||||
|     prefill_2=model_to_dict(resitel_edit) | ||||
|     prefill_3=model_to_dict(osoba_edit) | ||||
|     prefill_1.update(prefill_2) | ||||
|     prefill_1.update(prefill_3) | ||||
|     form = ProfileEditForm(initial=prefill_1) | ||||
|     ## Změna údajů a jejich uložení | ||||
|     if request.method == 'POST': | ||||
|         form = ProfileEditForm(request.POST) | ||||
|         if form.is_valid(): | ||||
|             ## Změny v osobě | ||||
|             fcd = form.cleaned_data | ||||
|             osoba_edit.jmeno = fcd['jmeno'] | ||||
|             osoba_edit.prijmeni = fcd['prijmeni'] | ||||
|             osoba_edit.pohlavi_muz = fcd['pohlavi_muz'] | ||||
|             osoba_edit.email = fcd['email'] | ||||
|             osoba_edit.telefon = fcd['telefon'] | ||||
|             osoba_edit.ulice = fcd['ulice'] | ||||
|             osoba_edit.mesto = fcd['mesto'] | ||||
|             osoba_edit.psc = fcd['psc'] | ||||
|             ## Změny v osobě s podmínkami | ||||
|             if fcd.get('spam',False): | ||||
|                 osoba_edit.datum_souhlasu_zasilani = date.today() | ||||
|             if fcd.get('stat','') in ('CZ','SK'): | ||||
|                 osoba_edit.stat = fcd['stat'] | ||||
|             else: | ||||
|                 ## Neznámá země | ||||
|                 msg = "Unknown country {}".format(fcd['stat_text']) | ||||
| 
 | ||||
|             ## Změny v řešiteli | ||||
|             resitel_edit.skola = fcd['skola'] | ||||
|             resitel_edit.rok_maturity = fcd['rok_maturity'] | ||||
|             resitel_edit.zasilat = fcd['zasilat'] | ||||
|             if fcd.get('skola'): | ||||
|                 resitel_edit.skola = fcd['skola'] | ||||
|             else: | ||||
|                 # Unknown school - log it | ||||
|                 msg = "Unknown school {}, {}".format(fcd['skola_nazev'],fcd['skola_adresa']) | ||||
|             resitel_edit.save() | ||||
|             osoba_edit.save() | ||||
|             return HttpResponseRedirect('/thanks/') | ||||
|     else: | ||||
|         ## Stránka před odeslaním formuláře = předvyplněný formulář | ||||
|         return render(request, 'seminar/edit.html', {'form': form}) | ||||
| 
 | ||||
| def prihlaskaView(request): | ||||
| 	generic_logger = logging.getLogger('seminar.prihlaska') | ||||
| 	err_logger = logging.getLogger('seminar.prihlaska.problem') | ||||
| 	form_logger = logging.getLogger('seminar.prihlaska.form') | ||||
| 	if request.method == 'POST': | ||||
| 		form = PrihlaskaForm(request.POST) | ||||
| 		# TODO vyresit, co se bude v jakych situacich zobrazovat | ||||
| 		if form.is_valid(): | ||||
| 			generic_logger.info("Form valid") | ||||
| 			fcd = form.cleaned_data | ||||
| 			form_hash = hash(fcd) | ||||
| 			form_logger.info(fcd,form_hash=form_hash) | ||||
| 			 | ||||
| 			with transaction.atomic(): | ||||
| 				u = User.objects.create_user( | ||||
| 					username=fcd['username'], | ||||
| 					password=fcd['password'], | ||||
| 					email = fcd['email']) | ||||
| 				u.save() | ||||
| 
 | ||||
| 				o = Osoba( | ||||
| 					jmeno = fcd['jmeno'], | ||||
| 					prijmeni = fcd['prijmeni'], | ||||
| 					pohlavi_muz = fcd['pohlavi_muz'], | ||||
| 					email = fcd['email'], | ||||
| 					telefon = fcd.get('telefon',''), | ||||
| 					datum_narozeni = fcd.get('datum_narozeni',None), | ||||
| 					datum_souhlasu_udaje = date.today(), | ||||
| 					datum_registrace = date.today(), | ||||
| 					ulice = fcd.get('ulice',''), | ||||
| 					mesto = fcd.get('mesto',''), | ||||
| 					psc = fcd.get('psc',''), | ||||
| 					poznamka = str(fcd) | ||||
| 					) | ||||
| 				if fcd.get('spam',False): | ||||
| 					o.datum_souhlasu_zasilani = date.today() | ||||
| 				if fcd.get('stat','') in ('CZ','SK'): | ||||
| 					o.stat = fcd['stat'] | ||||
| 				else: | ||||
| 					# Unknown country - log it | ||||
| 					msg = "Unknown country {}".format(fcd['stat_text']) | ||||
| 					err_logger.warn(msg,form_hash=form_hash) | ||||
| 
 | ||||
| 				o.save() | ||||
| 				o.user = u | ||||
| 				o.save() | ||||
| 
 | ||||
| 				r = Resitel( | ||||
| 					rok_maturity = fcd['rok_maturity'], | ||||
| 					zasilat = fcd['zasilat'] | ||||
| 					) | ||||
| 				 | ||||
| 				r.save() | ||||
| 				r.osoba = o | ||||
| 				if fcd.get('skola'): | ||||
| 					r.skola = fcd['skola'] | ||||
| 				else: | ||||
| 					# Unknown school - log it | ||||
| 					msg = "Unknown school {}, {}".format(fcd['skola_nazev'],fcd['skola_adresa']) | ||||
| 					err_logger.warn(msg,form_hash=form_hash) | ||||
| 				r.save() | ||||
| 
 | ||||
| 
 | ||||
| 			return HttpResponseRedirect('/thanks/') | ||||
| 
 | ||||
| 	# if a GET (or any other method) we'll create a blank form | ||||
| 	else: | ||||
| 		form = NameForm() | ||||
| 		form = PrihlaskaForm() | ||||
| 
 | ||||
| 	return render(request, 'seminar/prihlaska.html', {'form': form}) | ||||
| 
 | ||||
| class SkolaAutocomplete(autocomplete.Select2QuerySetView): | ||||
| 	def get_queryset(self): | ||||
|         	# Don't forget to filter out results depending on the visitor ! | ||||
| 		qs = Skola.objects.all() | ||||
| 		if self.q: | ||||
| 			qs = qs.filter( | ||||
| 				Q(nazev__istartswith=self.q)| | ||||
| 				Q(kratky_nazev__istartswith=self.q)| | ||||
| 				Q(ulice__istartswith=self.q)| | ||||
| 				Q(mesto__istartswith=self.q)) | ||||
| 		 | ||||
| 		return qs | ||||
| 
 | ||||
| class LoginRequiredAjaxMixin(object): | ||||
| 	def dispatch(self, request, *args, **kwargs): | ||||
| 		#if request.is_ajax() and not request.user.is_authenticated: # Pokud to otevřu jako stránku, tak se omezení neuplatní, takže to asi nechceme | ||||
| 		if not request.user.is_authenticated: | ||||
| 			return JsonResponse(data={'results': [], 'pagination': {}}, status=401) | ||||
| 		return super(LoginRequiredAjaxMixin, self).dispatch(request, *args, **kwargs) | ||||
| 
 | ||||
| class ResitelAutocomplete(LoginRequiredAjaxMixin,autocomplete.Select2QuerySetView): | ||||
| 	def get_queryset(self): | ||||
| 		qs = Resitel.objects.all() | ||||
| 		if self.q: | ||||
| 			qs = qs.filter( | ||||
| 				Q(osoba__jmeno__startswith=self.q)| | ||||
| 				Q(osoba__prijmeni__startswith=self.q)| | ||||
| 				Q(osoba__prezdivka__startswith=self.q) | ||||
| 				) | ||||
| 		return qs | ||||
| 
 | ||||
| 
 | ||||
| # Ceka na autocomplete v3 | ||||
| # class OrganizatorAutocomplete(autocomplete.Select2QuerySetView): | ||||
| # 	def get_queryset(self): | ||||
|  | @ -987,3 +1370,46 @@ def get_name(request): | |||
| # 				Q(user__last_name__isstartswith=query)) | ||||
| # | ||||
| # 		return qs | ||||
| 
 | ||||
| # FIXME: Tohle asi vlastně vůbec nepatří do aplikace 'seminar' | ||||
| class LoginView(auth_views.LoginView): | ||||
| 	# Jen vezmeme vestavěný a dáme mu vhodný template a přesměrovací URL | ||||
| 	template_name = 'seminar/login.html' | ||||
| 
 | ||||
| 	# Přesměrovací URL má být v kontextu: | ||||
| 	def get_context_data(self, **kwargs): | ||||
| 		ctx = super().get_context_data(**kwargs) | ||||
| 		ctx['next'] = reverse('titulni_strana') | ||||
| 		return ctx | ||||
| 
 | ||||
| class LogoutView(auth_views.LogoutView): | ||||
| 	# Jen vezmeme vestavěný a dáme mu vhodný template a přesměrovací URL | ||||
| 	template_name = 'seminar/logout.html' | ||||
| 	# Pavel: Vůbec nevím, proč to s _lazy funguje, ale bez toho to bylo rozbité. | ||||
| 	next_page = reverse_lazy('titulni_strana') | ||||
| 
 | ||||
| # "Chci resetovat heslo" | ||||
| class PasswordResetView(auth_views.PasswordResetView): | ||||
| 	#template_name = 'seminar/password_reset.html' | ||||
| 	# TODO: vlastní email_template_name a subject_template_name a html_email_template_name | ||||
| 	success_url = reverse_lazy('reset_password_done') | ||||
| 	from_email = 'login@mam.mff.cuni.cz' | ||||
| 
 | ||||
| # "Poslali jsme e-mail (pokud bylo kam))" | ||||
| class PasswordResetDoneView(auth_views.PasswordResetDoneView): | ||||
| 	#template_name = 'seminar/password_reset_done.html' | ||||
| 	pass | ||||
| 
 | ||||
| # "Vymysli si heslo" | ||||
| class PasswordResetConfirmView(auth_views.PasswordResetConfirmView): | ||||
| 	#template_name = 'seminar/password_confirm_done.html' | ||||
| 	success_url = reverse_lazy('reset_password_complete') | ||||
| 
 | ||||
| # "Heslo se asi změnilo." | ||||
| class PasswordResetCompleteView(auth_views.PasswordResetCompleteView): | ||||
| 	#template_name = 'seminar/password_complete_done.html' | ||||
| 	pass | ||||
| 
 | ||||
| class PasswordChangeView(auth_views.PasswordChangeView): | ||||
| 	#template_name = 'seminar/password_change.html' | ||||
| 	success_url = reverse_lazy('titulni_strana') | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Kateřina Čížková
						Kateřina Čížková